// Filename: pencrypt.cxx
// Created by:  drose (01Sep04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "filename.h"
#include "encryptStream.h"
#include "notify.h"

#ifndef HAVE_GETOPT
  #include "gnu_getopt.h"
#else
  #ifdef HAVE_GETOPT_H
    #include <getopt.h>
  #endif
#endif

void 
usage() {
  cerr
    << "\n"
    << "Usage: pencrypt [opts] <file> [<dest_file>]\n\n"
    
    << "This program will apply an encryption algorithm to a file, creating an\n"
    << "encrypted version of the file which can only be recovered using pdecrypt and\n"
    << "the same password that was supplied to pencrypt.  If the dest_file name is\n"
    << "not specified, a default output name is generated by appending .pe to the\n"
    << "input file name.\n\n"

    << "Options:\n\n"

    << "  -p \"password\"\n"
    << "      Specifies the password to use for encryption.  There are no\n"
    << "      restrictions on the password length or contents, but longer passwords\n"
    << "      are more secure.  If this is not specified, the user is prompted from\n"
    << "      standard input.\n\n"

    << "  -a \"algorithm\"\n"
    << "      Specifies the particular encryption algorithm to use.  The complete\n"
    << "      set of available algorithms is defined by the current version of\n"
    << "      OpenSSL.  The default algorithm is taken from the encryption-\n"
    << "      algorithm config variable.\n\n"

    << "  -k key_length\n"
    << "      Specifies the key length, in bits, for the selected encryption\n"
    << "      algorithm.  This only makes sense for those algorithms that support\n"
    << "      a variable key length.  The default value is taken from the\n"
    << "      encryption-key-length config variable.\n\n"

    << "  -i iteration_count\n"
    << "      Specifies the number of times the password is hashed to generate\n"
    << "      a key.  The only purpose of this is to make it computationally\n"
    << "      more expensive for an attacker to search the key space exhaustively.\n"
    << "      This should be a multiple of 1,000 and should not exceed about 65\n"
    << "      million; the value 0 indicates just one application of the hashing\n"
    << "      algorithm.  The default value is taken from the encryption-iteration-\n"
    << "      count config variable.\n\n";
}

int
main(int argc, char *argv[]) {
  extern char *optarg;
  extern int optind;
  const char *optstr = "p:a:k:i:h";

  string password;
  bool got_password = false;
  string algorithm;
  bool got_algorithm = false;
  int key_length = 0;
  bool got_key_length = false;
  int iteration_count = 0;
  bool got_iteration_count = false;

  int flag = getopt(argc, argv, optstr);

  while (flag != EOF) {
    switch (flag) {
    case 'p':
      password = optarg;
      got_password = true;
      break;

    case 'a':
      algorithm = optarg;
      got_algorithm = true;
      break;

    case 'k':
      key_length = atoi(optarg);
      got_key_length = true;
      break;

    case 'i':
      iteration_count = atoi(optarg);
      got_iteration_count = true;
      break;

    case 'h':
    case '?':
    default:
      usage();
      return 1;
    }
    flag = getopt(argc, argv, optstr);
  }

  argc -= (optind-1);
  argv += (optind-1);

  if (argc < 2) {
    usage();
    return 1;
  }

  bool implicit_dest_file;
  Filename source_file = Filename::from_os_specific(argv[1]);
  Filename dest_file;
  if (argc < 3) {
    dest_file = source_file.get_fullpath() + ".pe";
    implicit_dest_file = true;
  } else {
    dest_file = Filename::from_os_specific(argv[2]);
    implicit_dest_file = false;
  }

  // Open source file
  ifstream read_stream;
  source_file.set_binary();
  if (!source_file.open_read(read_stream)) {
    cerr << "failed to open: " << source_file << endl;
    return 1;
  }

  // Open destination file
  ofstream write_stream;
  dest_file.set_binary();
  if (!dest_file.open_write(write_stream, true)) {
    cerr << "failed to open: " << dest_file << endl;
    return 1;
  }

  // Prompt for password.
  if (!got_password) {
    cerr << "Enter password: ";
    getline(cin, password);
  }
    
  bool fail = false;
  {
    OEncryptStream encrypt;
    if (got_algorithm) {
      encrypt.set_algorithm(algorithm);
    }
    if (got_key_length) {
      encrypt.set_key_length(key_length);
    }
    if (got_iteration_count) {
      encrypt.set_iteration_count(iteration_count);
    }
    encrypt.open(&write_stream, false, password);
    
    int ch = read_stream.get();
    while (!read_stream.eof() && !read_stream.fail()) {
      encrypt.put(ch);
      ch = read_stream.get();
    }

    fail = encrypt.fail() && !encrypt.eof();
  }

  read_stream.close();
  write_stream.close();

  if (fail) {
    dest_file.unlink();

  } else {
    if (implicit_dest_file) {
      source_file.unlink();
    }
  }

  return 0;
}
