new cryptplug function importCertificateFromMem()
[gpgme.git] / gpgmeplug / gpgmeplug.c
index d5d827b..f603008 100644 (file)
@@ -6,6 +6,7 @@
               the common CRYPTPLUG specification.
 
   Copyright (C) 2001 by Klarälvdalens Datakonsult AB
+  Copyright (C) 2002 g10 Code GmbH
 
   GPGMEPLUG is free software; you can redistribute it and/or modify
   it under the terms of GNU General Public License as published by
@@ -62,7 +63,7 @@
 #endif
 
 /* definitions for signing */
-// 1. opaque signatures (only used for S/MIME)
+/* 1. opaque signatures (only used for S/MIME). */
 #ifndef GPGMEPLUG_OPA_SIGN_MAKE_MIME_OBJECT
 #define GPGMEPLUG_OPA_SIGN_INCLUDE_CLEARTEXT false
 #define GPGMEPLUG_OPA_SIGN_MAKE_MIME_OBJECT  false
@@ -81,7 +82,7 @@
 #define GPGMEPLUG_OPA_SIGN_FLAT_SEPARATOR    ""
 #define GPGMEPLUG_OPA_SIGN_FLAT_POSTFIX      ""
 #endif
-// 2. detached signatures (used for S/MIME and for OpenPGP)
+/* 2. detached signatures (used for S/MIME and for OpenPGP) */
 #ifndef GPGMEPLUG_DET_SIGN_MAKE_MIME_OBJECT
 #define GPGMEPLUG_DET_SIGN_INCLUDE_CLEARTEXT true
 #define GPGMEPLUG_DET_SIGN_MAKE_MIME_OBJECT  true
 #define GPGMEPLUG_DET_SIGN_FLAT_SEPARATOR    ""
 #define GPGMEPLUG_DET_SIGN_FLAT_POSTFIX      ""
 #endif
-// 3. common definitions for opaque and detached signing
+/* 3. common definitions for opaque and detached signing */
 #ifndef __GPGMEPLUG_SIGNATURE_CODE_IS_BINARY
 #define __GPGMEPLUG_SIGNATURE_CODE_IS_BINARY false
 #endif
 #include "cryptplug.h"
 
 
+#define days_from_seconds(x) ((x)/86400)
+
+
 typedef struct {
   const char*             bugURL;
   const char*             signatureKeyCertificate;
@@ -192,7 +196,7 @@ typedef struct {
   bool                    certificateInChainExpiryNearWarning;
   int                     certificateInChainExpiryNearWarningInterval;
   bool                    receiverEmailAddressNotInCertificateWarning;
-  const char* libVersion; // a statically allocated string with the GPGME Version used
+  const char* libVersion; /* a statically allocated string with the GPGME Version used */
 } Config;
 
 
@@ -200,6 +204,48 @@ Config config;
 
 #define NEAR_EXPIRY 14
 
+/* Max number of parts in a DN */
+#define MAX_GPGME_IDX 20
+
+/* some macros to replace ctype ones and avoid locale problems */
+#define spacep(p)   (*(p) == ' ' || *(p) == '\t')
+#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
+#define hexdigitp(a) (digitp (a)                     \
+                      || (*(a) >= 'A' && *(a) <= 'F')  \
+                      || (*(a) >= 'a' && *(a) <= 'f'))
+/* the atoi macros assume that the buffer has only valid digits */
+#define atoi_1(p)   (*(p) - '0' )
+#define atoi_2(p)   ((atoi_1(p) * 10) + atoi_1((p)+1))
+#define atoi_4(p)   ((atoi_2(p) * 100) + atoi_2((p)+2))
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+
+static void *
+xmalloc (size_t n)
+{
+  char *p = malloc (n);
+  if (!p)
+    { 
+      fputs ("\nfatal: out of core\n", stderr);
+      exit (4);
+    }
+  return p;
+}
+
+/* Please: Don't call an allocation function xfoo when it may return NULL. */
+/* Wrong: #define xstrdup( x ) (x)?strdup(x):0 */
+/* Right: */
+static char *
+xstrdup (const char *string)
+{
+  char *p = xmalloc (strlen (string)+1);
+  strcpy (p, string);
+  return p;
+}
+
+
+
 bool initialize()
 {
   config.bugURL                               = malloc( strlen( BUG_URL ) + 1 );
@@ -278,8 +324,8 @@ bool hasFeature( Feature flag )
   case Feature_WarnEncryptEmailNotInCertificate: return true;
   case Feature_StoreMessagesEncrypted:    return true;
   case Feature_CheckCertificatePath:      return true;
-  case Feature_CertificateDirectoryService: return true;
-  case Feature_CRLDirectoryService:       return true;
+  case Feature_CertificateDirectoryService: return false;
+  case Feature_CRLDirectoryService:       return false;
   /* undefined or not yet implemented: */
   case Feature_undef:                     return false;
   default:                                      return false;
@@ -496,8 +542,6 @@ bool signatureCertificateExpiryNearWarning( void )
 
 int signatureCertificateDaysLeftToExpiry( const char* certificate )
 {
-  // pending (khz): make this work!
-  /*
   GpgmeCtx ctx;
   GpgmeError err;
   GpgmeKey rKey;
@@ -514,17 +558,17 @@ int signatureCertificateDaysLeftToExpiry( const char* certificate )
       time_t expire_time = gpgme_key_get_ulong_attr(
                              rKey,GPGME_ATTR_EXPIRE, NULL, 0 );
       time_t cur_time = time (NULL);
-      daysLeft = expire_time - cur_time;
-      // convert seconds into days
-      daysLeft /= 43200;
+      daysLeft = days_from_seconds(expire_time - cur_time);
       gpgme_key_release( rKey );
     }
   }
   gpgme_release( ctx );
+    
+  /* 
+  fprintf( stderr, "gpgmeplug signatureCertificateDaysLeftToExpiry returned %d\n", daysLeft );
+  */
 
   return daysLeft;
-  */
-  return 10;
 }
 
 
@@ -555,6 +599,38 @@ int caCertificateDaysLeftToExpiry( const char* certificate )
        CA certificate for the certificate specified in the parameter
        certificate expires.
     */
+  /*
+  GpgmeCtx ctx;
+  GpgmeError err;
+  GpgmeKey rKey;
+  time_t daysLeft = 0;
+
+  gpgme_new( &ctx );
+  gpgme_set_protocol( ctx, GPGMEPLUG_PROTOCOL );
+
+  err = gpgme_op_keylist_start( ctx, certificate, 0 );
+  if ( GPGME_No_Error == err ) {
+    err = gpgme_op_keylist_next( ctx, &rKey );
+    gpgme_op_keylist_end( ctx );
+    if ( GPGME_No_Error == err ) {
+      time_t expire_time = gpgme_key_get_ulong_attr(
+                             rKey,
+                             
+??????????????????????? GPGME_ATTR_EXPIRE,  ???????????????????????
+                             
+                             NULL, 0 );
+      time_t cur_time = time (NULL);
+      daysLeft = days_from_seconds(expire_time - cur_time);
+      gpgme_key_release( rKey );
+    }
+  }
+  gpgme_release( ctx );
+    
+   
+  // fprintf( stderr, "gpgmeplug caCertificateDaysLeftToExpiry returned %d\n", daysLeft );
+  return daysLeft;
+  */
+  
   return 10; /* dummy that triggers a warning in the MUA */
 }
 
@@ -585,6 +661,38 @@ int rootCertificateDaysLeftToExpiry( const char* certificate )
        root certificate for the certificate specified in the parameter
        certificate expires.
     */
+  /*
+  GpgmeCtx ctx;
+  GpgmeError err;
+  GpgmeKey rKey;
+  time_t daysLeft = 0;
+
+  gpgme_new( &ctx );
+  gpgme_set_protocol( ctx, GPGMEPLUG_PROTOCOL );
+
+  err = gpgme_op_keylist_start( ctx, certificate, 0 );
+  if ( GPGME_No_Error == err ) {
+    err = gpgme_op_keylist_next( ctx, &rKey );
+    gpgme_op_keylist_end( ctx );
+    if ( GPGME_No_Error == err ) {
+      time_t expire_time = gpgme_key_get_ulong_attr(
+                             rKey,
+                             
+??????????????????????? GPGME_ATTR_EXPIRE,  ???????????????????????
+                             
+                             NULL, 0 );
+      time_t cur_time = time (NULL);
+      daysLeft = days_from_seconds(expire_time - cur_time);
+      gpgme_key_release( rKey );
+    }
+  }
+  gpgme_release( ctx );
+    
+   
+  // fprintf( stderr, "gpgmeplug rootCertificateDaysLeftToExpiry returned %d\n", daysLeft );
+  return daysLeft;
+  */
+  
   return 10; /* dummy that triggers a warning in the MUA */
 }
 
@@ -719,6 +827,36 @@ bool receiverCertificateExpiryNearWarning()
 
 int receiverCertificateDaysLeftToExpiry( const char* certificate )
 {
+  GpgmeCtx ctx;
+  GpgmeError err;
+  GpgmeKey rKey;
+  time_t daysLeft = 0;
+
+  gpgme_new( &ctx );
+  gpgme_set_protocol( ctx, GPGMEPLUG_PROTOCOL );
+
+  err = gpgme_op_keylist_start( ctx, certificate, 0 );
+  if ( GPGME_No_Error == err ) {
+    err = gpgme_op_keylist_next( ctx, &rKey );
+    gpgme_op_keylist_end( ctx );
+    if ( GPGME_No_Error == err ) {
+      time_t expire_time = gpgme_key_get_ulong_attr(
+                             rKey,GPGME_ATTR_EXPIRE, NULL, 0 );
+      time_t cur_time = time (NULL);
+      daysLeft = days_from_seconds(expire_time - cur_time);
+      gpgme_key_release( rKey );
+    }
+  }
+  gpgme_release( ctx );
+    
+  /*
+  fprintf( stderr, "gpgmeplug receiverCertificateDaysLeftToExpiry returned %d\n", daysLeft );
+  */
+
+  return daysLeft;
+    
+    
+    
     /* PENDING(g10)
        Please return the number of days that are left until the
        certificate specified in the parameter certificate expires.
@@ -923,9 +1061,8 @@ bool certificateValidity( const char* certificate,
 void storeNewCharPtr( char** dest, const char* src )
 {
   int sLen = strlen( src );
-  *dest = malloc( sLen + 1 );
+  *dest = xmalloc( sLen + 1 );
   strcpy( *dest, src );
-  (*dest)[sLen] = '\0';
 }
 
 
@@ -981,17 +1118,27 @@ bool signMessage( const char*  cleartext,
   /* select the signer's key if provided */
   if (certificate != 0) {
       err = gpgme_op_keylist_start(ctx, certificate, 0);
-      if (err == GPGME_No_Error) {
-         /* we only support one signer for now */
-         err = gpgme_op_keylist_next(ctx, &rKey);
-         if (err == GPGME_No_Error) {
-             /* clear existing signers */
-             gpgme_signers_clear(ctx);
-             /* set the signing key */
-             gpgme_signers_add(ctx, rKey);
-         }
-         gpgme_op_keylist_end(ctx);
+      while (err == GPGME_No_Error) {
+          err = gpgme_op_keylist_next(ctx, &rKey);
+          if (err == GPGME_No_Error) {
+              unsigned long u;
+              u = gpgme_key_get_ulong_attr(rKey, GPGME_ATTR_CAN_SIGN, 0, 0);
+              if( u ) {
+
+//                const char* s;
+//                s = gpgme_key_get_string_attr(rKey, GPGME_ATTR_FPR, 0, 0);
+//                fprintf( stderr, "gpgmeplug signMessage signing with key: %s\n", s );
+
+                  /* clear existing signers */
+                  gpgme_signers_clear(ctx);
+                  /* set the signing key */
+                  gpgme_signers_add(ctx, rKey);
+                  /* we only support one signer for now */
+                  break;
+              }
+          }
       }
+      gpgme_op_keylist_end(ctx);
   }
 
   /* PENDING(g10) Implement this
@@ -1004,8 +1151,8 @@ bool signMessage( const char*  cleartext,
                             strlen( cleartext ), 1 );
   gpgme_data_new ( &sig );
 
-  // NOTE: Currently we support Opaque signed messages only for S/MIME,
-  //       but not for OpenPGP mode!
+  /* NOTE: Currently we support Opaque signed messages only for S/MIME,
+     but not for OpenPGP mode! */
   if( GPGMEPLUG_PROTOCOL == GPGME_PROTOCOL_CMS )
     bIsOpaque = (SignatureCompoundMode_Opaque == signatureCompoundMode());
   else
@@ -1139,467 +1286,402 @@ err = 0;
 }
 
 
-static const char*
-sig_status_to_string( GpgmeSigStat status )
+
+bool storeCertificatesFromMessage(
+        const char* ciphertext ){ return true; }
+
+
+/* returns address if address doesn't contain a <xxx> part
+ * else it returns a new string xxx and frees address
+ */
+static char* parseAddress( char* address )
 {
-  const char *result;
+  char* result = address;
+  char* i;
+  char* j;
+  if( !result ) return result;
+  i = index( address, '<' );
+  if( i ) {
+    j = index( i+1, '>' );
+    if( j == NULL ) j = address+strlen(address);
+    result = xmalloc( j-i );
+    strncpy( result, i+1, j-i-1 );
+    result[j-i-1] = '\0';
+    free( address );
+  } else {
+    i = address;
+    j = i+strlen(address);
+  }
+  {
+    /* remove surrounding whitespace */
+    char* k = result+(j-i-1);
+    char* l = result;
+    while( isspace( *l ) ) ++l;
+    while( isspace( *k ) ) --k;
+    if( l != result || k != result+(j-i-1) ) {
+      char* result2 = xmalloc( k-l+2 );
+      strncpy( result2, l, k-l+1 );
+      result2[k-l+1] = '\0';
+      free(result);
+      result = result2;
+    }
+  }
+  return result;
+}
 
-  switch (status) {
-    case GPGME_SIG_STAT_NONE:
-      result = "Oops: Signature not verified";
-      break;
-    case GPGME_SIG_STAT_NOSIG:
-      result = "No signature found";
-      break;
-    case GPGME_SIG_STAT_GOOD:
-      result = "Good signature";
-      break;
-    case GPGME_SIG_STAT_BAD:
-      result = "BAD signature";
+static char* nextAddress( const char** address )
+{
+  const char *start = *address;
+  char* result = NULL;
+  int quote = 0;
+  int comment = 0;
+  int found = 0;
+  if( *address == NULL ) return NULL;
+  while( **address ) {
+
+    switch( **address ) {
+    case '\\': /* escaped character */
+      ++(*address);
       break;
-    case GPGME_SIG_STAT_NOKEY:
-      result = "No public key to verify the signature";
+    case '"':
+      if( comment == 0 ) {
+        if( quote > 0 ) --quote;
+        else ++quote;
+      }
       break;
-    case GPGME_SIG_STAT_ERROR:
-      result = "Error verifying the signature";
+    case '(': /* comment start */
+      if( quote == 0 ) ++comment;
       break;
-    case GPGME_SIG_STAT_DIFF:
-      result = "Different results for signatures";
+    case ')': /* comment end */
+      if( quote == 0 ) --comment;
       break;
-    default:
-      result = "Error: Unknown status";
+    case '\0':
+    case '\1': /* delimiter */
+      if( quote == 0 && comment == 0 ) {
+        found = 1;
+      }
       break;
+    }
+    ++(*address);
+    if( found ) break;
   }
-
-  return result;
+  if( found || **address == 0 ) {
+    size_t len;
+    len = *address - start;
+    if( len > 0 ) {
+      if( **address != 0 ) --len;
+      result = xmalloc( len*sizeof(char)+1 );
+      strncpy( result, start, len );
+      result[len] = '\0';
+    }
+  }
+  return parseAddress(result);
 }
 
-
-bool checkMessageSignature( char** cleartext,
-                            const char* signaturetext,
-                            bool signatureIsBinary,
-                            int signatureLen,
-                            struct SignatureMetaData* sigmeta )
+bool encryptMessage( const char*  cleartext,
+                     const char** ciphertext,
+                     const size_t* cipherLen,
+                     const char*  certificate,
+                     struct StructuringInfo* structuring,
+                     int* errId,
+                     char** errTxt )
 {
   GpgmeCtx ctx;
-  GpgmeSigStat status;
-  unsigned long sumGPGME;
-  SigStatusFlags sumPlug;
-  GpgmeData datapart, sigpart;
-  char* rClear = 0;
-  size_t clearLen;
   GpgmeError err;
-  GpgmeKey key;
-  time_t created;
-  int sig_idx = 0;
-  const char* statusStr;
-  const char* fpr;
-  bool isOpaqueSigned;
-
-  if( !cleartext ) {
-    if( sigmeta )
-      storeNewCharPtr( &sigmeta->status,
-                        __GPGMEPLUG_ERROR_CLEARTEXT_IS_ZERO );
-
-    return false;
-  }
+  GpgmeData gCiphertext, gPlaintext;
+  GpgmeRecipients rset;
+  char*  rCiph = 0;
+  bool   bOk   = false;
 
-  isOpaqueSigned = !*cleartext;
+  init_StructuringInfo( structuring );
 
-  gpgme_new( &ctx );
+  gpgme_new (&ctx);
   gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
-  gpgme_set_armor (ctx,    signatureIsBinary ? 0 : 1);
-  /*  gpgme_set_textmode (ctx, signatureIsBinary ? 0 : 1); */
 
-  if( isOpaqueSigned )
-    gpgme_data_new( &datapart );
-  else
-    gpgme_data_new_from_mem( &datapart, *cleartext,
-                             strlen( *cleartext ), 1 );
+  gpgme_set_armor (ctx, __GPGMEPLUG_ENCRYPTED_CODE_IS_BINARY ? 0 : 1);
+  /*  gpgme_set_textmode (ctx, 1); */
 
-  gpgme_data_new_from_mem( &sigpart,
-                           signaturetext,
-                           signatureIsBinary
-                           ? signatureLen
-                           : strlen( signaturetext ),
-                           1 );
+  gpgme_data_new_from_mem (&gPlaintext, cleartext,
+                            1+strlen( cleartext ), 1 );
+  err = gpgme_data_new ( &gCiphertext );
 
-  gpgme_op_verify( ctx, sigpart, datapart, &status );
+  gpgme_recipients_new (&rset);
 
-  if( isOpaqueSigned ) {
-    rClear = gpgme_data_release_and_get_mem( datapart, &clearLen );
-    *cleartext = malloc( clearLen + 1 );
-    if( *cleartext ) {
-      if( clearLen )
-        strncpy(*cleartext, rClear, clearLen );
-      (*cleartext)[clearLen] = '\0';
-    }
-    free( rClear );
+  /*
+  if( GPGMEPLUG_PROTOCOL == GPGME_PROTOCOL_CMS )
+  {
+    gpgme_recipients_add_name (rset,
+      "/CN=test cert 1,OU=Aegypten Project,O=g10 Code GmbH,L=DÃ?sseldorf,C=DE" );
+
+    fputs( "\nGPGSMPLUG encryptMessage() using test key of Aegypten Project\n", stderr );
   }
   else
-    gpgme_data_release( datapart );
-
-  gpgme_data_release( sigpart );
-
-  /* Provide information in the sigmeta struct */
-  /* the status string */
-  statusStr = sig_status_to_string( status );
-  sigmeta->status = malloc( strlen( statusStr ) + 1 );
-  if( sigmeta->status ) {
-    strcpy( sigmeta->status, statusStr );
-    sigmeta->status[strlen( statusStr )] = '\0';
-  } else
-    ; /* nothing to do, is already 0 */
-
-  /* Extended information for any number of signatures. */
-  fpr = gpgme_get_sig_status( ctx, sig_idx, &status, &created );
-  sigmeta->extended_info = 0;
-  while( fpr != NULL ) {
-    struct tm* ctime_val;
-    const char* sig_status;
-
-    void* realloc_return = realloc( sigmeta->extended_info,
-                                    sizeof( struct SignatureMetaDataExtendedInfo ) * ( sig_idx + 1 ) );
-    if( realloc_return ) {
-      sigmeta->extended_info = realloc_return;
+  */
+  {
+    const char* p = certificate;
+    char* tok;
+    while( (tok = nextAddress( &p ) ) != 0 ) {
+      gpgme_recipients_add_name (rset, tok );
+      fprintf( stderr, "\nGPGMEPLUG encryptMessage() using addressee %s\n", tok );
+      free(tok);
+    }
+  }
 
-      /* clear the data area */
-      memset( &sigmeta->extended_info[sig_idx], 0, sizeof (struct SignatureMetaDataExtendedInfo) );
+  /* PENDING(g10) Implement this
+     Possible values: RSA = 1, SHA1 = 2, TripleDES = 3
+     gpgme_set_encryption_algorithm( ctx, config.encryptionAlgorithm );
 
-      /* the creation time */
-      sigmeta->extended_info[sig_idx].creation_time = malloc( sizeof( struct tm ) );
-      if( sigmeta->extended_info[sig_idx].creation_time ) {
-        ctime_val = localtime( &created );
-        memcpy( sigmeta->extended_info[sig_idx].creation_time,
-                ctime_val, sizeof( struct tm ) );
-      }
+     -> Your are mixing public key and symmetric algorithms.  The
+     latter may be configured but the sphix specifications do opnly
+     allow 3-DES so this is not nothing we need to do.  The proper way
+     to select the symmetric algorithm is anyway by looking at the
+     capabilities of the certificate because this is the only way to
+     know what the recipient can accept. [wk 2002-03-23]
 
-      /* the extended signature verification status */
-      sumGPGME = gpgme_get_sig_ulong_attr( ctx,
-                                           sig_idx,
-                                           GPGME_ATTR_SIG_SUMMARY,
-                                           0 );
-      // translate GPGME status flags to common CryptPlug status flags
-      sumPlug = 0;
-      if( sumGPGME & GPGME_SIGSUM_VALID       ) sumPlug |= SigStat_VALID      ;
-      if( sumGPGME & GPGME_SIGSUM_GREEN       ) sumPlug |= SigStat_GREEN      ;
-      if( sumGPGME & GPGME_SIGSUM_RED         ) sumPlug |= SigStat_RED        ;
-      if( sumGPGME & GPGME_SIGSUM_KEY_REVOKED ) sumPlug |= SigStat_KEY_REVOKED;
-      if( sumGPGME & GPGME_SIGSUM_KEY_EXPIRED ) sumPlug |= SigStat_KEY_EXPIRED;
-      if( sumGPGME & GPGME_SIGSUM_SIG_EXPIRED ) sumPlug |= SigStat_SIG_EXPIRED;
-      if( sumGPGME & GPGME_SIGSUM_KEY_MISSING ) sumPlug |= SigStat_KEY_MISSING;
-      if( sumGPGME & GPGME_SIGSUM_CRL_MISSING ) sumPlug |= SigStat_CRL_MISSING;
-      if( sumGPGME & GPGME_SIGSUM_CRL_TOO_OLD ) sumPlug |= SigStat_CRL_TOO_OLD;
-      if( sumGPGME & GPGME_SIGSUM_BAD_POLICY  ) sumPlug |= SigStat_BAD_POLICY ;
-      if( sumGPGME & GPGME_SIGSUM_SYS_ERROR   ) sumPlug |= SigStat_SYS_ERROR  ;
-      if( !sumPlug )
-        sumPlug = SigStat_NUMERICAL_CODE | sumGPGME;
-      sigmeta->extended_info[sig_idx].sigStatusFlags = sumPlug;
+     PENDING(g10) Implement this
+     gpgme_set_encryption_check_certificate_path(
+     config.checkCertificatePath )
 
-      sigmeta->extended_info[sig_idx].validity = GPGME_VALIDITY_UNKNOWN;
+     PENDING(g10) Implement this
+     gpgme_set_encryption_check_certificate_path_to_root(
+     config.checkEncryptionCertificatePathToRoot )
 
-      err = gpgme_get_sig_key (ctx, sig_idx, &key);
+     -> Not checking a certificate up to the ROOT CA is dangerous and
+     stupid. There is no need for those options.  [wk 2002-03-23] */
 
-      if ( err == GPGME_No_Error) {
-        const char* attr_string;
-        unsigned long attr_ulong;
 
-        /* extract key identidy */
-        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_KEYID, 0, 0);
-        if (attr_string != 0)
-            storeNewCharPtr( &sigmeta->extended_info[sig_idx].keyid, attr_string );
 
-        /* extract finger print */
-        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_FPR, 0, 0);
-        if (attr_string != 0)
-            storeNewCharPtr( &sigmeta->extended_info[sig_idx].fingerprint,
-                            attr_string );
+  err = gpgme_op_encrypt (ctx, rset, gPlaintext, gCiphertext );
+  if( err ) {
+    fprintf( stderr, "\ngpgme_op_encrypt() returned this error code:  %i\n", err );
+    if( errId )
+      *errId = err;
+    if( errTxt ) {
+      const char* _errTxt = gpgme_strerror( err );
+      *errTxt = malloc( strlen( _errTxt ) + 100 ); // leave room for reason string
+      if( *errTxt ) {
+        char* opInfo;
+        strcpy(*errTxt, _errTxt );
+        opInfo = gpgme_get_op_info(ctx, 0);
+        if( NULL != opInfo && *opInfo ){
+          const int opLen = strlen( opInfo );
+          const int reasonLen = 8;
+          char reason[ 1+reasonLen ];
+          char* pos1;
+          strcpy( reason, "<reason>" );
+          pos1 = strstr( opInfo, reason );
+          if( NULL != pos1 && 
+              opLen > reasonLen + (pos1 - opInfo) ){
+            char* pos2;
+            pos1 += reasonLen;
+            pos2 = strchr( pos1, '<' );
+            if( NULL != pos2 &&
+                pos1 < pos2 ){
+              long int reasonId;
+              strcat( *errTxt, " - " );
+              *pos2 = '\0';
+              fprintf( stderr, "                        and this reason code:  %s\n\n", pos1 );
+              reasonId = strtol( pos1, NULL, 10 );
+              switch( reasonId ) {
+                case  0: strcat( *errTxt, "No specific reason given" );
+                         break;
+                case  1: strcat( *errTxt, "Not Found" );
+                         break;
+                case  2: strcat( *errTxt, "Ambigious specification" );
+                         break;
+                case  3: strcat( *errTxt, "Key can't be used for operation" );
+                         break;
+                case  4: strcat( *errTxt, "Key has been revoked" );
+                         break;
+                case  5: strcat( *errTxt, "Key has expired" );
+                         break;
+                case  6: strcat( *errTxt, "No CRL known for certificate" );
+                         break;
+                case  7: strcat( *errTxt, "No current CRL available" );
+                         break;
+                case  8: strcat( *errTxt, "Contraints not matched" );
+                         break;
+                default: {
+                           strcat( *errTxt, "Extended error Id: #" );
+                           strcat( *errTxt, pos1 );
+                         }  
+              }
+              *pos2 = '<';
+            }
+          }
+          free( opInfo );
+        }
+      }
+    }
+  }
 
-        /* algorithms useable with this key */
-        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_ALGO, 0, 0);
-        if (attr_string != 0)
-            storeNewCharPtr( &sigmeta->extended_info[sig_idx].algo,
-                            attr_string );
-        attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_ALGO, 0, 0);
-        sigmeta->extended_info[sig_idx].algo_num = attr_ulong;
+  gpgme_recipients_release (rset);
+  gpgme_data_release (gPlaintext);
 
-        /* extract key validity */
-        attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_VALIDITY, 0, 0);
-        sigmeta->extended_info[sig_idx].validity = attr_ulong;
+  if( err == GPGME_No_Error ) {
+    if( __GPGMEPLUG_ENCRYPTED_CODE_IS_BINARY ) {
+      *ciphertext = gpgme_data_release_and_get_mem( gCiphertext, (size_t*)cipherLen );
+      bOk = true;
+    }
+    else {
+      rCiph = gpgme_data_release_and_get_mem( gCiphertext, (size_t*)cipherLen );
+      *ciphertext = malloc( *cipherLen + 1 );
+      if( *ciphertext ) {
+        if( *cipherLen ) {
+          bOk = true;
+          strncpy((char*)*ciphertext, rCiph, *cipherLen );
+        }
+        ((char*)(*ciphertext))[*cipherLen] = 0;
+      }
+      free( rCiph );
+    }
+  }
+  else {
+    gpgme_data_release ( gCiphertext );
+    *ciphertext = 0;
+    /* error handling is missing: if only one untrusted key was found
+      (or none at all), gpg won't sign the message.  (hier fehlt eine
+      Fehlerbehandlung: fuer einen Recipient nur ein untrusted key
+      (oder gar keiner) gefunden wurde, verweigert gpg das signieren.)
+    */
+  }
 
-        /* extract user id, according to the documentation it's representable
-        * as a number, but it seems that it also has a string representation
-        */
-        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_USERID, 0, 0);
-        if (attr_string != 0)
-            storeNewCharPtr( &sigmeta->extended_info[sig_idx].userid,
-                            attr_string );
-        attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_USERID, 0, 0);
-        sigmeta->extended_info[sig_idx].userid_num = attr_ulong;
+  gpgme_release (ctx);
 
-        /* extract the length */
-        attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_LEN, 0, 0);
-        sigmeta->extended_info[sig_idx].keylen = attr_ulong;
+  fflush( stderr );
 
-        /* extract the creation time of the key */
-        attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_CREATED, 0, 0);
-        sigmeta->extended_info[sig_idx].key_created = attr_ulong;
+  if( bOk && structuring ) {
+    structuring->includeCleartext = GPGMEPLUG_ENC_INCLUDE_CLEARTEXT;
+    structuring->makeMimeObject   = GPGMEPLUG_ENC_MAKE_MIME_OBJECT;
+    if( structuring->makeMimeObject ) {
+      structuring->makeMultiMime  = GPGMEPLUG_ENC_MAKE_MULTI_MIME;
+      storeNewCharPtr( &structuring->contentTypeMain,
+                       GPGMEPLUG_ENC_CTYPE_MAIN );
+      storeNewCharPtr( &structuring->contentDispMain,
+                       GPGMEPLUG_ENC_CDISP_MAIN );
+      storeNewCharPtr( &structuring->contentTEncMain,
+                       GPGMEPLUG_ENC_CTENC_MAIN );
+      if( structuring->makeMultiMime ) {
+        storeNewCharPtr( &structuring->contentTypeVersion,
+                         GPGMEPLUG_ENC_CTYPE_VERSION );
+        storeNewCharPtr( &structuring->contentDispVersion,
+                         GPGMEPLUG_ENC_CDISP_VERSION );
+        storeNewCharPtr( &structuring->contentTEncVersion,
+                         GPGMEPLUG_ENC_CTENC_VERSION );
+        storeNewCharPtr( &structuring->bodyTextVersion,
+                         GPGMEPLUG_ENC_BTEXT_VERSION );
+        storeNewCharPtr( &structuring->contentTypeCode,
+                         GPGMEPLUG_ENC_CTYPE_CODE );
+        storeNewCharPtr( &structuring->contentDispCode,
+                         GPGMEPLUG_ENC_CDISP_CODE );
+        storeNewCharPtr( &structuring->contentTEncCode,
+                         GPGMEPLUG_ENC_CTENC_CODE );
+      }
+    } else {
+      storeNewCharPtr( &structuring->flatTextPrefix,
+                       GPGMEPLUG_ENC_FLAT_PREFIX );
+      storeNewCharPtr( &structuring->flatTextSeparator,
+                       GPGMEPLUG_ENC_FLAT_SEPARATOR );
+      storeNewCharPtr( &structuring->flatTextPostfix,
+                       GPGMEPLUG_ENC_FLAT_POSTFIX );
+    }
+  }
+  return bOk;
+}
 
-        /* extract the expiration time of the key */
-        attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_EXPIRE, 0, 0);
-        sigmeta->extended_info[sig_idx].key_expires = attr_ulong;
 
-        /* extract user name */
-        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_NAME, 0, 0);
-        if (attr_string != 0)
-            storeNewCharPtr( &sigmeta->extended_info[sig_idx].name,
-                            attr_string );
+bool encryptAndSignMessage( const char* cleartext,
+                            const char** ciphertext,
+                            const char* certificate,
+                            struct StructuringInfo* structuring )
+{
+  bool bOk;
 
-        /* extract email */
-        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_EMAIL, 0, 0);
-        if (attr_string != 0)
-            storeNewCharPtr( &sigmeta->extended_info[sig_idx].email,
-                            attr_string );
+  init_StructuringInfo( structuring );
 
-        /* extract the comment */
-        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_COMMENT, 0, 0);
-        if (attr_string != 0)
-            storeNewCharPtr( &sigmeta->extended_info[sig_idx].comment,
-                            attr_string );
-      }
-      else
-        storeNewCharPtr( &sigmeta->extended_info[sig_idx].fingerprint, fpr );
+  bOk = false;
 
-      sig_status = sig_status_to_string( status );
-      storeNewCharPtr( &sigmeta->extended_info[sig_idx].status_text,
-                       sig_status );
+  /* implementation of this function is still missing */
 
-    } else
-      break; /* if allocation fails once, it isn't likely to
-                succeed the next time either */
-
-    fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
-  }
-  sigmeta->extended_info_count = sig_idx;
-  sigmeta->nota_xml = gpgme_get_notation( ctx );
-  sigmeta->status_code = status;
-
-  gpgme_release( ctx );
-  return ( status == GPGME_SIG_STAT_GOOD );
-}
-
-bool storeCertificatesFromMessage(
-        const char* ciphertext ){ return true; }
-
-
-/* returns address if address doesn't contain a <xxx> part
- * else it returns a new string xxx and frees address
- */
-static char* parseAddress( char* address )
-{
-  char* result = address;
-  char* i;
-  char* j;
-  if( !result ) return result;
-  i = index( address, '<' );
-  if( i ) {
-    j = index( i+1, '>' );
-    if( j == NULL ) j = address+strlen(address);
-    result = malloc( j-i );
-    strncpy( result, i+1, j-i-1 );
-    result[j-i-1] = '\0';
-    free( address );
-  } else {
-    i = address;
-    j = i+strlen(address);
-  }
-  {
-    /* remove surrounding whitespace */
-    char* k = result+(j-i-1);
-    char* l = result;
-    while( isspace( *l ) ) ++l;
-    while( isspace( *k ) ) --k;
-    if( l != result || k != result+(j-i-1) ) {
-      char* result2 = malloc( k-l+2 );
-      strncpy( result2, l, k-l+1 );
-      result2[k-l+1] = '\0';
-      free(result);
-      result = result2;
-    }
-  }
-  return result;
-}
-
-static char* nextAddress( const char** address )
-{
-  const char *start = *address;
-  char* result = NULL;
-  int quote = 0;
-  int comment = 0;
-  int found = 0;
-  if( *address == NULL ) return NULL;
-  while( **address ) {
-
-    switch( **address ) {
-    case '\\': /* escaped character */
-      ++(*address);
-      break;
-    case '"':
-      if( comment == 0 ) {
-        if( quote > 0 ) --quote;
-        else ++quote;
-      }
-      break;
-    case '(': /* comment start */
-      if( quote == 0 ) ++comment;
-      break;
-    case ')': /* comment end */
-      if( quote == 0 ) --comment;
-      break;
-    case '\0':
-    case '\1': /* delimiter */
-      if( quote == 0 && comment == 0 ) {
-        found = 1;
+  if( bOk && structuring ) {
+    structuring->includeCleartext = GPGMEPLUG_ENCSIGN_INCLUDE_CLEARTEXT;
+    structuring->makeMimeObject   = GPGMEPLUG_ENCSIGN_MAKE_MIME_OBJECT;
+    if( structuring->makeMimeObject ) {
+      structuring->makeMultiMime  = GPGMEPLUG_ENCSIGN_MAKE_MULTI_MIME;
+      storeNewCharPtr( &structuring->contentTypeMain,
+                       GPGMEPLUG_ENCSIGN_CTYPE_MAIN );
+      storeNewCharPtr( &structuring->contentDispMain,
+                       GPGMEPLUG_ENCSIGN_CDISP_MAIN );
+      storeNewCharPtr( &structuring->contentTEncMain,
+                       GPGMEPLUG_ENCSIGN_CTENC_MAIN );
+      if( structuring->makeMultiMime ) {
+        storeNewCharPtr( &structuring->contentTypeVersion,
+                         GPGMEPLUG_ENCSIGN_CTYPE_VERSION );
+        storeNewCharPtr( &structuring->contentDispVersion,
+                         GPGMEPLUG_ENCSIGN_CDISP_VERSION );
+        storeNewCharPtr( &structuring->contentTEncVersion,
+                         GPGMEPLUG_ENCSIGN_CTENC_VERSION );
+        storeNewCharPtr( &structuring->bodyTextVersion,
+                         GPGMEPLUG_ENCSIGN_BTEXT_VERSION );
+        storeNewCharPtr( &structuring->contentTypeCode,
+                         GPGMEPLUG_ENCSIGN_CTYPE_CODE );
+        storeNewCharPtr( &structuring->contentDispCode,
+                         GPGMEPLUG_ENCSIGN_CDISP_CODE );
+        storeNewCharPtr( &structuring->contentTEncCode,
+                         GPGMEPLUG_ENCSIGN_CTENC_CODE );
       }
-      break;
-    }
-    ++(*address);
-    if( found ) break;
-  }
-  if( found || **address == 0 ) {
-    size_t len;
-    len = *address - start;
-    if( len > 0 ) {
-      if( **address != 0 ) --len;
-      result = malloc( len*sizeof(char)+1 );
-      strncpy( result, start, len );
-      result[len] = '\0';
+    } else {
+      storeNewCharPtr( &structuring->flatTextPrefix,
+                       GPGMEPLUG_ENCSIGN_FLAT_PREFIX );
+      storeNewCharPtr( &structuring->flatTextSeparator,
+                       GPGMEPLUG_ENCSIGN_FLAT_SEPARATOR );
+      storeNewCharPtr( &structuring->flatTextPostfix,
+                       GPGMEPLUG_ENCSIGN_FLAT_POSTFIX );
     }
   }
-  return parseAddress(result);
+  return bOk;
 }
 
-/*
-  Find all certificate for a given addressee and return them in a
-  '\1' separated list.
-  NOTE: The certificate parameter must point to an allready allocated
-  block of memory which is large enough to hold the complete list.
-*/
-bool findCertificates( const char* addressee, char** certificates )
-{
-  GpgmeCtx ctx;
-  GpgmeError err;
-  GpgmeKey rKey;
-  const char *s;
-  const char *s2;
-  int nFound = 0;
-
-  strcpy( *certificates, "" );
-
-  gpgme_new (&ctx);
-  gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
-
-  err = gpgme_op_keylist_start(ctx, addressee, 0);
-  while( GPGME_No_Error == err ) {
-    err = gpgme_op_keylist_next(ctx, &rKey);
-    if( GPGME_No_Error == err ) {
-      s = gpgme_key_get_string_attr (rKey, GPGME_ATTR_USERID, NULL, 0);
-      if( s ) {
-        s2 = gpgme_key_get_string_attr (rKey, GPGME_ATTR_FPR, NULL, 0);
-        if( s2 ) {
-          if( nFound )
-            strcat(*certificates,"\1" );
-          strcat( *certificates, s );
-          strcat( *certificates, "    (" );
-          strcat( *certificates, s2 );
-          strcat( *certificates, ")" );
-          ++nFound;
-        }
-      }
-    }
-  }
-  gpgme_op_keylist_end( ctx );
-  gpgme_release (ctx);
-
-  return ( 0 < nFound );
-}
 
-bool encryptMessage( const char*  cleartext,
-                     const char** ciphertext,
-                     const size_t* cipherLen,
-                     const char*  certificate,
-                     struct StructuringInfo* structuring,
+bool decryptMessage( const char* ciphertext,
+                     bool        cipherIsBinary,
+                     int         cipherLen,
+                     const char** cleartext,
+                     const char* certificate,
                      int* errId,
                      char** errTxt )
 {
   GpgmeCtx ctx;
   GpgmeError err;
   GpgmeData gCiphertext, gPlaintext;
-  GpgmeRecipients rset;
+  size_t rCLen = 0;
   char*  rCiph = 0;
-  bool   bOk   = false;
+  bool bOk = false;
 
-  init_StructuringInfo( structuring );
+  if( !ciphertext )
+    return false;
 
-  gpgme_new (&ctx);
+  err = gpgme_new (&ctx);
   gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
-
-  gpgme_set_armor (ctx, __GPGMEPLUG_ENCRYPTED_CODE_IS_BINARY ? 0 : 1);
-  /*  gpgme_set_textmode (ctx, 1); */
-
-  gpgme_data_new_from_mem (&gPlaintext, cleartext,
-                            1+strlen( cleartext ), 1 );
-  err = gpgme_data_new ( &gCiphertext );
-
-  gpgme_recipients_new (&rset);
+  
+  gpgme_set_armor (ctx, cipherIsBinary ? 0 : 1);
+  /*  gpgme_set_textmode (ctx, cipherIsBinary ? 0 : 1); */
 
   /*
-  if( GPGMEPLUG_PROTOCOL == GPGME_PROTOCOL_CMS )
-  {
-    gpgme_recipients_add_name (rset,
-      "/CN=test cert 1,OU=Aegypten Project,O=g10 Code GmbH,L=Düsseldorf,C=DE" );
-
-    fputs( "\nGPGSMPLUG encryptMessage() using test key of Aegypten Project\n", stderr );
-  }
-  else
-  */
-  {
-    const char* p = certificate;
-    char* tok;
-    while( (tok = nextAddress( &p ) ) != 0 ) {
-      gpgme_recipients_add_name (rset, tok );
-      fprintf( stderr, "\nGPGMEPLUG encryptMessage() using addressee %s\n", tok );
-      free(tok);
-    }
-  }
-
-  /* PENDING(g10) Implement this
-     Possible values: RSA = 1, SHA1 = 2, TripleDES = 3
-     gpgme_set_encryption_algorithm( ctx, config.encryptionAlgorithm );
-
-     -> Your are mixing public key and symmetric algorithms.  The
-     latter may be configured but the sphix specifications do opnly
-     allow 3-DES so this is not nothing we need to do.  The proper way
-     to select the symmetric algorithm is anyway by looking at the
-     capabilities of the certificate because this is the only way to
-     know what the recipient can accept. [wk 2002-03-23]
-
-     PENDING(g10) Implement this
-     gpgme_set_encryption_check_certificate_path(
-     config.checkCertificatePath )
-
-     PENDING(g10) Implement this
-     gpgme_set_encryption_check_certificate_path_to_root(
-     config.checkEncryptionCertificatePathToRoot )
-
-     -> Not checking a certificate up to the ROOT CA is dangerous and
-     stupid. There is no need for those options.  [wk 2002-03-23] */
-
+  gpgme_data_new_from_mem( &gCiphertext, ciphertext,
+                           1+strlen( ciphertext ), 1 ); */
+  gpgme_data_new_from_mem( &gCiphertext,
+                           ciphertext,
+                           cipherIsBinary
+                           ? cipherLen
+                           : strlen( ciphertext ),
+                           1 );
 
+  gpgme_data_new( &gPlaintext );
 
-  err = gpgme_op_encrypt (ctx, rset, gPlaintext, gCiphertext );
+  err = err = gpgme_op_decrypt( ctx, gCiphertext, gPlaintext );
   if( err ) {
-    fprintf( stderr, "\ngpgme_op_encrypt() returned this error code:  %i\n\n", err );
+    fprintf( stderr, "\ngpgme_op_decrypt() returned this error code:  %i\n\n", err );
     if( errId )
       *errId = err;
     if( errTxt ) {
@@ -1609,173 +1691,10 @@ bool encryptMessage( const char*  cleartext,
         strcpy(*errTxt, _errTxt );
     }
   }
+  
+  gpgme_data_release( gCiphertext );
 
-  gpgme_recipients_release (rset);
-  gpgme_data_release (gPlaintext);
-
-  if( err == GPGME_No_Error ) {
-    if( __GPGMEPLUG_ENCRYPTED_CODE_IS_BINARY ) {
-      *ciphertext = gpgme_data_release_and_get_mem( gCiphertext, (size_t*)cipherLen );
-      bOk = true;
-    }
-    else {
-      rCiph = gpgme_data_release_and_get_mem( gCiphertext, (size_t*)cipherLen );
-      *ciphertext = malloc( *cipherLen + 1 );
-      if( *ciphertext ) {
-        if( *cipherLen ) {
-          bOk = true;
-          strncpy((char*)*ciphertext, rCiph, *cipherLen );
-        }
-        ((char*)(*ciphertext))[*cipherLen] = 0;
-      }
-      free( rCiph );
-    }
-  }
-  else {
-    gpgme_data_release ( gCiphertext );
-    *ciphertext = 0;
-    /* error handling is missing: if only one untrusted key was found
-      (or none at all), gpg won't sign the message.  (hier fehlt eine
-      Fehlerbehandlung: fuer einen Recipient nur ein untrusted key
-      (oder gar keiner) gefunden wurde, verweigert gpg das signieren.)
-    */
-  }
-
-  gpgme_release (ctx);
-
-  fflush( stderr );
-
-  if( bOk && structuring ) {
-    structuring->includeCleartext = GPGMEPLUG_ENC_INCLUDE_CLEARTEXT;
-    structuring->makeMimeObject   = GPGMEPLUG_ENC_MAKE_MIME_OBJECT;
-    if( structuring->makeMimeObject ) {
-      structuring->makeMultiMime  = GPGMEPLUG_ENC_MAKE_MULTI_MIME;
-      storeNewCharPtr( &structuring->contentTypeMain,
-                       GPGMEPLUG_ENC_CTYPE_MAIN );
-      storeNewCharPtr( &structuring->contentDispMain,
-                       GPGMEPLUG_ENC_CDISP_MAIN );
-      storeNewCharPtr( &structuring->contentTEncMain,
-                       GPGMEPLUG_ENC_CTENC_MAIN );
-      if( structuring->makeMultiMime ) {
-        storeNewCharPtr( &structuring->contentTypeVersion,
-                         GPGMEPLUG_ENC_CTYPE_VERSION );
-        storeNewCharPtr( &structuring->contentDispVersion,
-                         GPGMEPLUG_ENC_CDISP_VERSION );
-        storeNewCharPtr( &structuring->contentTEncVersion,
-                         GPGMEPLUG_ENC_CTENC_VERSION );
-        storeNewCharPtr( &structuring->bodyTextVersion,
-                         GPGMEPLUG_ENC_BTEXT_VERSION );
-        storeNewCharPtr( &structuring->contentTypeCode,
-                         GPGMEPLUG_ENC_CTYPE_CODE );
-        storeNewCharPtr( &structuring->contentDispCode,
-                         GPGMEPLUG_ENC_CDISP_CODE );
-        storeNewCharPtr( &structuring->contentTEncCode,
-                         GPGMEPLUG_ENC_CTENC_CODE );
-      }
-    } else {
-      storeNewCharPtr( &structuring->flatTextPrefix,
-                       GPGMEPLUG_ENC_FLAT_PREFIX );
-      storeNewCharPtr( &structuring->flatTextSeparator,
-                       GPGMEPLUG_ENC_FLAT_SEPARATOR );
-      storeNewCharPtr( &structuring->flatTextPostfix,
-                       GPGMEPLUG_ENC_FLAT_POSTFIX );
-    }
-  }
-  return bOk;
-}
-
-
-bool encryptAndSignMessage( const char* cleartext,
-                            const char** ciphertext,
-                            const char* certificate,
-                            struct StructuringInfo* structuring )
-{
-  bool bOk;
-
-  init_StructuringInfo( structuring );
-
-  bOk = false;
-
-  /* implementation of this function is still missing */
-
-  if( bOk && structuring ) {
-    structuring->includeCleartext = GPGMEPLUG_ENCSIGN_INCLUDE_CLEARTEXT;
-    structuring->makeMimeObject   = GPGMEPLUG_ENCSIGN_MAKE_MIME_OBJECT;
-    if( structuring->makeMimeObject ) {
-      structuring->makeMultiMime  = GPGMEPLUG_ENCSIGN_MAKE_MULTI_MIME;
-      storeNewCharPtr( &structuring->contentTypeMain,
-                       GPGMEPLUG_ENCSIGN_CTYPE_MAIN );
-      storeNewCharPtr( &structuring->contentDispMain,
-                       GPGMEPLUG_ENCSIGN_CDISP_MAIN );
-      storeNewCharPtr( &structuring->contentTEncMain,
-                       GPGMEPLUG_ENCSIGN_CTENC_MAIN );
-      if( structuring->makeMultiMime ) {
-        storeNewCharPtr( &structuring->contentTypeVersion,
-                         GPGMEPLUG_ENCSIGN_CTYPE_VERSION );
-        storeNewCharPtr( &structuring->contentDispVersion,
-                         GPGMEPLUG_ENCSIGN_CDISP_VERSION );
-        storeNewCharPtr( &structuring->contentTEncVersion,
-                         GPGMEPLUG_ENCSIGN_CTENC_VERSION );
-        storeNewCharPtr( &structuring->bodyTextVersion,
-                         GPGMEPLUG_ENCSIGN_BTEXT_VERSION );
-        storeNewCharPtr( &structuring->contentTypeCode,
-                         GPGMEPLUG_ENCSIGN_CTYPE_CODE );
-        storeNewCharPtr( &structuring->contentDispCode,
-                         GPGMEPLUG_ENCSIGN_CDISP_CODE );
-        storeNewCharPtr( &structuring->contentTEncCode,
-                         GPGMEPLUG_ENCSIGN_CTENC_CODE );
-      }
-    } else {
-      storeNewCharPtr( &structuring->flatTextPrefix,
-                       GPGMEPLUG_ENCSIGN_FLAT_PREFIX );
-      storeNewCharPtr( &structuring->flatTextSeparator,
-                       GPGMEPLUG_ENCSIGN_FLAT_SEPARATOR );
-      storeNewCharPtr( &structuring->flatTextPostfix,
-                       GPGMEPLUG_ENCSIGN_FLAT_POSTFIX );
-    }
-  }
-  return bOk;
-}
-
-
-bool decryptMessage( const char* ciphertext,
-                     bool        cipherIsBinary,
-                     int         cipherLen,
-                     const char** cleartext,
-                     const char* certificate )
-{
-  GpgmeCtx ctx;
-  GpgmeError err;
-  GpgmeData gCiphertext, gPlaintext;
-  size_t rCLen = 0;
-  char*  rCiph = 0;
-  bool bOk = false;
-
-  if( !ciphertext )
-    return false;
-
-  err = gpgme_new (&ctx);
-  gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
-  
-  gpgme_set_armor (ctx, cipherIsBinary ? 0 : 1);
-  /*  gpgme_set_textmode (ctx, cipherIsBinary ? 0 : 1); */
-
-  /*
-  gpgme_data_new_from_mem( &gCiphertext, ciphertext,
-                           1+strlen( ciphertext ), 1 ); */
-  gpgme_data_new_from_mem( &gCiphertext,
-                           ciphertext,
-                           cipherIsBinary
-                           ? cipherLen
-                           : strlen( ciphertext ),
-                           1 );
-
-  gpgme_data_new( &gPlaintext );
-
-  gpgme_op_decrypt( ctx, gCiphertext, gPlaintext );
-  gpgme_data_release( gCiphertext );
-
-  rCiph = gpgme_data_release_and_get_mem( gPlaintext,  &rCLen );
+  rCiph = gpgme_data_release_and_get_mem( gPlaintext,  &rCLen );
 
   *cleartext = malloc( rCLen + 1 );
   if( *cleartext ) {
@@ -1798,6 +1717,9 @@ bool decryptAndCheckMessage( const char* ciphertext,
 
 const char* requestCertificateDialog(){ return 0; }
 
+
+/* The buffer generatedKey contains the LEN bytes you want.
+   Caller is responsible for freeing. */
 bool requestDecentralCertificate( const char* certparms, 
                                   char** generatedKey, int* length )
 {
@@ -1833,8 +1755,6 @@ bool requestDecentralCertificate( const char* certparms,
     *generatedKey = gpgme_data_release_and_get_mem (pub, &len);
     *length = len;
 
-    /* The buffer generatedKey contains the LEN bytes you want */
-    // Caller is responsible for freeing
     return true;
 }
 
@@ -1860,53 +1780,25 @@ const char* displayCRL(){ return 0; }
 
 void updateCRL(){}
 
-/*
- * Copyright (C) 2002 g10 Code GmbH
- * 
- *     This program 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.
- * 
- *     This program 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.
- * 
- *     You should have received a copy of the GNU General Public
- *     License along with this program; if not, write to the Free
- *     Software Foundation, Inc., 59 Temple Place - Suite 330,
- *     Boston, MA  02111, USA.
- */
-
-/* Max number of parts in a DN */
-#define MAX_GPGME_IDX 20
 
-/* some macros to replace ctype ones and avoid locale problems */
-#define spacep(p)   (*(p) == ' ' || *(p) == '\t')
-#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
-#define hexdigitp(a) (digitp (a)                     \
-                      || (*(a) >= 'A' && *(a) <= 'F')  \
-                      || (*(a) >= 'a' && *(a) <= 'f'))
-/* the atoi macros assume that the buffer has only valid digits */
-#define atoi_1(p)   (*(p) - '0' )
-#define atoi_2(p)   ((atoi_1(p) * 10) + atoi_1((p)+1))
-#define atoi_4(p)   ((atoi_2(p) * 100) + atoi_2((p)+2))
-#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
-                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
-#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+char *
+trim_trailing_spaces( char *string )
+{
+    char *p, *mark;
 
-#define safe_malloc( x ) malloc( x )
-#define xstrdup( x ) (x)?strdup(x):0
+    for( mark = NULL, p = string; *p; p++ ) {
+       if( isspace( *p ) ) {
+           if( !mark )
+               mark = p;
+       }
+       else
+           mark = NULL;
+    }
+    if( mark )
+       *mark = '\0' ;
 
-static void safe_free( void** x ) 
-{
-  free( *x );
-  *x = 0;
+    return string ;
 }
-/*#define safe_free( x ) free( x )*/
 
 /* Parse a DN and return an array-ized one.  This is not a validating
    parser and it does not support any old-stylish syntax; gpgme is
@@ -1926,9 +1818,14 @@ parse_dn_part (struct DnPair *array, const unsigned char *string)
   n = s - string;
   if (!n)
     return NULL; /* empty key */
-  array->key = p = safe_malloc (n+1);
-  memcpy (p, string, n); /* fixme: trim trailing spaces */
+  array->key = p = xmalloc (n+1);
+  
+  
+  memcpy (p, string, n);
   p[n] = 0;
+  trim_trailing_spaces (p);
+  if ( !strcmp (p, "1.2.840.113549.1.9.1") )
+    strcpy (p, "EMail");
   string = s + 1;
 
   if (*string == '#')
@@ -1940,7 +1837,9 @@ parse_dn_part (struct DnPair *array, const unsigned char *string)
       if (!n || (n & 1))
         return NULL; /* empty or odd number of digits */
       n /= 2;
-      array->value = p = safe_malloc (n+1);
+      array->value = p = xmalloc (n+1);
+      
+      
       for (s1=string; n; s1 += 2, n--)
         *p++ = xtoi_2 (s1);
       *p = 0;
@@ -1973,7 +1872,9 @@ parse_dn_part (struct DnPair *array, const unsigned char *string)
             n++;
         }
 
-      array->value = p = safe_malloc (n+1);
+      array->value = p = xmalloc (n+1);
+      
+      
       for (s=string; n; s++, n--)
         {
           if (*s == '\\')
@@ -2006,9 +1907,14 @@ parse_dn (const unsigned char *string)
   size_t arrayidx, arraysize;
   int i;
 
+  if( !string )
+    return NULL;
+
   arraysize = 7; /* C,ST,L,O,OU,CN,email */
-  array = safe_malloc ((arraysize+1) * sizeof *array);
   arrayidx = 0;
+  array = xmalloc ((arraysize+1) * sizeof *array);
+  
+  
   while (*string)
     {
       while (*string == ' ')
@@ -2020,13 +1926,13 @@ parse_dn (const unsigned char *string)
           struct DnPair *a2;
 
           arraysize += 5;
-          a2 = safe_malloc ((arraysize+1) * sizeof *array);
+          a2 = xmalloc ((arraysize+1) * sizeof *array);
           for (i=0; i < arrayidx; i++)
             {
               a2[i].key = array[i].key;
               a2[i].value = array[i].value;
             }
-          safe_free ((void **)&array);
+          free (array);
           array = a2;
         }
       array[arrayidx].key = NULL;
@@ -2049,39 +1955,38 @@ parse_dn (const unsigned char *string)
  failure:
   for (i=0; i < arrayidx; i++)
     {
-      safe_free ((void**)&array[i].key);
-      safe_free ((void**)&array[i].value);
+      free (array[i].key);
+      free (array[i].value);
     }
-  safe_free ((void**)&array);
+  free (array);
   return NULL;
 }
 
-static int add_dn_part( char* result, struct DnPair* dn, const char* part )
+static int 
+add_dn_part( char* result, struct DnPair* dn, const char* part )
 {
   int any = 0;
 
-  for(; dn->key; ++dn ) {
-    if( !strcmp( dn->key, part ) ) {
-      if( any ) strcat( result, "+" );
-      /* email hack */
-      if( !strcmp( part, "1.2.840.113549.1.9.1" ) ) strcat( result, "EMail" );
-      else strcat( result, part );
-      strcat( result, "=" );
-      strcat( result, dn->value );
-      any = 1;
+  if( dn ) {
+    for(; dn->key; ++dn ) {
+      if( !strcmp( dn->key, part ) ) {
+        if( any ) strcat( result, "+" );
+        /* email hack */
+        if( !strcmp( part, "1.2.840.113549.1.9.1" ) ) strcat( result, "EMail" );
+        else strcat( result, part );
+        strcat( result, "=" );
+        strcat( result, dn->value );
+        any = 1;
+      }
     }
   }
   return any;
 }
 
-static char* reorder_dn( struct DnPair *dn )
+static char* 
+reorder_dn( struct DnPair *dn )
 {
-  /*
-  const char* stdpart[] = {
-    "CN", "OU", "O", "STREET", "L", "ST", "C", NULL 
-  };
-  */
-  // note: The must parts are: CN, L, OU, O, C
+  /* note: The must parts are: CN, L, OU, O, C */
   const char* stdpart[] = {
     "CN", "S", "SN", "GN", "T", "UID",
           "MAIL", "EMAIL", "MOBILE", "TEL", "FAX", "STREET",
@@ -2093,12 +1998,14 @@ static char* reorder_dn( struct DnPair *dn )
   };
   int any=0, any2=0, len=0, i;
   char* result;
-  for( i = 0; dn[i].key; ++i ) {
-    len += strlen( dn[i].key );
-    len += strlen( dn[i].value );
-    len += 4; /* ',' and '=', and possibly "(" and ")" */
+  if( dn ) {
+    for( i = 0; dn[i].key; ++i ) {
+      len += strlen( dn[i].key );
+      len += strlen( dn[i].value );
+      len += 4; /* ',' and '=', and possibly "(" and ")" */
+    }
   }
-  result = (char*)safe_malloc( (len+1)*sizeof(char) );
+  result = xmalloc( (len+1)*sizeof(char) );
   *result = 0;
 
   /* add standard parts */
@@ -2109,200 +2016,743 @@ static char* reorder_dn( struct DnPair *dn )
     any = add_dn_part( result, dn, stdpart[i] );
   }
 
-  /* add remaining parts in no particular order */
-  for(; dn->key; ++dn ) {
-    for( i = 0; stdpart[i]; ++i ) {
-      if( !strcmp( dn->key, stdpart[i] ) ) {
-       break;
-      }
-    }
-    if( !stdpart[i] ) {
-      if( any ) strcat( result, "," );
-      if( !any2 ) strcat( result, "(");
-      any = add_dn_part( result, dn, dn->key );
-      any2 = 1;
+  /* add remaining parts in no particular order */
+  if( dn ) {
+    for(; dn->key; ++dn ) {
+      for( i = 0; stdpart[i]; ++i ) {
+        if( !strcmp( dn->key, stdpart[i] ) ) {
+          break;
+        }
+      }
+      if( !stdpart[i] ) {
+        if( any ) strcat( result, "," );
+        if( !any2 ) strcat( result, "(");
+        any = add_dn_part( result, dn, dn->key );
+        any2 = 1;
+      }
+    }
+  }
+  if( any2 ) strcat( result, ")");
+  return result;
+}
+
+struct CertIterator {
+  GpgmeCtx ctx;  
+  struct CertificateInfo info;
+};
+
+struct CertIterator* 
+startListCertificates( const char* pattern, int remote )
+{
+    GpgmeError err;
+    struct CertIterator* it;
+    const char* patterns[] = { pattern, NULL };
+    fprintf( stderr,  "startListCertificates( \"%s\", %d )\n", pattern, remote );
+
+    it = xmalloc( sizeof( struct CertIterator ) );
+
+    err = gpgme_new (&(it->ctx));
+    /*fprintf( stderr,  "2: gpgme returned %d\n", err );*/
+    if( err != GPGME_No_Error ) {
+      free( it );
+      return NULL;
+    }
+
+    gpgme_set_protocol (it->ctx, GPGME_PROTOCOL_CMS);
+    if( remote ) gpgme_set_keylist_mode ( it->ctx, GPGME_KEYLIST_MODE_EXTERN ); 
+    else gpgme_set_keylist_mode ( it->ctx, GPGME_KEYLIST_MODE_LOCAL );
+    err =  gpgme_op_keylist_ext_start ( it->ctx, patterns, 0, 0);
+    if( err != GPGME_No_Error ) {
+      fprintf( stderr,  "gpgme_op_keylist_ext_start returned %d", err );
+      endListCertificates( it );
+      return NULL;
+    }
+    memset( &(it->info), 0, sizeof( struct CertificateInfo ) );
+    return it;
+}
+
+/* free() each string in a char*[] and the array itself */
+static void 
+freeStringArray( char** c )
+{
+    char** _c = c;
+
+    while( c && *c ) {
+      /*fprintf( stderr, "freeing \"%s\"\n", *c );*/
+      free( *c );
+      ++c;
+    }
+    free( _c );
+}
+
+/* free all malloc'ed data in a struct CertificateInfo */
+static void 
+freeInfo( struct CertificateInfo* info )
+{
+  struct DnPair* a = info->dnarray;
+  assert( info );
+  freeStringArray( info->userid );
+  free( info->serial);
+  free( info->fingerprint );
+  free( info->issuer );
+  free( info->chainid );
+  free( info->caps );
+  while( a && a->key && a->value ) {
+    free (a->key);
+    free (a->value);
+    ++a;
+  }
+  free (info->dnarray);
+  memset( info, 0, sizeof( *info ) );
+}
+
+/* Format the fingerprint nicely. The caller should
+   free the returned value using free() */
+static char* make_fingerprint( const char* fpr )
+{
+  int len = strlen(fpr);
+  int i = 0;
+  char* result = xmalloc( (len + len/2 + 1)*sizeof(char) );
+
+  for(; *fpr; ++fpr, ++i ) {
+    if( i%3 == 2) {
+      result[i] = ':'; ++i;
+    }
+    result[i] = *fpr;
+  }
+  result[i] = 0;
+  return result;
+}
+
+int 
+nextCertificate( struct CertIterator* it, struct CertificateInfo** result )
+{
+  GpgmeError err;
+  GpgmeKey   key;
+  int retval = GPGME_No_Error;
+  assert( it );
+  fprintf( stderr,  "nextCertificates( %p, %p )\n", it, result );
+  err = gpgme_op_keylist_next ( it->ctx, &key);
+  if( err != GPGME_EOF ) {   
+    int idx;
+    const char* s;
+    unsigned long u;
+    char* names[MAX_GPGME_IDX+1];
+    struct DnPair *issuer_dn, *tmp_dn;
+    retval = err;
+    memset( names, 0, sizeof( names ) );
+    freeInfo( &(it->info) );
+
+    for( idx = 0; (s = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, 0, idx)) && idx < MAX_GPGME_IDX; 
+        ++idx ) {
+      names[idx] = xstrdup( s );
+    }
+    
+    it->info.userid = xmalloc( sizeof( char* ) * (idx+1) );
+    memset( it->info.userid, 0, sizeof( char* ) * (idx+1) );
+    it->info.dnarray = 0;
+    for( idx = 0; names[idx] != 0; ++idx ) {
+      struct DnPair* a = parse_dn( names[idx] ); 
+      if( idx == 0 ) {
+       it->info.userid[idx] = reorder_dn( a );
+       it->info.dnarray = a;
+       free (names[idx]);
+        names[idx] = NULL;
+      } else {
+       it->info.userid[idx] = names[idx];
+      }
+    }
+    it->info.userid[idx] = 0;
+
+    s = gpgme_key_get_string_attr (key, GPGME_ATTR_SERIAL, 0, 0); 
+    it->info.serial = s? xstrdup(s) : NULL;
+
+    s = gpgme_key_get_string_attr (key, GPGME_ATTR_FPR, 0, 0); 
+    it->info.fingerprint = make_fingerprint( s );
+
+    s = gpgme_key_get_string_attr (key, GPGME_ATTR_ISSUER, 0, 0); 
+    if( s ) {
+      issuer_dn = tmp_dn = parse_dn( s );     
+      /*it->info.issuer = xstrdup(s);*/
+      it->info.issuer = reorder_dn( issuer_dn );
+      while( tmp_dn->key ) {
+       free( tmp_dn->key );
+       free( tmp_dn->value );
+       ++tmp_dn;
+      }
+      free( issuer_dn );
+      issuer_dn = tmp_dn = NULL;
+    } else {
+      it->info.issuer = NULL;
+    }
+    s = gpgme_key_get_string_attr (key, GPGME_ATTR_CHAINID, 0, 0); 
+    it->info.chainid = s? xstrdup(s): NULL;
+
+    s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEY_CAPS, 0, 0); 
+    it->info.caps = s? xstrdup(s) : NULL;
+
+    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_CREATED, 0, 0); 
+    it->info.created = u;
+
+    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_EXPIRE, 0, 0); 
+    it->info.expire = u;
+
+    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_IS_SECRET, 0, 0); 
+    it->info.secret = u;
+
+    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_UID_INVALID, 0, 0); 
+    it->info.invalid = u;
+
+    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_KEY_EXPIRED, 0, 0); 
+    it->info.expired = u;
+
+    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_KEY_DISABLED, 0, 0); 
+    it->info.disabled = u;
+
+    gpgme_key_release (key);
+    /*return &(it->info);*/
+    *result =  &(it->info);
+  } else {
+    *result = NULL;
+  }
+  return retval;
+}
+
+int
+endListCertificates( struct CertIterator* it )
+{
+  char *s = gpgme_get_op_info (it->ctx, 0);
+  int truncated = s && strstr (s, "<truncated/>");
+  fprintf( stderr,  "endListCertificates( %p )\n", it );
+  if( s ) free( s );
+  assert(it);
+  freeInfo( &(it->info) );
+  gpgme_op_keylist_end(it->ctx);
+  gpgme_release (it->ctx);
+  free( it );
+  return truncated;
+}
+
+int
+importCertificateWithFPR( const char* fingerprint, char** additional_info )
+{
+  GpgmeError err;
+  GpgmeCtx  ctx;
+  GpgmeData keydata;
+  GpgmeRecipients recips;
+  char* buf;
+  const char* tmp1;
+  char* tmp2;
+
+  err = gpgme_new( &ctx );
+  /*fprintf( stderr,  "2: gpgme returned %d\n", err );*/
+  if( err != GPGME_No_Error ) {
+    return err;
+  }
+  gpgme_set_protocol( ctx, GPGME_PROTOCOL_CMS );
+  gpgme_set_keylist_mode( ctx, GPGME_KEYLIST_MODE_LOCAL );
+
+  err = gpgme_data_new( &keydata );
+  if( err ) {
+    fprintf( stderr,  "gpgme_data_new returned %d\n", err );
+    gpgme_release( ctx );
+    return err;
+  }
+
+  err = gpgme_recipients_new( &recips );
+  if( err ) {
+    fprintf( stderr,  "gpgme_recipients_new returned %d\n", err );
+    gpgme_data_release( keydata );
+    gpgme_release( ctx );
+    return err;
+  }
+  
+  buf = malloc( sizeof(char)*( strlen( fingerprint ) + 1 ) );
+  if( !buf ) {
+    gpgme_recipients_release( recips );
+    gpgme_data_release( keydata );    
+    gpgme_release( ctx );
+    return GPGME_Out_Of_Core;
+  }
+  tmp1 = fingerprint;
+  tmp2 = buf;
+  while( *tmp1 ) {
+    if( *tmp1 != ':' ) *tmp2++ = *tmp1;
+    tmp1++;
+  }
+  *tmp2 = 0;
+  fprintf( stderr,  "calling gpgme_recipients_add_name( %s )\n", buf );  
+  err = gpgme_recipients_add_name( recips, buf ); 
+  if( err ) {
+    fprintf( stderr,  "gpgme_recipients_add_name returned %d\n", err );
+    free (buf);
+    gpgme_recipients_release( recips );
+    gpgme_data_release( keydata );    
+    gpgme_release( ctx );
+    return err;
+  }
+
+  err = gpgme_op_export( ctx, recips, keydata );
+  if( err ) {
+    fprintf( stderr,  "gpgme_op_export returned %d\n", err );
+    free (buf);
+    *additional_info = gpgme_get_op_info( ctx, 0 );
+    gpgme_recipients_release( recips );
+    gpgme_data_release( keydata );    
+    gpgme_release( ctx );
+    return err;
+  }
+  free (buf);
+  buf = NULL;
+
+  err = gpgme_op_import( ctx, keydata );
+  *additional_info = gpgme_get_op_info( ctx, 0 );
+  if( err ) {    
+    fprintf( stderr,  "gpgme_op_import returned %d\n", err );
+    gpgme_recipients_release( recips );
+    gpgme_data_release( keydata );
+    gpgme_release( ctx );
+    return err;
+  }
+
+  gpgme_recipients_release( recips );
+  gpgme_data_release( keydata );    
+  gpgme_release( ctx );
+  return 0;
+}
+int
+importCertificateFromMem( const char* data, size_t length , char** additional_info )
+{
+  GpgmeError err;
+  GpgmeCtx  ctx;
+  GpgmeData keydata;
+
+  err = gpgme_new( &ctx );
+  /*fprintf( stderr,  "2: gpgme returned %d\n", err );*/
+  if( err != GPGME_No_Error ) {
+    return err;
+  }
+  gpgme_set_protocol( ctx, GPGME_PROTOCOL_CMS );
+  gpgme_set_keylist_mode( ctx, GPGME_KEYLIST_MODE_LOCAL );
+
+  err = gpgme_data_new_from_mem( &keydata, data, length, 0 );
+  if( err ) {
+    fprintf( stderr,  "gpgme_data_new returned %d\n", err );
+    gpgme_release( ctx );
+    return err;
+  }
+
+  err = gpgme_op_import( ctx, keydata );
+  *additional_info = gpgme_get_op_info( ctx, 0 );
+  if( err ) {    
+    fprintf( stderr,  "gpgme_op_import returned %d\n", err );
+    gpgme_data_release( keydata );    
+    gpgme_release( ctx );
+    return err;
+  }
+
+  gpgme_data_release( keydata );    
+  gpgme_release( ctx );
+  return 0;
+}
+
+/*  == == == == == == == == == == == == == == == == == == == == == == == == ==
+   ==                                                                      ==
+  ==         Continuation of CryptPlug code                               ==
+ ==                                                                      ==
+== == == == == == == == == == == == == == == == == == == == == == == == ==  */
+
+
+/*
+  Find all certificate for a given addressee and return them in a
+  '\1' separated list.
+  NOTE: The certificate parameter must point to a not-yet allocated
+        char*.  The function will allocate the memory needed and
+        return the size in newSize.
+  If secretOnly is true, only secret keys are returned.
+*/
+bool findCertificates( const char* addressee,
+                       char** certificates,
+                       int* newSize,
+                       bool secretOnly )
+{
+#define MAXCERTS 1024
+  /* use const char declarations since all of them are needed twice */
+  const char* delimiter = "\1";
+  const char* openBracket = "    (";
+  const char* closeBracket = ")";
+
+  GpgmeCtx ctx;
+  GpgmeError err;
+  GpgmeKey rKey;
+  const char *s;
+  const char *s2;
+  char* dn;
+  struct DnPair* a;
+  int nFound = 0;
+  int iFound = 0;
+  int siz = 0;
+  char* DNs[MAXCERTS];
+  char* FPRs[MAXCERTS];
+  
+  if( ! certificates ){
+    fprintf( stderr, "gpgme: findCertificates called with invalid *certificates pointer\n" );
+    return false;
+  }
+
+  if( ! newSize ){
+    fprintf( stderr, "gpgme: findCertificates called with invalid newSize pointer\n" );
+    return false;
+  }
+
+  *certificates = 0;
+  *newSize = 0;
+  
+  /* calculate length of buffer needed for certs plus fingerprints */
+  gpgme_new (&ctx);
+  gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
+  err = gpgme_op_keylist_start(ctx, addressee, secretOnly ? 1 : 0);
+  while( GPGME_No_Error == err ) {
+    err = gpgme_op_keylist_next(ctx, &rKey);
+    if( GPGME_No_Error == err ) {
+      s = gpgme_key_get_string_attr (rKey, GPGME_ATTR_USERID, NULL, 0);
+      if( s ) {
+        dn = xstrdup( s );
+        s2 = gpgme_key_get_string_attr (rKey, GPGME_ATTR_FPR, NULL, 0);
+        if( s2 ) {
+          if( nFound )
+            siz += strlen( delimiter );
+          a = parse_dn( dn );
+          free( dn );
+          dn = reorder_dn( a );
+          siz += strlen( dn );
+          siz += strlen( openBracket );
+          siz += strlen( s2 );
+          siz += strlen( closeBracket );
+          DNs[ nFound ] = dn;
+          dn = NULL; /* prevent it from being free'ed below. */
+          FPRs[nFound ] = xstrdup( s2 );
+          ++nFound;
+          if( nFound >= MAXCERTS ) {
+            fprintf( stderr,
+                     "gpgme: findCertificates found too many certificates (%d)\n",
+                     MAXCERTS );
+            break;
+          }
+        }
+        free (dn); 
+      }
+    }
+  }
+  gpgme_op_keylist_end( ctx );
+  gpgme_release (ctx);
+  
+  
+  if( 0 < siz ) {
+    /* add one for trailing ZERO char */
+    ++siz;
+    *newSize = siz;
+    /* allocate the buffer */
+    *certificates = xmalloc( sizeof(char) * siz );
+    memset( *certificates, 0, sizeof(char) * siz );
+    /* fill the buffer */
+    for (iFound=0; iFound < nFound; iFound++) {
+      if( !iFound )
+        strcpy(*certificates, DNs[iFound] );
+      else {
+        strcat(*certificates, delimiter );
+        strcat(*certificates, DNs[iFound] );
+      }
+      strcat(  *certificates, openBracket );
+      strcat(  *certificates, FPRs[iFound] );
+      strcat(  *certificates, closeBracket );
+      free( DNs[ iFound ] );
+      free( FPRs[iFound ] );
+    }
+  }
+    
+  return ( 0 < nFound );
+}
+
+
+static const char*
+sig_status_to_string( GpgmeSigStat status )
+{
+  const char *result;
+
+  switch (status) {
+    case GPGME_SIG_STAT_NONE:
+      result = "Oops: Signature not verified";
+      break;
+    case GPGME_SIG_STAT_NOSIG:
+      result = "No signature found";
+      break;
+    case GPGME_SIG_STAT_GOOD:
+      result = "Good signature";
+      break;
+    case GPGME_SIG_STAT_BAD:
+      result = "BAD signature";
+      break;
+    case GPGME_SIG_STAT_NOKEY:
+      result = "No public key to verify the signature";
+      break;
+    case GPGME_SIG_STAT_ERROR:
+      result = "Error verifying the signature";
+      break;
+    case GPGME_SIG_STAT_DIFF:
+      result = "Different results for signatures";
+      break;
+    default:
+      result = "Error: Unknown status";
+      break;
+  }
+
+  return result;
+}
+
+
+bool checkMessageSignature( char** cleartext,
+                            const char* signaturetext,
+                            bool signatureIsBinary,
+                            int signatureLen,
+                            struct SignatureMetaData* sigmeta )
+{
+  GpgmeCtx ctx;
+  GpgmeSigStat status;
+  unsigned long sumGPGME;
+  SigStatusFlags sumPlug;
+  GpgmeData datapart, sigpart;
+  char* rClear = 0;
+  size_t clearLen;
+  GpgmeError err;
+  GpgmeKey key;
+  time_t created;
+  struct DnPair* a;
+  int sig_idx=0;
+  int UID_idx=0;
+  const char* statusStr;
+  const char* fpr;
+  bool isOpaqueSigned;
+  
+  if( !cleartext ) {
+    if( sigmeta )
+      storeNewCharPtr( &sigmeta->status,
+                        __GPGMEPLUG_ERROR_CLEARTEXT_IS_ZERO );
+
+    return false;
+  }
+
+  isOpaqueSigned = !*cleartext;
+
+  gpgme_new( &ctx );
+  gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
+  gpgme_set_armor (ctx,    signatureIsBinary ? 0 : 1);
+  /*  gpgme_set_textmode (ctx, signatureIsBinary ? 0 : 1); */
+
+  if( isOpaqueSigned )
+    gpgme_data_new( &datapart );
+  else
+    gpgme_data_new_from_mem( &datapart, *cleartext,
+                             strlen( *cleartext ), 1 );
+
+  gpgme_data_new_from_mem( &sigpart,
+                           signaturetext,
+                           signatureIsBinary
+                           ? signatureLen
+                           : strlen( signaturetext ),
+                           1 );
+
+  gpgme_op_verify( ctx, sigpart, datapart, &status );
+
+  if( isOpaqueSigned ) {
+    rClear = gpgme_data_release_and_get_mem( datapart, &clearLen );
+    *cleartext = malloc( clearLen + 1 );
+    if( *cleartext ) {
+      if( clearLen )
+        strncpy(*cleartext, rClear, clearLen );
+      (*cleartext)[clearLen] = '\0';
     }
+    free( rClear );
   }
-  if( any2 ) strcat( result, ")");
-  return result;
-}
+  else
+    gpgme_data_release( datapart );
 
-struct CertIterator {
-  GpgmeCtx ctx;  
-  struct CertificateInfo info;
-};
+  gpgme_data_release( sigpart );
 
-struct CertIterator* startListCertificates( const char* pattern, int remote )
-{
-    GpgmeError err;
-    struct CertIterator* it;
-    /*fprintf( stderr,  "startListCertificates()" );*/
+  /* Provide information in the sigmeta struct */
+  /* the status string */
+  statusStr = sig_status_to_string( status );
+  sigmeta->status = malloc( strlen( statusStr ) + 1 );
+  if( sigmeta->status ) {
+    strcpy( sigmeta->status, statusStr );
+    sigmeta->status[strlen( statusStr )] = '\0';
+  } else
+    ; /* nothing to do, is already 0 */
 
-    it = (struct CertIterator*)safe_malloc( sizeof( struct CertIterator ) );
+  /* Extended information for any number of signatures. */
+  fpr = gpgme_get_sig_status( ctx, sig_idx, &status, &created );
+  sigmeta->extended_info = 0;
+  while( fpr != NULL ) {
+    struct tm* ctime_val;
+    const char* sig_status;
 
-    err = gpgme_new (&(it->ctx));
-    /*fprintf( stderr,  "2: gpgme returned %d\n", err );*/
-    if( err != GPGME_No_Error ) {
-      free( it );
-      return NULL;
-    }
+    void* alloc_return = realloc( sigmeta->extended_info,
+                                  sizeof( struct SignatureMetaDataExtendedInfo )
+                                  * ( sig_idx + 1 ) );
+    if( alloc_return ) {
+      sigmeta->extended_info = alloc_return;
 
-    gpgme_set_protocol (it->ctx, GPGME_PROTOCOL_CMS);
-    if( remote ) gpgme_set_keylist_mode ( it->ctx, GPGME_KEYLIST_MODE_EXTERN ); 
-    else gpgme_set_keylist_mode ( it->ctx, GPGME_KEYLIST_MODE_LOCAL );
-    err =  gpgme_op_keylist_start ( it->ctx, pattern, 0);
-    if( err != GPGME_No_Error ) {
-      endListCertificates( it );
-      return NULL;
-    }
-    memset( &(it->info), 0, sizeof( struct CertificateInfo ) );
-    return it;
-}
+      /* clear the data area */
+      memset( &sigmeta->extended_info[sig_idx], 
+              0,
+              sizeof (struct SignatureMetaDataExtendedInfo) );
 
-/* free() each string in a char*[] and the array itself */
-static void freeStringArray( char** c )
-{
-    char** _c = c;
-    while( c && *c ) {
-      /*fprintf( stderr, "freeing \"%s\"\n", *c );*/
-      safe_free( (void**)&(*c) );
-      ++c;
-    }
-    safe_free( (void**)&_c );
-}
+      /* the creation time */
+      sigmeta->extended_info[sig_idx].creation_time = malloc( sizeof( struct tm ) );
+      if( sigmeta->extended_info[sig_idx].creation_time ) {
+        ctime_val = localtime( &created );
+        memcpy( sigmeta->extended_info[sig_idx].creation_time,
+                ctime_val, sizeof( struct tm ) );
+      }
 
-/* free all malloc'ed data in a struct CertificateInfo */
-static void freeInfo( struct CertificateInfo* info )
-{
-  struct DnPair* a = info->dnarray;
-  assert( info );
-  if( info->userid ) freeStringArray( info->userid );
-  if( info->serial ) safe_free( (void**)&(info->serial) );
-  if( info->fingerprint ) safe_free( (void**)&(info->fingerprint) );
-  if( info->issuer ) safe_free( (void**)&(info->issuer) );
-  if( info->chainid ) safe_free( (void**)&(info->chainid) );
-  if( info->caps ) safe_free( (void**)&(info->caps) );
-  while( a && a->key && a->value ) {
-    safe_free ((void**)&(a->key));
-    safe_free ((void**)&(a->value));
-    ++a;
-  }
-  if( info->dnarray ) safe_free ((void**)&(info->dnarray));
-  memset( info, 0, sizeof( *info ) );
-}
+      /* the extended signature verification status */
+      sumGPGME = gpgme_get_sig_ulong_attr( ctx,
+                                           sig_idx,
+                                           GPGME_ATTR_SIG_SUMMARY,
+                                           0 );
+      fprintf( stderr, "gpgmeplug checkMessageSignature status flags: %lX\n", sumGPGME );
+      /* translate GPGME status flags to common CryptPlug status flags */
+      sumPlug = 0;
+      if( sumGPGME & GPGME_SIGSUM_VALID       ) sumPlug |= SigStat_VALID      ;
+      if( sumGPGME & GPGME_SIGSUM_GREEN       ) sumPlug |= SigStat_GREEN      ;
+      if( sumGPGME & GPGME_SIGSUM_RED         ) sumPlug |= SigStat_RED        ;
+      if( sumGPGME & GPGME_SIGSUM_KEY_REVOKED ) sumPlug |= SigStat_KEY_REVOKED;
+      if( sumGPGME & GPGME_SIGSUM_KEY_EXPIRED ) sumPlug |= SigStat_KEY_EXPIRED;
+      if( sumGPGME & GPGME_SIGSUM_SIG_EXPIRED ) sumPlug |= SigStat_SIG_EXPIRED;
+      if( sumGPGME & GPGME_SIGSUM_KEY_MISSING ) sumPlug |= SigStat_KEY_MISSING;
+      if( sumGPGME & GPGME_SIGSUM_CRL_MISSING ) sumPlug |= SigStat_CRL_MISSING;
+      if( sumGPGME & GPGME_SIGSUM_CRL_TOO_OLD ) sumPlug |= SigStat_CRL_TOO_OLD;
+      if( sumGPGME & GPGME_SIGSUM_BAD_POLICY  ) sumPlug |= SigStat_BAD_POLICY ;
+      if( sumGPGME & GPGME_SIGSUM_SYS_ERROR   ) sumPlug |= SigStat_SYS_ERROR  ;
+      if( !sumPlug )
+        sumPlug = SigStat_NUMERICAL_CODE | sumGPGME;
+      sigmeta->extended_info[sig_idx].sigStatusFlags = sumPlug;
 
-/* Format the fingerprint nicely. The caller should
-   free the returned value with safe_free() */
-static char* make_fingerprint( const char* fpr )
-{
-  int len = strlen(fpr);
-  int i = 0;
-  char* result = safe_malloc( (len + len/2 + 1)*sizeof(char) );
-  if( !result ) return NULL;
-  for(; *fpr; ++fpr, ++i ) {
-    if( i%3 == 2) {
-      result[i] = ':'; ++i;
-    }
-    result[i] = *fpr;
-  }
-  result[i] = 0;
-  return result;
-}
+      sigmeta->extended_info[sig_idx].validity = GPGME_VALIDITY_UNKNOWN;
 
-int nextCertificate( struct CertIterator* it, struct CertificateInfo** result )
-{
-  GpgmeError err;
-  GpgmeKey   key;
-  int retval = GPGME_No_Error;
-  assert( it );
-  err = gpgme_op_keylist_next ( it->ctx, &key);
-  if( err != GPGME_EOF ) {   
-    int idx;
-    const char* s;
-    unsigned long u;
-    char* names[MAX_GPGME_IDX+1];
-    struct DnPair *issuer_dn, *tmp_dn;
-    retval = err;
-    memset( names, 0, sizeof( names ) );
-    freeInfo( &(it->info) );
+      err = gpgme_get_sig_key (ctx, sig_idx, &key);
 
-    for( idx = 0; (s = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, 0, idx)) && idx < MAX_GPGME_IDX; 
-        ++idx ) {
-      names[idx] = xstrdup( s );
-    }
-    
-    it->info.userid = safe_malloc( sizeof( char* ) * (idx+1) );
-    memset( it->info.userid, 0, sizeof( char* ) * (idx+1) );
-    it->info.dnarray = 0;
-    for( idx = 0; names[idx] != 0; ++idx ) {
-      struct DnPair* a = parse_dn( names[idx] ); 
-      if( idx == 0 ) {
-       it->info.userid[idx] = reorder_dn( a );
-       it->info.dnarray = a;
-       safe_free( (void **)&(names[idx]) );
-      } else {
-       it->info.userid[idx] = names[idx];
-      }
-    }
-    it->info.userid[idx] = 0;
+      if ( err == GPGME_No_Error) {
+        const char* attr_string;
+        unsigned long attr_ulong;
 
-    s = gpgme_key_get_string_attr (key, GPGME_ATTR_SERIAL, 0, 0); 
-    it->info.serial = xstrdup(s);
+        /* extract key identidy */
+        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_KEYID, 0, 0);
+        if (attr_string != 0)
+            storeNewCharPtr( &sigmeta->extended_info[sig_idx].keyid, attr_string );
 
-    s = gpgme_key_get_string_attr (key, GPGME_ATTR_FPR, 0, 0); 
-    it->info.fingerprint = make_fingerprint( s );
+        /* extract finger print */
+        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_FPR, 0, 0);
+        if (attr_string != 0)
+            storeNewCharPtr( &sigmeta->extended_info[sig_idx].fingerprint,
+                            attr_string );
 
-    s = gpgme_key_get_string_attr (key, GPGME_ATTR_ISSUER, 0, 0); 
-    if( s ) {
-      issuer_dn = tmp_dn = parse_dn( s );     
-      /*it->info.issuer = xstrdup(s);*/
-      it->info.issuer = reorder_dn( issuer_dn );
-      while( tmp_dn->key ) {
-       safe_free( (void**)&issuer_dn->key );
-       safe_free( (void**)&issuer_dn->value );
-       ++tmp_dn;
-      }
-      safe_free( (void**)&issuer_dn );
-    } else {
-      it->info.issuer = NULL;
-    }
-    s = gpgme_key_get_string_attr (key, GPGME_ATTR_CHAINID, 0, 0); 
-    it->info.chainid = xstrdup(s);
+        /* algorithms useable with this key */
+        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_ALGO, 0, 0);
+        if (attr_string != 0)
+            storeNewCharPtr( &sigmeta->extended_info[sig_idx].algo,
+                            attr_string );
+        attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_ALGO, 0, 0);
+        sigmeta->extended_info[sig_idx].algo_num = attr_ulong;
 
-    s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEY_CAPS, 0, 0); 
-    it->info.caps = xstrdup(s);
+        /* extract key validity */
+        attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_VALIDITY, 0, 0);
+        sigmeta->extended_info[sig_idx].validity = attr_ulong;
 
-    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_CREATED, 0, 0); 
-    it->info.created = u;
+        /* extract user id, according to the documentation it's representable
+        * as a number, but it seems that it also has a string representation
+        */
+        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_USERID, 0, 0);
+        if (attr_string != 0) {
+            a = parse_dn( attr_string );
+            sigmeta->extended_info[sig_idx].userid = reorder_dn( a );
+        }
+        
+        attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_USERID, 0, 0);
+        sigmeta->extended_info[sig_idx].userid_num = attr_ulong;
 
-    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_EXPIRE, 0, 0); 
-    it->info.expire = u;
+        /* extract the length */
+          sigmeta->extended_info[sig_idx].keylen = attr_ulong;
 
-    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_IS_SECRET, 0, 0); 
-    it->info.secret = u;
+        /* extract the creation time of the key */
+        attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_CREATED, 0, 0);
+        sigmeta->extended_info[sig_idx].key_created = attr_ulong;
 
-    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_UID_INVALID, 0, 0); 
-    it->info.invalid = u;
+        /* extract the expiration time of the key */
+        attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_EXPIRE, 0, 0);
+        sigmeta->extended_info[sig_idx].key_expires = attr_ulong;
 
-    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_KEY_EXPIRED, 0, 0); 
-    it->info.expired = u;
+        /* extract user name */
+        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_NAME, 0, 0);
+        if (attr_string != 0) {
+            a = parse_dn( attr_string );
+            sigmeta->extended_info[sig_idx].name = reorder_dn( a );
+        }
 
-    u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_KEY_DISABLED, 0, 0); 
-    it->info.disabled = u;
+        /* extract email(s) */
+        sigmeta->extended_info[sig_idx].emailCount = 0;
+        sigmeta->extended_info[sig_idx].emailList = 0;
+        for( UID_idx=0; 
+             (attr_string = gpgme_key_get_string_attr(key,
+                              GPGME_ATTR_EMAIL, 0, UID_idx)); 
+             ++UID_idx ){
+          if (*attr_string) {
+            fprintf( stderr, "gpgmeplug checkMessageSignature found email: %s\n", attr_string );
+            if( !sigmeta->extended_info[sig_idx].emailCount )
+                alloc_return = 
+                    malloc( sizeof( char*) );
+            else
+                alloc_return = 
+                    realloc( sigmeta->extended_info[sig_idx].emailList,
+                             sizeof( char*)
+                             * (sigmeta->extended_info[sig_idx].emailCount + 1) );
+            if( alloc_return ) {
+              sigmeta->extended_info[sig_idx].emailList = alloc_return;
+              storeNewCharPtr( 
+                  &( sigmeta->extended_info[sig_idx].emailList[
+                          sigmeta->extended_info[sig_idx].emailCount ] ),
+                  attr_string );
+              ++sigmeta->extended_info[sig_idx].emailCount;
+            }
+          }
+        }
+        if( !sigmeta->extended_info[sig_idx].emailCount )
+          fprintf( stderr, "gpgmeplug checkMessageSignature found NO EMAIL\n" );
 
-    gpgme_key_release (key);
-    /*return &(it->info);*/
-    *result =  &(it->info);
-  } else *result = NULL;
-  return retval;
-}
+        /* extract the comment */
+        attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_COMMENT, 0, 0);
+        if (attr_string != 0)
+            storeNewCharPtr( &sigmeta->extended_info[sig_idx].comment,
+                            attr_string );
+      }
+      else
+        storeNewCharPtr( &sigmeta->extended_info[sig_idx].fingerprint, fpr );
 
-void endListCertificates( struct CertIterator* it )
-{
-  /*fprintf( stderr,  "endListCertificates()\n" );*/
-  assert(it);
-  freeInfo( &(it->info) );
-  gpgme_op_keylist_end(it->ctx);
-  gpgme_release (it->ctx);
-  free( it );
+      sig_status = sig_status_to_string( status );
+      storeNewCharPtr( &sigmeta->extended_info[sig_idx].status_text,
+                       sig_status );
+
+    } else
+      break; /* if allocation fails once, it isn't likely to
+                succeed the next time either */
+
+    fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
+  }
+  sigmeta->extended_info_count = sig_idx;
+  sigmeta->nota_xml = gpgme_get_notation( ctx );
+  sigmeta->status_code = status;
+
+  gpgme_release( ctx );
+  return ( status == GPGME_SIG_STAT_GOOD );
 }
+