/* testpa.c - __Test__ privacy application which uses OpenCDK
 *        Copyright (C) 2002, 2003 Timo Schulz
 *
 * This file is part of OpenCDK.
 *
 * OpenCDK is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * OpenCDK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <popt.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

#include "../src/opencdk.h"

/* This file needs to be migrated to the latest opencdk 0.6 API.
   it can be compiled but does not expect all function work. */

enum
{
  cSignEncr = 'b',
  cEnarmor = 'a',
  cDearmor = 'u',
  cHelp = 'h',
  cVersion = 'V',
  cSymmetric = 'c',
  cKeyList = 'k',
  cVerify = 'y',
  cGenKey = 'w',
  cSigList = 'l',
  cEncr = 'e',
  cEncrFiles = '@',
  cDecr = 'd',
  cDecrFiles = '$',
  cExport = 'x',
  cImport = 'i',
  cShowpref = 'q',
  oCompress = 'z',
  oRecipient = 'r',
  oVerbose = 'v',
  oOutput = 'o',
  oCipherAlg = 'n',
  oDigestAlgo = 'm',
  oFetchKey = 'f',
  cSign = 's',
  cSignClear = 't',
  oArmor = 'p',
  oCompat = 'g',
  oNoCompress = '0',
  oDetachSign = 'j',
  oBlockMode = '+'
};


static struct
{
  const char * name;
  int algid;
} algo_table[] = {
  {    "3DES", CDK_CIPHER_3DES},
  {   "CAST5", CDK_CIPHER_CAST5},
  {"BLOWFISH", CDK_CIPHER_BLOWFISH},
  { "TWOFISH", CDK_CIPHER_TWOFISH},
  {     "AES", CDK_CIPHER_AES},
  {  "AES192", CDK_CIPHER_AES192},
  {  "AES256", CDK_CIPHER_AES256},
  {   "dummy", -1},
  {      "MD5", CDK_MD_MD5},
  {     "SHA1", CDK_MD_SHA1},
  {"RIPEMD160", CDK_MD_RMD160},
  {   "SHA256", CDK_MD_SHA256},
  {NULL},
};

char *file = "(none)";
char *user = "(none)";
char *outname = "(none)";
char *cipher_algo = "(none)";
char *digest_algo = "(none)";
char *ks_keyid = "(none)";
char *compress_level = "(none)";
static int cipher_algid = 0;
static int digest_algid = 0;
static int use_outname = 0;
static int verbose = 0;

struct poptOption arguments[] =
{
  {"block-mode", oBlockMode, POPT_ARG_NONE, 0, oBlockMode,
   "force partial block mode", 0},
  {"show-prefs", cShowpref, POPT_ARG_STRING, &user, cShowpref,
   "show the preferences of a key", 0},
  {"detach-sign", oDetachSign, POPT_ARG_NONE, 0, oDetachSign,
   "create a detached signature", 0},
  {"gen-key", cGenKey, POPT_ARG_NONE, 0, cGenKey, "Generate a key", 0},
  {"no-compression", oNoCompress, POPT_ARG_NONE, 0, oNoCompress,
   "Turns off compression", 0},
  {"compat", oCompat, POPT_ARG_NONE, 0, oCompat, "PGP compat mode", 0},
  {"verify", cVerify, POPT_ARG_STRING, &file, cVerify,
   "Verify a signature", 0},
  {"sign", cSign, POPT_ARG_STRING, &file, cSign, "Sign a file", 0},
  {"sign-encrypt", cSignEncr, POPT_ARG_STRING, &file, cSignEncr,
   "Sign and encrypt a file", 0},
  {"clearsign", cSignClear, POPT_ARG_STRING, &file, cSignClear,
   "Sign a text file", 0},
  {"fetch-key", oFetchKey, POPT_ARG_STRING, &ks_keyid, oFetchKey,
   "Fetch key from keyserver", 0},
  {"cipher-algo", oCipherAlg, POPT_ARG_STRING, &cipher_algo, oCipherAlg,
   "Cipher algorithm", 0},
  {"digest-algo", oDigestAlgo, POPT_ARG_STRING, &digest_algo, oDigestAlgo,
   "Digest algorithm", 0},
  {"list-sigs", cSigList, POPT_ARG_STRING, &user, cSigList,
   "List signatures", 0},
  {"output", oOutput, POPT_ARG_STRING, &outname, oOutput,
   "Output filename", 0},
  {"export", cExport, POPT_ARG_STRING, &user, cExport,
   "Export keys", 0},
  {"import", cImport, POPT_ARG_STRING, &file, cImport, "Import keys", 0},
  {"list-keys", cKeyList, POPT_ARG_STRING, &user, cKeyList,
   "List keys", 0},
  {"help", cHelp, POPT_ARG_NONE, 0, cHelp, "Show help message", 0},
  {"version", cVersion, POPT_ARG_NONE, 0, cVersion,
   "Show program version", 0},
  {"enarmor", cEnarmor, POPT_ARG_STRING, &file, cEnarmor,
   "Protect a file with ASCII-Armor", 0},  
  {"dearmor", cDearmor, POPT_ARG_STRING, &file, cDearmor,
   "Remove ASCII-Armor from a file", 0},
  {"compress", oCompress, POPT_ARG_STRING, &compress_level, oCompress,
   "Use compression", 0},
  {"symmetric", cSymmetric, POPT_ARG_STRING, &file, cSymmetric,
   "Symmetrically encrypt a file", 0},
  {"recipient", oRecipient, POPT_ARG_STRING, &user, oRecipient,
   "Use this recipient", 0},
  {"encrypt", cEncr, POPT_ARG_STRING, &file, cEncr, "Encrypt a file", 0},
  {"encrypt-files", cEncrFiles, POPT_ARG_STRING, &file, cEncrFiles,
   "Encrypt |files|", 0},
  {"decrypt", cDecr, POPT_ARG_STRING, &file, cDecr, "Decrypt a file", 0},
  {"decrypt-files", cDecrFiles, POPT_ARG_STRING, &file, cDecrFiles,
   "Decrypt |files|", 0},
  {"verbose", oVerbose, POPT_ARG_NONE, 0, oVerbose, "Verbosity", 0},
  {"armor", oArmor, POPT_ARG_NONE, 0, oArmor, "Armor output", 0},
  {NULL}
};

static const char * get_sig_algo (int pubkey_algo);
     

static void
out_of_core (void)
{
  fputs ("\n** out of core **\n", stderr);
  exit (1);
}



static char *
do_add_ext (const char * filename, const char * ext)
{
  char * output = NULL;

  if (!filename)
    return NULL;
  output = cdk_calloc (1, strlen (filename) + 5);
  if (!output)
    out_of_core ();
  strcpy (output, filename);
  strcat (output, ext ? ext : ".gpg");
  return output;
}


static char *
do_remove_ext (const char * filename)
{
  char * output = NULL;

  if (!filename)
    return NULL;
  if (!strstr (filename, ".gpg") && !strstr (filename, ".pgp")
      && !strstr (filename, ".sig") && !strstr (filename, ".asc"))
    {
      output = cdk_calloc (1, strlen (filename) + 5);
      if (!output)
        out_of_core ();
      strcpy (output, filename);
      strcat (output, ".dec");
      return output;
    }
  output = cdk_strdup (filename);
  if (!output)
    out_of_core ();
  output[strlen (output) - 4] = '\0';
  return output;
}


static char
key_get_letter (int pkalgo)
{
  switch (pkalgo)
    {
    case CDK_PK_RSA:   return 'R';
    case CDK_PK_RSA_E: return 'r';
    case CDK_PK_RSA_S: return 'r';
    case CDK_PK_ELG_E: return 'g';
    case CDK_PK_DSA:   return 'D';
    default:            return '?';
    }
  return '?';
}


static const char *
strdate_from_long (long timestamp)
{
  static char kdate[32];
  struct tm * tm;

  tm = localtime (&timestamp);
  sprintf (kdate, "%04d-%02d-%02d",
           tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
  return kdate;
}


static char
sig_get_letter (cdk_pkt_signature_t sig)
{
  if (sig->flags.missing_key)
    return '%';
  if (sig->flags.checked)
    return sig->flags.valid == 1? '!' : '-';
  return '?';
}


static char
sig_get_status (cdk_pkt_signature_t sig)
{
  if (!sig->flags.exportable)
    return 'L';
  if (sig->sig_class == 0x13)
    return '3';
  else if (sig->sig_class == 0x12)
    return '2';
  else if (sig->sig_class == 0x11)
    return '1';
  return ' ';
}


static int
algo_get_id (const char *algo, int what)
{
  int i = 0;

  for (i = 0; algo_table[i].name; i++)
    {
      if (!strcmp (algo, algo_table[i].name))
	return algo_table[i].algid;
    }
  i = what? CDK_CIPHER_CAST5: CDK_MD_SHA1;
  return i;
}


static char *
get_keyvalid_char (cdk_pkt_pubkey_t pk)
{
  static char flags[2];
  memset (flags, 0, 2);
  /*printf ("[invalid=%d]\n", pk->is_invalid);*/
  if (pk->has_expired)
    strcat (flags, "e");
  else
    strcat (flags, " ");
  if (pk->is_revoked)
    strcat (flags, "r");
  else
    strcat (flags, " ");
  if (pk->is_invalid)
    strcat (flags, "?");
  else if (!pk->has_expired && !pk->is_revoked)
    strcat (flags, "!");
  else
    strcat (flags, " ");
  return flags;
}

static void
do_show_key (cdk_kbnode_t pk, cdk_keydb_hd_t hd, int with_sigs)
{
  cdk_kbnode_t p, ctx = NULL;
  cdk_packet_t pkt;
  
  while ((p = cdk_kbnode_walk (pk, &ctx, 0)))
    {
      pkt = cdk_kbnode_get_packet (p);
      if (pkt->pkttype == CDK_PKT_PUBLIC_KEY)
	{
	  cdk_pkt_pubkey_t key = pkt->pkt.public_key;
	  fprintf (stdout, "pub[%s]  %04d%c/%08lX %s",
                   get_keyvalid_char (key),
		   cdk_pk_get_nbits (key),
		   key_get_letter (key->pubkey_algo),
		   (unsigned long)cdk_pk_get_keyid (key, NULL),
		   strdate_from_long (key->timestamp));
          if (!key->expiredate)
            fputc ('\n', stdout);
          else
            fprintf (stdout, "  [expires: %s]\n",
                     strdate_from_long (key->expiredate));
          if (key->revkeys)
            {
              cdk_desig_revoker_t ctx = NULL;
              const unsigned char *p;
              int algid, class, i;
              char hexbuf[8];

              while ((p = cdk_key_desig_revoker_walk
                      (key->revkeys, &ctx, &class, &algid)))
                {
                  printf ("revkey    %s class 0x%X fingerprint ",
                          get_sig_algo (algid), class);
                  for (i = 0; i < 20; i++)
                    {
                      sprintf (hexbuf, "%X", p[i]);
                      fputs (hexbuf, stdout);
                    }
                  fputc ('\n', stdout);
                }
            }
	}
      else if (pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY)
	{
	  cdk_pkt_pubkey_t key = pkt->pkt.public_key;
	  fprintf (stdout, "sub[%s]  %04d%c/%08lX %s",
                   get_keyvalid_char (key),
		   cdk_pk_get_nbits (key),
		   key_get_letter (key->pubkey_algo),
		   (unsigned long)cdk_pk_get_keyid (key, NULL),
		   strdate_from_long (key->timestamp));
          if (!key->expiredate)
            fputc ('\n', stdout);
          else
            fprintf (stdout, "  [expires: %s]\n",
                     strdate_from_long (key->expiredate));
	}
      else if (pkt->pkttype == CDK_PKT_USER_ID)
	{
	  cdk_pkt_userid_t id = pkt->pkt.user_id;
          char * uid = cdk_utf8_decode (id->name, id->len, 0);
          fprintf (stdout, "uid %s %s\n",
                   id->is_revoked ? "[revoked]" : "         ", uid);
          cdk_free (uid);
	}
      else if (with_sigs && pkt->pkttype == CDK_PKT_SIGNATURE)
	{
	  cdk_pkt_signature_t sig = pkt->pkt.signature;
          cdk_packet_t pkt;
          cdk_kbnode_t pubk;

	  fprintf (stdout, " sig%c %c %08lX %s",
		   sig_get_letter (sig),
		   sig_get_status (sig),
		   (unsigned long)cdk_sig_get_keyid (sig, NULL),
		   strdate_from_long (sig->timestamp));
          if (!cdk_keydb_get_bykeyid (hd, sig->keyid, &pubk))
            {
              pkt = cdk_kbnode_find_packet (pubk, CDK_PKT_USER_ID);
              printf (" %s\n", pkt?
                      pkt->pkt.user_id->name : "[user-ID not found]");
              cdk_kbnode_release (pubk);
            }
          else
            printf (" [user-ID not found]\n");
	}
    }
}


static int
check_for_wildcards (cdk_strlist_t list)
{
  cdk_strlist_t n;
  const char *s;
  
  n = cdk_strlist_next (list, &s);
  if (*s == '*')
    return 1;
  return 0;
}


int
do_list_keys (cdk_keydb_hd_t hd, cdk_strlist_t keys)
{
  unsigned char fpr[32];
  cdk_kbnode_t pk = NULL;
  cdk_listkey_t list;
  int rc = 0, i;

  if (check_for_wildcards (keys))
    keys = NULL;
  if (keys)
    {
      rc = cdk_listkey_start (&list, hd, NULL, keys);
      if (rc)
        {
          fprintf (stderr, "listkey start: %s\n", cdk_strerror (rc));
          return rc;
        }
      while (!(rc = cdk_listkey_next (list, &pk)))
        {
	  cdk_packet_t pkt;
	  
          do_show_key (pk, hd, 0);
	  pkt = cdk_kbnode_get_packet (pk);
	  cdk_pk_get_fingerprint (pkt->pkt.public_key, fpr);
	  printf ("Fingerprint ");
	  printf ("v%d ", pkt->pkt.public_key->version);
	  for (i = 0; i < 20/2; i++)
	    printf ("%02X%02X ", fpr[2*i], fpr[2*i+1]);
	  printf ("\n");

	  cdk_kbnode_release (pk);
	  pk = NULL;
	}
      cdk_listkey_close (list);
      return 0;
    }
  
  /* Show all keys */
  rc = cdk_listkey_start (&list, hd, "*", NULL);
  if (rc)
    {
      fprintf (stderr, "listkey start: %s\n", cdk_strerror (rc));
      return rc;
    }
  
  while (!(rc = cdk_listkey_next (list, &pk)))
    {
      do_show_key (pk, hd, 0);
      fputc ('\n', stdout);
      cdk_kbnode_release (pk);
      pk = NULL;
    }
  cdk_listkey_close (list);
  return 0;
}


int
do_export_keys (cdk_ctx_t ctx, cdk_keydb_hd_t hd, cdk_strlist_t keys, int secret)
{
  cdk_stream_t outp = NULL;
  int rc = 0;

  if (!use_outname)
    {
      fprintf (stdout, "please use --output for a file.\n");
      return CDK_General_Error;
    }

  rc = cdk_stream_create (outname, &outp);
  if (rc)
    {
      fprintf (stdout, "can't open `%s': %s\n", outname, cdk_strerror (rc));
      return rc;
    }
  if (keys)
    {
      if (cdk_handle_control (ctx, CDK_CTLF_GET, CDK_CTL_ARMOR))
        cdk_stream_set_armor_flag (outp, CDK_ARMOR_PUBKEY);
      rc = cdk_keydb_export (hd, outp, keys);
      if (rc)
	fprintf (stdout, "key export `%s'\n", cdk_strerror (rc));
    }
  cdk_stream_close (outp);
  return rc;
}


int
do_import_keys (cdk_keydb_hd_t pubdb, cdk_keydb_hd_t secdb,
                const char * keyfile)
{
  cdk_keydb_hd_t db;
  cdk_kbnode_t node = NULL, n, ctx = NULL;
  cdk_stream_t s;
  int rc;

  if (!pubdb)
    {
      fprintf (stderr, "no public keyring available.\n");
      return -1;
    }
  rc = cdk_stream_open (keyfile, &s);
  if (rc)
    {
      fprintf (stderr, "open `%s' failed: %s\n", keyfile, cdk_strerror (rc));
      return -1;
    }
  if (cdk_armor_filter_use (s))
    {
      if (verbose)
        fprintf (stderr, "push armor filter\n");
      cdk_stream_set_armor_flag (s, 0);
    }
  rc = cdk_keydb_get_keyblock (s, &node);
  if (!node)
    {
      cdk_stream_close (s);
      fprintf (stderr, "get keyblock failed: %s\n", cdk_strerror (rc));
      return -1;
    }
  db = cdk_kbnode_find (node, CDK_PKT_SECRET_KEY)? secdb : pubdb;
  rc = cdk_keydb_import (db, node);
  if (rc)
    fprintf (stderr, "import failed: %s\n", cdk_strerror (rc));
  else
    {
      while ((n = cdk_kbnode_walk (node, &ctx, 0)))
        {
          cdk_packet_t pkt = cdk_kbnode_get_packet (n);
          if (pkt->pkttype == CDK_PKT_USER_ID)
	    printf ("`%s' successfully imported.\n",
		    pkt->pkt.user_id->name);
        }
    }
  cdk_stream_close (s);
  cdk_kbnode_release (node);
  return 0;
}
  

int
do_list_sigs (cdk_keydb_hd_t hd, cdk_strlist_t keys)
{
  cdk_kbnode_t pk = NULL;
  cdk_strlist_t n;
  const char * s;
  int kstat = 0;
  int rc = 0;

  n = keys;
  if (keys)
    {
      while ((n = cdk_strlist_next (n, &s)))
	{
	  rc = cdk_keydb_get_bypattern (hd, s, &pk);
	  if (rc)
	    fprintf (stdout, "get key `%s': %s\n", s, cdk_strerror (rc));
	  rc = cdk_pk_check_sigs (pk, hd, &kstat);
	  if (rc)
	    fprintf (stdout, "check sigs `%s': %s\n", s, cdk_strerror (rc));
	  do_show_key (pk, hd, 1);
	}
    }

  return 0;
}


char *
get_passphrase (void * hd, const char *prompt)
{
  char * p = NULL;
  int maxlen = 255;

  fprintf (stdout, "%s", prompt);
  p = cdk_calloc (1, 256);
  if (!p)
    out_of_core ();

  fgets (p, maxlen, stdin);
  p[strlen (p) - 1] = '\0';
  return p;

  return NULL;
}


int
ks_fetch_key (const char * kid)
{
  unsigned long keyid;
  unsigned char kidbuf[4];
  cdk_packet_t uid;
  cdk_kbnode_t pk = NULL;
  int rc;
  
  if (!strncmp (kid, "0x", 2))
    kid += 2;
  if (strlen (kid) != 8 && strlen (kid) != 16)
    {
      printf ("wrong keyID usage\n");
      return -1;
    }
  if (strlen (kid) == 16)
    keyid = strtoul (kid + 8, NULL, 16);
  else
    keyid = strtoul (kid, NULL, 16);

  kidbuf[0] = keyid >> 24;
  kidbuf[1] = keyid >> 16;
  kidbuf[2] = keyid >>  8;
  kidbuf[3] = keyid;
  cdk_set_log_level (CDK_LOG_DEBUG);
  printf ("fetch %08lX from the keyserver...", keyid);
  rc = cdk_keyserver_recv_key ("http://horowitz.surfnet.nl", 11371,
                               kidbuf, CDK_DBSEARCH_SHORT_KEYID, &pk);
  if (rc)
      printf ("%s\n", cdk_strerror (rc));
  else
      printf ("OK\n");
  uid = cdk_kbnode_find_packet (pk, CDK_PKT_USER_ID);
  if (uid)
    printf ("%s\n", uid->pkt.user_id->name);

  cdk_kbnode_release (pk);
  return 0;
}


static const char *
get_sig_status (int id)
{
  const char * s = "unknown error";

  switch (id)
    {
    case CDK_SIGSTAT_NONE : s = "no signature"; break;
    case CDK_SIGSTAT_GOOD : s = "good signature"; break;
    case CDK_SIGSTAT_BAD  : s = "bad signature"; break;
    case CDK_SIGSTAT_NOKEY: s = "no key for checking the signature"; break; 
    }
  return s;
}

static const char *
get_sig_algo (int pubkey_algo)
{
  switch (pubkey_algo)
    {
    case CDK_PK_RSA_E:
    case CDK_PK_RSA_S:
    case CDK_PK_RSA  : return "RSA";
    case CDK_PK_ELG_E: return "ELG";
    case CDK_PK_DSA  : return "DSA";
    default           : return "???"; 
    }
  return NULL; 
}

static const char *
get_sig_time (long timestamp)
{
  static char buf[128];
  struct tm * tmbuf;

  tmbuf = localtime (&timestamp);
  sprintf (buf, "%04d-%02d-%02d %02d:%02d:%02d",
           tmbuf->tm_year+1900, tmbuf->tm_mon+1, tmbuf->tm_mday,
           tmbuf->tm_hour, tmbuf->tm_min, tmbuf->tm_sec);
  return buf;  
}


void
print_mpibuf (const unsigned char * buf, int pubkey_algo)
{
  int i, j, n;

  printf ("signature data:\n");
  for (i = 0, j = 0; i < cdk_pk_get_nsig (pubkey_algo); i++)
    {
      n  = buf[j++] << 8;
      n |= buf[j++];
      n = (n + 7) / 8;
      while (n--)
        printf ("%02x", buf[j++]);
    }
  printf ("\n");
}
      

void
print_verify_stats (cdk_ctx_t hd, int extflag)
{
  cdk_verify_result_t res;
  
  res = cdk_handle_verify_get_result (hd);  
  printf ("%s keyid 0x%08lX algo %s created %s\n\t\"%s\"\n",
	  get_sig_status (res->sig_status),
	  (unsigned long)res->keyid[1], get_sig_algo (res->pubkey_algo), 
	  get_sig_time (res->created),
	  res->user_id? res->user_id : "user ID not found");
}


int
genkey (void)
{
  int c, algo = 0, rc =0;
  unsigned char mdbuf[3] = {CDK_MD_MD5, CDK_MD_RMD160, CDK_MD_SHA1};
  cdk_keygen_ctx_t hd;
  
  printf ("(1) DSA (sign only)\n");
  printf ("(2) DSA and ElGamal\n");
  printf ("(3) DSA and RSA\n");
  printf ("(4) RSA and ElGamal\n");
  printf ("your choice: ");
  c = fgetc (stdin);
  switch (c)
    {
    case '1':
    case '2': 
    case '3': algo = CDK_PK_DSA; break;
    case '4': algo = CDK_PK_RSA; break;
    default: printf ("invalid public key algorithm\n"); break;
    }
  if (!algo)
    return CDK_Inv_Algo;
  cdk_salloc (1, 0);
  cdk_keygen_new (&hd);
  cdk_keygen_set_algo_info (hd, 0, CDK_KEY_USG_SIGN, algo, 768);
  if (c > '1' && c < '5')
    {
      switch (c)
        {
        case '4':
        case '2': cdk_keygen_set_algo_info (hd, 1, CDK_KEY_USG_ENCR, CDK_PK_ELG_E, 768); break;
        case '3': cdk_keygen_set_algo_info (hd, 1, CDK_KEY_USG_ENCR, CDK_PK_RSA_E, 768); break;
        }
      cdk_keygen_set_expire_date (hd, 1, 8640000);
    }
  cdk_keygen_set_name (hd, "Otto Bismarck");
  cdk_keygen_set_keyserver_flags (hd, 1, "http://horowitz.surfnet.nl");
  cdk_keygen_set_expire_date (hd, 0, 864000);
  cdk_keygen_set_prefs (hd, CDK_PREFTYPE_HASH, mdbuf, 3);
  /*cdk_keygen_set_passphrase (hd, "abc");*/
  rc = cdk_keygen_start (hd);
  if (!rc)
    rc = cdk_keygen_save (hd, "pub.test", "sec.test");
  cdk_keygen_free (hd);
  return rc;
}



void
file_error (const char * fil, const char * fnc, int rc)
{
  fprintf (stderr, "minpg: %s %s: `%s'\n", fil, fnc, cdk_strerror (rc));
}


int
main (int argc, char ** argv)
{
  cdk_ctx_t hd;
  cdk_strlist_t remusr = NULL, klist = NULL;
  cdk_keydb_hd_t sec_db, pub_db;
  poptContext opt_ctx;
  char * output = NULL, * s;
  char u[128];
  int c = 0, i = 0, sigmode = 0, zip_level = 6;
  int blockmode = 0;
  int rc = 0, nfiles = 0;

  s = "pub.gpg";
  rc = cdk_keydb_new (&pub_db, CDK_DBTYPE_PK_KEYRING, s, strlen (s));
  if (rc)
    {
      fprintf (stderr, "keydb new: `%s'\n", cdk_strerror (rc));
      return 0;
    }
  s = "sec.gpg";
  rc = cdk_keydb_new (&sec_db, CDK_DBTYPE_SK_KEYRING, s, strlen (s));
  if (rc)
    {
      fprintf (stderr, "keydb new: `%s'\n", cdk_strerror (rc));
      return 0;
    }
  

  cdk_handle_new (&hd);
  cdk_handle_set_passphrase_cb (hd, get_passphrase, NULL);
  cdk_handle_control (hd, CDK_CTLF_SET, CDK_CTL_OVERWRITE, 1);
  cdk_handle_control (hd, CDK_CTLF_SET, CDK_CTL_COMPRESS, 2, 5);
  opt_ctx = poptGetContext ("pred", argc, (const char**)argv, arguments, 0);
  while ((c = poptGetNextOpt (opt_ctx)) >= 0)
    {
      switch (c)
	{
        case '+':
          if (verbose)
            printf ("enable block-mode\n");
          blockmode++;
          break;
          
	case 'v':
	  verbose++;
	  break;

        case oNoCompress:
          if (verbose)
            fprintf (stdout, "disable compression\n");
          cdk_handle_control (hd, CDK_CTLF_SET, CDK_CTL_COMPRESS, 0, 0);
          break;
          
        case 'p':
          if (verbose)
            fprintf (stdout, "protect output with ascii armor\n");
          cdk_handle_set_armor( hd, 1 );
          break;

	case 'o':
	  if (verbose)
	    fprintf (stdout, "output filename `%s'\n", outname);
	  use_outname = 1;
	  break;

        case 'g':
          if (verbose)
            fprintf (stdout, "set PGP compat mode\n");
          cdk_handle_control (hd, CDK_CTLF_SET, CDK_CTL_COMPAT, 1);
          break;

	case 'h':
	  fprintf (stderr, "opencdk (MinPG) %s\n\n", OPENCDK_VERSION);
	  poptPrintHelp (opt_ctx, stdout, 0);
	  return 0;

	case 'V':
	  fprintf (stderr, "Minimal Privacy Guard (minpg) %s\n\n", OPENCDK_VERSION);
	  fprintf (stderr, "Supported algorithms:\n");
	  fprintf (stderr, "Cipher: ");
	  for (i = 0; algo_table[i].name && algo_table[i].algid != -1; i++)
	    fprintf (stderr, "%s ", algo_table[i].name);
          i++;
          fprintf (stderr, "\nDigest: ");
          for (; algo_table[i].name; i++)
            fprintf (stderr, "%s ", algo_table[i].name);
	  fprintf (stderr, "\n");
	  return 0;

        case 'j':
          sigmode = CDK_SIGMODE_DETACHED;
          break;


        case 'm':
          digest_algid = algo_get_id (digest_algo, 0);
          if (verbose)
            fprintf (stdout, "use %s as the message digest (%d)\n",
                     digest_algo, digest_algid);
          break;
          
	case 'n':
	  cipher_algid = algo_get_id (cipher_algo, 1);
	  if (verbose)
	    fprintf (stdout, "use %s for encryption (%d)\n",
		     cipher_algo, cipher_algid);
	  break;

        case 'y':
          if (verbose > 1)
            cdk_set_log_level (CDK_LOG_DEBUG);
          cdk_handle_set_keydb (hd, pub_db);
          rc = cdk_file_verify (hd, file, NULL, NULL);
          if (rc)
            file_error ("verify", file, rc);
          else
            print_verify_stats (hd, 0);
          break;
          
	case 'u':
          if (verbose > 1)
            cdk_set_log_level (CDK_LOG_DEBUG);
	  output = cdk_calloc (1, strlen (file) + 1);
          if (!output)
            out_of_core ();
	  if (strstr (file, ".asc") || strstr (file, ".gpg"))
	    strncpy (output, file, strlen (file) - 4);
	  else
	    strcpy (output, file);
	  if (verbose)
	    fprintf (stdout, "dearmor `%s' -> %s\n", file, output);
	  rc = cdk_file_dearmor (file, output);
	  if (rc)
            file_error ("dearmor file", file, rc);
	  break;

	case 'l':
          if (verbose > 1)
            cdk_set_log_level (CDK_LOG_DEBUG);
	  cdk_strlist_add (&klist, user);
	  do_list_sigs (pub_db, klist);
	  break;

	case 'a':
	  output = do_add_ext (file, ".asc");
	  if (verbose)
	    fprintf (stdout, "armor `%s' -> %s\n", file, output);
	  rc = cdk_file_armor (hd, file, output);
	  if (rc)
            file_error ("armor file", file, rc);
	  break;

	case 'z':
          zip_level = atol (compress_level);
          if (zip_level < 0 || zip_level > 10)
            {
              printf ("invalid compression level `%d', changed to 6.\n",
                      zip_level);
              zip_level = 6;
            }
          cdk_handle_control (hd, CDK_CTLF_SET, CDK_CTL_COMPRESS,
                              CDK_COMPRESS_ZIP, zip_level);
	  break;

	case 'c':
          if (blockmode)
            cdk_handle_set_blockmode (hd, 1);
          if (verbose > 1)
            cdk_set_log_level (CDK_LOG_DEBUG);
	  output = do_add_ext (file, ".gpg");
	  if (verbose)
	    fprintf (stdout, "symencrypt `%s' -> %s\n", file, output);
	  if (cipher_algid)
            cdk_handle_control (hd, CDK_CTLF_SET, CDK_CTL_CIPHER,
                                cipher_algid);
          if (digest_algid)
            {
              cdk_handle_control (hd, CDK_CTLF_SET, CDK_CTL_DIGEST,
                                  digest_algid);
              if (!cipher_algid)
                cipher_algid = CDK_CIPHER_3DES;
              cdk_handle_control (hd, CDK_CTLF_SET, CDK_CTL_S2K,
                                  3, digest_algid, cipher_algid);
            }
          cdk_handle_set_keydb (hd, pub_db);
	  rc = cdk_file_encrypt (hd, NULL, file, output);
	  if (rc)
            file_error ("symmetric file", file, rc);
	  break;

	case 'k':
	  cdk_strlist_add (&klist, user);
	  do_list_keys (pub_db, klist);
	  break;

	case 'x':
	  cdk_strlist_add (&klist, user);
	  do_export_keys (hd, pub_db, klist, 0);
	  break;

        case 'i':
          if (verbose > 1)
            cdk_set_log_level (CDK_LOG_DEBUG);
          do_import_keys (pub_db, sec_db, file);
          break;

        case 'f':
          ks_fetch_key (ks_keyid);
          break;

	case 'r':
	  cdk_strlist_add (&remusr, user);
	  break;

	case 'e':
        case '@':
	  if (!remusr)
	    {
	      fprintf (stdout, "No recipient was chosen.\n\n");
	      fprintf (stdout, "Recipient: ");
	      fgets (u, sizeof u, stdin);
	      cdk_strlist_add (&remusr, user);
	    }
          if (verbose > 1)
            cdk_set_log_level (CDK_LOG_DEBUG);
          if (blockmode)
            cdk_handle_set_blockmode (hd, 1);
	  output = do_add_ext (file, ".gpg");
	  if (verbose)
	    fprintf (stdout, "encrypt `%s' -> %s\n", file, output);
          cdk_handle_set_keydb (hd, pub_db);
	  rc = cdk_file_encrypt (hd, remusr, file, output);
	  if (rc)
            file_error ("encrypt file", file, rc);
          if (c == '@')
            nfiles = 1;
	  break;

	case 'd':
        case '$':
          if (verbose > 1)
            cdk_set_log_level (CDK_LOG_DEBUG);
	  output = do_remove_ext (file);
	  if (verbose)
	    fprintf (stdout, "decrypt `%s' -> %s\n", file, output);
          cdk_handle_set_keydb (hd, sec_db);
          cdk_handle_set_keydb (hd, pub_db);
	  rc = cdk_file_decrypt (hd, file, output);
	  if (rc)
            file_error ("decrypt file", file, rc);
          else
            print_verify_stats (hd, 0);
          if (c == '$')
            nfiles = 2;
	  break;

        case 'w':
          rc = genkey ();
          if (rc)
            printf ("key generation failed `%s'\n", cdk_strerror (rc));
          break;
          
        case 's':
          cdk_handle_control (hd, CDK_CTLF_SET, CDK_CTL_FORCE_DIGEST, 1);
          if (digest_algid)
            cdk_handle_control (hd, CDK_CTLF_SET, CDK_CTL_DIGEST,digest_algid);
          if (verbose)
            fprintf (stdout, "force digest algo\n");
          if (!remusr)
            {
              fprintf (stdout, "No user was chosen.\n");
              exit (0);
            } 
          output = do_add_ext (file, ".sig");
          if (verbose)
            fprintf (stdout, "sign `%s' -> %s\n", file, output);
          cdk_handle_set_keydb (hd, sec_db);
          if (sigmode == CDK_SIGMODE_DETACHED)
            fprintf (stdout, "create a detached signature\n");
          rc = cdk_file_sign (hd, remusr, NULL, file, output, sigmode, 0);
          if (rc)
            file_error ("sign file", file, rc);
          break;

        case 't':
          if (!remusr)
            {
              fprintf (stdout, "No user was chosen.\n");
              exit (0);
            }
          output = do_add_ext (file, ".asc");
          cdk_handle_set_keydb (hd, sec_db);
          rc = cdk_file_sign (hd, remusr, NULL, file, output, CDK_SIGMODE_CLEAR, 0);
          if (rc)
            file_error ("clearsign file", file, rc);
          break;

        case 'b':
          if (!remusr)
            {
              fprintf (stderr, "No recipient was chosen.\n");
              exit (0);
            }
          output = do_add_ext (file, ".gpg");
          cdk_strlist_add (&klist, "opencdk");
          cdk_handle_set_keydb (hd, pub_db);
          cdk_handle_set_keydb (hd, sec_db);
          rc = cdk_file_sign (hd, klist, remusr, file, output, CDK_SIGMODE_NORMAL, 1);
          if (rc)
            file_error ("sign encrypt", file, rc);
          break;
	}
    }

  if (nfiles)
    {
      const char ** files = poptGetArgs (opt_ctx);
      /*cdk_handle_control (hd, CDK_CTLF_SET, CDK_CTL_KEYCACHE_FREE, 1);*/
      while (*files)
        {
          if (nfiles == 1)
            {
              output = do_add_ext (*files, ".gpg");
              if (verbose)
                printf ("encrypt `%s' -> %s\n", *files, output);
              rc = cdk_file_encrypt (hd, remusr, *files, output);
            }
          else if (nfiles == 2)
            {
              output = do_remove_ext (*files);
              if (verbose)
                printf ("decrypt `%s' -> %s\n", *files, output);
              rc = cdk_file_decrypt (hd, *files, output);
            }
          if (rc)
              printf ("%s file `%s': %s\n", nfiles==1? "encrypt" : "decrypt",
                      *files, cdk_strerror (rc));
          files++;
          cdk_free (output);
          output = NULL;
        }
    }

  poptFreeContext (opt_ctx);
  cdk_keydb_free (pub_db);
  cdk_keydb_free (sec_db);
  cdk_handle_free (hd);
  cdk_strlist_free (remusr);
  cdk_strlist_free (klist);
  cdk_free (output);
  return 0;
}
