Bugfix: enable function receiverCertificateDaysLeftToExpiry().
[gpgme.git] / gpgmeplug / gpgmeplug.c
1 /* -*- Mode: C -*-
2
3   $Id$
4
5   GPGMEPLUG - an GPGME based cryptography plug-in following
6               the common CRYPTPLUG specification.
7
8   Copyright (C) 2001 by Klarälvdalens Datakonsult AB
9
10   GPGMEPLUG is free software; you can redistribute it and/or modify
11   it under the terms of GNU General Public License as published by
12   the Free Software Foundation; version 2 of the License.
13
14   GPGMEPLUG is distributed in the hope that it will be useful,
15   it under the terms of GNU General Public License as published by
16   the Free Software Foundation; version 2 of the License
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   GNU General Public License for more details.
20
21   You should have received a copy of the GNU General Public License
22   along with this program; if not, write to the Free Software
23   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
24 */
25
26
27
28 /*! \file gpgmeplug.c
29     \brief GPGME implementation of CRYPTPLUG following the
30     specification located in common API header cryptplug.h.
31
32     CRYPTPLUG is an independent cryptography plug-in API
33     developed for Sphinx-enabeling KMail and Mutt.
34
35     CRYPTPLUG was designed for the Aegypten project, but it may
36     be used by 3rd party developers as well to design pluggable
37     crypto backends for the above mentioned MUAs.
38
39     \note All string parameters appearing in this API are to be
40     interpreted as UTF-8 encoded.
41
42     \see cryptplug.h
43 */
44
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif
48 #include <stdio.h>
49 #include <string.h>
50 #include <assert.h>
51 #include <errno.h>
52 #include <time.h>
53 #include <ctype.h>
54
55 #ifndef BUG_URL
56 #define BUG_URL "http:://www.gnupg.org/aegypten/"
57 #endif
58
59 #include "gpgme.h"
60 #ifndef GPGMEPLUG_PROTOCOL
61 #define GPGMEPLUG_PROTOCOL GPGME_PROTOCOL_OpenPGP
62 #endif
63
64 /* definitions for signing */
65 // 1. opaque signatures (only used for S/MIME)
66 #ifndef GPGMEPLUG_OPA_SIGN_MAKE_MIME_OBJECT
67 #define GPGMEPLUG_OPA_SIGN_INCLUDE_CLEARTEXT false
68 #define GPGMEPLUG_OPA_SIGN_MAKE_MIME_OBJECT  false
69 #define GPGMEPLUG_OPA_SIGN_MAKE_MULTI_MIME   false
70 #define GPGMEPLUG_OPA_SIGN_CTYPE_MAIN        ""
71 #define GPGMEPLUG_OPA_SIGN_CDISP_MAIN        ""
72 #define GPGMEPLUG_OPA_SIGN_CTENC_MAIN        ""
73 #define GPGMEPLUG_OPA_SIGN_CTYPE_VERSION     ""
74 #define GPGMEPLUG_OPA_SIGN_CDISP_VERSION     ""
75 #define GPGMEPLUG_OPA_SIGN_CTENC_VERSION     ""
76 #define GPGMEPLUG_OPA_SIGN_BTEXT_VERSION     ""
77 #define GPGMEPLUG_OPA_SIGN_CTYPE_CODE        ""
78 #define GPGMEPLUG_OPA_SIGN_CDISP_CODE        ""
79 #define GPGMEPLUG_OPA_SIGN_CTENC_CODE        ""
80 #define GPGMEPLUG_OPA_SIGN_FLAT_PREFIX       ""
81 #define GPGMEPLUG_OPA_SIGN_FLAT_SEPARATOR    ""
82 #define GPGMEPLUG_OPA_SIGN_FLAT_POSTFIX      ""
83 #endif
84 // 2. detached signatures (used for S/MIME and for OpenPGP)
85 #ifndef GPGMEPLUG_DET_SIGN_MAKE_MIME_OBJECT
86 #define GPGMEPLUG_DET_SIGN_INCLUDE_CLEARTEXT true
87 #define GPGMEPLUG_DET_SIGN_MAKE_MIME_OBJECT  true
88 #define GPGMEPLUG_DET_SIGN_MAKE_MULTI_MIME   true
89 #define GPGMEPLUG_DET_SIGN_CTYPE_MAIN        "multipart/signed;protocol=application/pgp-signature;micalg=pgp-sha1"
90 #define GPGMEPLUG_DET_SIGN_CDISP_MAIN        ""
91 #define GPGMEPLUG_DET_SIGN_CTENC_MAIN        ""
92 #define GPGMEPLUG_DET_SIGN_CTYPE_VERSION     ""
93 #define GPGMEPLUG_DET_SIGN_CDISP_VERSION     ""
94 #define GPGMEPLUG_DET_SIGN_CTENC_VERSION     ""
95 #define GPGMEPLUG_DET_SIGN_BTEXT_VERSION     ""
96 #define GPGMEPLUG_DET_SIGN_CTYPE_CODE        "application/pgp-signature"
97 #define GPGMEPLUG_DET_SIGN_CDISP_CODE        ""
98 #define GPGMEPLUG_DET_SIGN_CTENC_CODE        ""
99 #define GPGMEPLUG_DET_SIGN_FLAT_PREFIX       ""
100 #define GPGMEPLUG_DET_SIGN_FLAT_SEPARATOR    ""
101 #define GPGMEPLUG_DET_SIGN_FLAT_POSTFIX      ""
102 #endif
103 // 3. common definitions for opaque and detached signing
104 #ifndef __GPGMEPLUG_SIGNATURE_CODE_IS_BINARY
105 #define __GPGMEPLUG_SIGNATURE_CODE_IS_BINARY false
106 #endif
107
108 #define __GPGMEPLUG_ERROR_CLEARTEXT_IS_ZERO "Error: Cannot run checkMessageSignature() with cleartext == 0"
109
110 /* definitions for encoding */
111 #ifndef GPGMEPLUG_ENC_MAKE_MIME_OBJECT
112 #define GPGMEPLUG_ENC_INCLUDE_CLEARTEXT  false
113 #define GPGMEPLUG_ENC_MAKE_MIME_OBJECT   true
114 #define GPGMEPLUG_ENC_MAKE_MULTI_MIME    true
115 #define GPGMEPLUG_ENC_CTYPE_MAIN         "multipart/encrypted; protocol=application/pgp-encrypted"
116 #define GPGMEPLUG_ENC_CDISP_MAIN         ""
117 #define GPGMEPLUG_ENC_CTENC_MAIN         ""
118 #define GPGMEPLUG_ENC_CTYPE_VERSION      "application/pgp-encrypted"
119 #define GPGMEPLUG_ENC_CDISP_VERSION      "attachment"
120 #define GPGMEPLUG_ENC_CTENC_VERSION      ""
121 #define GPGMEPLUG_ENC_BTEXT_VERSION      "Version: 1"
122 #define GPGMEPLUG_ENC_CTYPE_CODE         "application/octet-stream"
123 #define GPGMEPLUG_ENC_CDISP_CODE         "inline; filename=\"msg.asc\""
124 #define GPGMEPLUG_ENC_CTENC_CODE         ""
125 #define GPGMEPLUG_ENC_FLAT_PREFIX        ""
126 #define GPGMEPLUG_ENC_FLAT_SEPARATOR     ""
127 #define GPGMEPLUG_ENC_FLAT_POSTFIX       ""
128 #define __GPGMEPLUG_ENCRYPTED_CODE_IS_BINARY false
129 #endif
130 /* Note: The following specification will result in
131        function encryptAndSignMessage() producing
132        _empty_ mails.
133        This must be changed as soon as our plugin
134        is supporting the encryptAndSignMessage() function. */
135 #ifndef GPGMEPLUG_ENCSIGN_MAKE_MIME_OBJECT
136 #define GPGMEPLUG_ENCSIGN_INCLUDE_CLEARTEXT false
137 #define GPGMEPLUG_ENCSIGN_MAKE_MIME_OBJECT  false
138 #define GPGMEPLUG_ENCSIGN_MAKE_MULTI_MIME   false
139 #define GPGMEPLUG_ENCSIGN_CTYPE_MAIN        ""
140 #define GPGMEPLUG_ENCSIGN_CDISP_MAIN        ""
141 #define GPGMEPLUG_ENCSIGN_CTENC_MAIN        ""
142 #define GPGMEPLUG_ENCSIGN_CTYPE_VERSION     ""
143 #define GPGMEPLUG_ENCSIGN_CDISP_VERSION     ""
144 #define GPGMEPLUG_ENCSIGN_CTENC_VERSION     ""
145 #define GPGMEPLUG_ENCSIGN_BTEXT_VERSION     ""
146 #define GPGMEPLUG_ENCSIGN_CTYPE_CODE        ""
147 #define GPGMEPLUG_ENCSIGN_CDISP_CODE        ""
148 #define GPGMEPLUG_ENCSIGN_CTENC_CODE        ""
149 #define GPGMEPLUG_ENCSIGN_FLAT_PREFIX       ""
150 #define GPGMEPLUG_ENCSIGN_FLAT_SEPARATOR    ""
151 #define GPGMEPLUG_ENCSIGN_FLAT_POSTFIX      ""
152 #endif
153
154 #include "cryptplug.h"
155
156
157 typedef struct {
158   const char*             bugURL;
159   const char*             signatureKeyCertificate;
160   SignatureAlgorithm      signatureAlgorithm;
161   SignatureCompoundMode   signatureCompoundMode;
162   SendCertificates        sendCertificates;
163   SignEmail               signEmail;
164   bool                    saveSentSignatures;
165   bool                    warnNoCertificate;
166   PinRequests             numPINRequests;
167   bool                    checkSignatureCertificatePathToRoot;
168   bool                    signatureUseCRLs;
169   EncryptionAlgorithm     encryptionAlgorithm;
170   EncryptEmail            encryptEmail;
171   bool                    saveMessagesEncrypted;
172   bool                    checkEncryptionCertificatePathToRoot;
173   bool                    encryptionUseCRLs;
174   bool                    encryptionCRLExpiryNearWarning;
175   int                     encryptionCRLNearExpiryInterval;
176   struct DirectoryServer *directoryServers;
177   unsigned int            numDirectoryServers;
178   CertificateSource       certificateSource;
179   CertificateSource       cRLSource;
180   bool                    warnSendUnsigned;
181   int                     numPINRequestsInterval;
182   bool                    signatureCertificateExpiryNearWarning;
183   int                     signatureCertificateExpiryNearInterval;
184   bool                    cACertificateExpiryNearWarning;
185   int                     cACertificateExpiryNearInterval;
186   bool                    rootCertificateExpiryNearWarning;
187   int                     rootCertificateExpiryNearInterval;
188   bool                    warnSendUnencrypted;
189   bool                    checkCertificatePath;
190   bool                    receiverCertificateExpiryNearWarning;
191   int                     receiverCertificateExpiryNearWarningInterval;
192   bool                    certificateInChainExpiryNearWarning;
193   int                     certificateInChainExpiryNearWarningInterval;
194   bool                    receiverEmailAddressNotInCertificateWarning;
195   const char* libVersion; // a statically allocated string with the GPGME Version used
196 } Config;
197
198
199 Config config;
200
201 #define NEAR_EXPIRY 14
202
203 bool initialize()
204 {
205   config.bugURL                               = malloc( strlen( BUG_URL ) + 1 );
206   strcpy( (char* )config.bugURL,                BUG_URL );
207   config.signatureKeyCertificate              = malloc( 1 );
208   strcpy( (char* )config.signatureKeyCertificate, "" );
209   config.signatureAlgorithm                   = SignAlg_SHA1;
210   if( GPGMEPLUG_PROTOCOL == GPGME_PROTOCOL_CMS )
211     config.signatureCompoundMode              = SignatureCompoundMode_Opaque;
212   else
213     config.signatureCompoundMode              = SignatureCompoundMode_Detached;
214   config.sendCertificates                     = SendCert_SendChainWithRoot;
215   config.signEmail                            = SignEmail_SignAll;
216   config.saveSentSignatures                   = true;
217   config.warnNoCertificate                    = true;
218   config.numPINRequests                       = PinRequest_Always;
219   config.checkSignatureCertificatePathToRoot  = true;
220   config.signatureUseCRLs                     = true;
221   config.encryptionAlgorithm                  = EncryptAlg_RSA;
222   config.encryptEmail                         = EncryptEmail_Ask;
223   config.saveMessagesEncrypted                = true;
224   config.checkEncryptionCertificatePathToRoot = true;
225   config.encryptionUseCRLs                    = true;
226   config.encryptionCRLExpiryNearWarning       = true;
227   config.encryptionCRLNearExpiryInterval      = NEAR_EXPIRY;
228   config.directoryServers                     = NULL;
229   config.numDirectoryServers                  = 0;
230   config.certificateSource                    = CertSrc_Server;
231   config.cRLSource                            = CertSrc_Server;
232   config.warnSendUnsigned                             = true;
233   config.numPINRequestsInterval                       = NEAR_EXPIRY;
234   config.signatureCertificateExpiryNearWarning        = true;
235   config.signatureCertificateExpiryNearInterval       = NEAR_EXPIRY;
236   config.cACertificateExpiryNearWarning               = true;
237   config.cACertificateExpiryNearInterval              = NEAR_EXPIRY;
238   config.rootCertificateExpiryNearWarning             = true;
239   config.rootCertificateExpiryNearInterval            = NEAR_EXPIRY;
240   config.warnSendUnencrypted                          = false;
241   config.checkCertificatePath                         = true;
242   config.receiverCertificateExpiryNearWarning         = true;
243   config.receiverCertificateExpiryNearWarningInterval = NEAR_EXPIRY;
244   config.certificateInChainExpiryNearWarning          = true;
245   config.certificateInChainExpiryNearWarningInterval  = NEAR_EXPIRY;
246   config.receiverEmailAddressNotInCertificateWarning  = true;
247   config.libVersion = gpgme_check_version (NULL);
248   return (gpgme_engine_check_version (GPGMEPLUG_PROTOCOL) == GPGME_No_Error);
249 };
250
251
252 void deinitialize()
253 {
254   unsigned int i;
255   for( i = 0; i < config.numDirectoryServers; ++i ) {
256     free( (char *)config.directoryServers[i].servername );
257     free( (char *)config.directoryServers[i].description );
258   }
259   free( config.directoryServers );
260 }
261
262
263 bool hasFeature( Feature flag )
264 {
265   /* our own plugins are supposed to support everything */
266   switch ( flag ) {
267   case Feature_SignMessages:              return true;
268   case Feature_VerifySignatures:          return true;
269   case Feature_EncryptMessages:           return true;
270   case Feature_DecryptMessages:           return true;
271   case Feature_SendCertificates:          return true;
272   case Feature_WarnSignCertificateExpiry: return true;
273   case Feature_WarnSignEmailNotInCertificate: return true;
274   case Feature_PinEntrySettings:          return true;
275   case Feature_StoreMessagesWithSigs:     return true;
276   case Feature_EncryptionCRLs:            return true;
277   case Feature_WarnEncryptCertificateExpiry: return true;
278   case Feature_WarnEncryptEmailNotInCertificate: return true;
279   case Feature_StoreMessagesEncrypted:    return true;
280   case Feature_CheckCertificatePath:      return true;
281   case Feature_CertificateDirectoryService: return false;
282   case Feature_CRLDirectoryService:       return false;
283   /* undefined or not yet implemented: */
284   case Feature_undef:                     return false;
285   default:                                      return false;
286   }
287 }
288
289
290 const char* libVersion(){ return config.libVersion; }
291
292
293 const char* bugURL(){ return config.bugURL; }
294
295
296 void unsafeStationery( void** pixmap, const char** menutext, char* accel,
297           const char** tooltip, const char** statusbartext ){}
298
299 void signedStationery( void** pixmap, const char** menutext, char* accel,
300           const char** tooltip, const char** statusbartext ){}
301
302 void encryptedStationery( void** pixmap, const char**
303           menutext, char* accel,
304           const char** tooltip, const char** statusbartext ){}
305
306 void signedEncryptedStationery( void** pixmap, const char**
307           menutext, char* accel,
308           const char** tooltip, const char** statusbartext ){}
309
310 const char* signatureConfigurationDialog(){ return 0; }
311
312 const char* signatureKeySelectionDialog(){ return 0; }
313
314 const char* signatureAlgorithmDialog(){ return 0; }
315
316 const char* signatureHandlingDialog(){ return 0; }
317
318 void setSignatureKeyCertificate( const char* certificate )
319 {
320   config.signatureKeyCertificate = certificate;
321 }
322
323 const char* signatureKeyCertificate()
324 {
325   return config.signatureKeyCertificate;
326 }
327
328 void setSignatureAlgorithm( SignatureAlgorithm sigAlg )
329 {
330   config.signatureAlgorithm = sigAlg;
331 }
332
333 SignatureAlgorithm signatureAlgorithm()
334 {
335   return config.signatureAlgorithm;
336 }
337
338 void setSignatureCompoundMode( SignatureCompoundMode signComp )
339 {
340   config.signatureCompoundMode = signComp;
341 }
342
343 SignatureCompoundMode signatureCompoundMode()
344 {
345   return config.signatureCompoundMode;
346 }
347
348 void setSendCertificates( SendCertificates sendCert )
349 {
350   config.sendCertificates = sendCert;
351 }
352
353 SendCertificates sendCertificates()
354 {
355   return config.sendCertificates;
356 }
357
358 void setSignEmail( SignEmail signMail )
359 {
360   config.signEmail = signMail;
361 }
362
363 SignEmail signEmail()
364 {
365   return config.signEmail;
366 }
367
368
369
370
371
372 void setWarnSendUnsigned( bool flag )
373 {
374   config.warnSendUnsigned = flag;
375 }
376
377 bool warnSendUnsigned()
378 {
379   return config.warnSendUnsigned;
380 }
381
382
383
384
385
386
387 void setSaveSentSignatures( bool flag )
388 {
389   config.saveSentSignatures = flag;
390 }
391
392 bool saveSentSignatures()
393 {
394   return config.saveSentSignatures;
395 }
396
397 void setWarnNoCertificate( bool flag )
398 {
399   config.warnNoCertificate = flag;
400 }
401
402 bool warnNoCertificate()
403 {
404   return config.warnNoCertificate;
405 }
406
407
408 bool isEmailInCertificate( const char* email, const char* certificate )
409 {
410     /* PENDING(g10) this function should return true if the email
411        address passed as the first parameter is contained in the
412        certificate passed as the second parameter, and false
413        otherwise. This is used to alert the user if his own email
414        address is not contained in the certificate he uses for
415        signing.
416        Note that the parameter email can be anything that is allowed
417        in a From: line.
418        Another note: OK, OK, we'll handle that in the MUA. You can
419        assume that you only get the email address.
420     */
421   return false; /* dummy*/
422 }
423
424
425 void setNumPINRequests( PinRequests reqMode )
426 {
427   config.numPINRequests = reqMode;
428
429   /* PENDING(g10) Put this value into gpg and make it ask for the pin
430      according to this. Note that there is also
431      setNumPINRequestsInterval() which is only used if reqMode ==
432      PinRequest_AfterMinutes.
433   */
434 }
435
436 PinRequests numPINRequests()
437 {
438   return config.numPINRequests;
439 }
440
441
442
443 void setNumPINRequestsInterval( int interval )
444 {
445   config.numPINRequestsInterval = interval;
446
447   /* PENDING(g10) Put this value into gpg and make it ask for the pin
448      according to this. Note that this should only be used if
449      config.numPINRequests (set with setNumPINRequests()) has the
450      value PinRequest_AfterMinutes.
451   */
452 }
453
454 int numPINRequestsInterval()
455 {
456   return config.numPINRequestsInterval;
457 }
458
459
460
461 void setCheckSignatureCertificatePathToRoot( bool flag )
462 {
463   config.checkSignatureCertificatePathToRoot = flag;
464 }
465
466 bool checkSignatureCertificatePathToRoot()
467 {
468   return config.checkSignatureCertificatePathToRoot;
469 }
470
471 void setSignatureUseCRLs( bool flag )
472 {
473   config.signatureUseCRLs = flag;
474 }
475
476 bool signatureUseCRLs()
477 {
478   return config.signatureUseCRLs;
479 }
480
481
482
483
484
485
486 void setSignatureCertificateExpiryNearWarning( bool flag )
487 {
488   config.signatureCertificateExpiryNearWarning = flag;
489 }
490
491 bool signatureCertificateExpiryNearWarning( void )
492 {
493   return config.signatureCertificateExpiryNearWarning;
494 }
495
496
497 int signatureCertificateDaysLeftToExpiry( const char* certificate )
498 {
499   GpgmeCtx ctx;
500   GpgmeError err;
501   GpgmeKey rKey;
502   time_t daysLeft = 0;
503
504   gpgme_new( &ctx );
505   gpgme_set_protocol( ctx, GPGMEPLUG_PROTOCOL );
506
507   err = gpgme_op_keylist_start( ctx, certificate, 0 );
508   if ( GPGME_No_Error == err ) {
509     err = gpgme_op_keylist_next( ctx, &rKey );
510     gpgme_op_keylist_end( ctx );
511     if ( GPGME_No_Error == err ) {
512       time_t expire_time = gpgme_key_get_ulong_attr(
513                              rKey,GPGME_ATTR_EXPIRE, NULL, 0 );
514       time_t cur_time = time (NULL);
515       daysLeft = expire_time - cur_time;
516       // convert seconds into days
517       daysLeft /= 43200;
518       gpgme_key_release( rKey );
519     }
520   }
521   gpgme_release( ctx );
522     
523   /* 
524   fprintf( stderr, "gpgmeplug signatureCertificateDaysLeftToExpiry returned %d\n", daysLeft );
525   */
526
527   return daysLeft;
528 }
529
530
531 void setSignatureCertificateExpiryNearInterval( int interval )
532 {
533   config.signatureCertificateExpiryNearInterval = interval;
534 }
535
536 int signatureCertificateExpiryNearInterval( void )
537 {
538   return config.signatureCertificateExpiryNearInterval;
539 }
540
541 void setCACertificateExpiryNearWarning( bool flag )
542 {
543   config.cACertificateExpiryNearWarning = flag;
544 }
545
546 bool caCertificateExpiryNearWarning( void )
547 {
548   return config.cACertificateExpiryNearWarning;
549 }
550
551 int caCertificateDaysLeftToExpiry( const char* certificate )
552 {
553     /* PENDING(g10)
554        Please return the number of days that are left until the
555        CA certificate for the certificate specified in the parameter
556        certificate expires.
557     */
558   /*
559   GpgmeCtx ctx;
560   GpgmeError err;
561   GpgmeKey rKey;
562   time_t daysLeft = 0;
563
564   gpgme_new( &ctx );
565   gpgme_set_protocol( ctx, GPGMEPLUG_PROTOCOL );
566
567   err = gpgme_op_keylist_start( ctx, certificate, 0 );
568   if ( GPGME_No_Error == err ) {
569     err = gpgme_op_keylist_next( ctx, &rKey );
570     gpgme_op_keylist_end( ctx );
571     if ( GPGME_No_Error == err ) {
572       time_t expire_time = gpgme_key_get_ulong_attr(
573                              rKey,
574                              
575 ??????????????????????? GPGME_ATTR_EXPIRE,  ???????????????????????
576                              
577                              NULL, 0 );
578       time_t cur_time = time (NULL);
579       daysLeft = expire_time - cur_time;
580       // convert seconds into days
581       daysLeft /= 43200;
582       gpgme_key_release( rKey );
583     }
584   }
585   gpgme_release( ctx );
586     
587    
588   // fprintf( stderr, "gpgmeplug caCertificateDaysLeftToExpiry returned %d\n", daysLeft );
589   return daysLeft;
590   */
591   
592   return 10; /* dummy that triggers a warning in the MUA */
593 }
594
595 void setCACertificateExpiryNearInterval( int interval )
596 {
597   config.cACertificateExpiryNearInterval = interval;
598 }
599
600 int caCertificateExpiryNearInterval( void )
601 {
602   return config.cACertificateExpiryNearInterval;
603 }
604
605 void setRootCertificateExpiryNearWarning( bool flag )
606 {
607   config.rootCertificateExpiryNearWarning = flag;
608 }
609
610 bool rootCertificateExpiryNearWarning( void )
611 {
612   return config.rootCertificateExpiryNearWarning;
613 }
614
615 int rootCertificateDaysLeftToExpiry( const char* certificate )
616 {
617     /* PENDING(g10)
618        Please return the number of days that are left until the
619        root certificate for the certificate specified in the parameter
620        certificate expires.
621     */
622   /*
623   GpgmeCtx ctx;
624   GpgmeError err;
625   GpgmeKey rKey;
626   time_t daysLeft = 0;
627
628   gpgme_new( &ctx );
629   gpgme_set_protocol( ctx, GPGMEPLUG_PROTOCOL );
630
631   err = gpgme_op_keylist_start( ctx, certificate, 0 );
632   if ( GPGME_No_Error == err ) {
633     err = gpgme_op_keylist_next( ctx, &rKey );
634     gpgme_op_keylist_end( ctx );
635     if ( GPGME_No_Error == err ) {
636       time_t expire_time = gpgme_key_get_ulong_attr(
637                              rKey,
638                              
639 ??????????????????????? GPGME_ATTR_EXPIRE,  ???????????????????????
640                              
641                              NULL, 0 );
642       time_t cur_time = time (NULL);
643       daysLeft = expire_time - cur_time;
644       // convert seconds into days
645       daysLeft /= 43200;
646       gpgme_key_release( rKey );
647     }
648   }
649   gpgme_release( ctx );
650     
651    
652   // fprintf( stderr, "gpgmeplug rootCertificateDaysLeftToExpiry returned %d\n", daysLeft );
653   return daysLeft;
654   */
655   
656   return 10; /* dummy that triggers a warning in the MUA */
657 }
658
659
660 void setRootCertificateExpiryNearInterval( int interval )
661 {
662   config.rootCertificateExpiryNearInterval = interval;
663 }
664
665 int rootCertificateExpiryNearInterval( void )
666 {
667   return config.rootCertificateExpiryNearInterval;
668 }
669
670
671
672
673
674
675
676
677 const char* encryptionConfigurationDialog(){ return 0; }
678
679 const char* encryptionAlgorithmDialog(){ return 0; }
680
681 const char* encryptionHandlingDialog(){ return 0; }
682
683 const char* encryptionReceiverDialog(){ return 0; }
684
685 void setEncryptionAlgorithm( EncryptionAlgorithm cryptAlg )
686 {
687   config.encryptionAlgorithm = cryptAlg;
688 }
689
690 EncryptionAlgorithm encryptionAlgorithm()
691 {
692   return config.encryptionAlgorithm;
693 }
694
695 void setEncryptEmail( EncryptEmail cryptMode )
696 {
697   config.encryptEmail = cryptMode;
698 }
699
700 EncryptEmail encryptEmail()
701 {
702   return config.encryptEmail;
703 }
704
705
706
707
708
709
710 void setWarnSendUnencrypted( bool flag )
711 {
712   config.warnSendUnencrypted = flag;
713 }
714
715 bool warnSendUnencrypted()
716 {
717   return config.warnSendUnencrypted;
718 }
719
720
721
722
723
724
725
726
727
728 void setSaveMessagesEncrypted( bool flag )
729 {
730   config.saveMessagesEncrypted = flag;
731 }
732
733 bool saveMessagesEncrypted()
734 {
735   return config.saveMessagesEncrypted;
736 }
737
738
739
740
741
742
743
744 void setCheckCertificatePath( bool flag )
745 {
746   config.checkCertificatePath = flag;
747 }
748
749 bool checkCertificatePath()
750 {
751   return config.checkCertificatePath;
752 }
753
754
755
756
757
758
759
760
761 void setCheckEncryptionCertificatePathToRoot( bool flag )
762 {
763   config.checkEncryptionCertificatePathToRoot = flag;
764 }
765
766 bool checkEncryptionCertificatePathToRoot()
767 {
768   return config.checkEncryptionCertificatePathToRoot;
769 }
770
771
772
773
774
775
776
777 void setReceiverCertificateExpiryNearWarning( bool flag )
778 {
779   config.receiverCertificateExpiryNearWarning = flag;
780 }
781
782 bool receiverCertificateExpiryNearWarning()
783 {
784   return config.receiverCertificateExpiryNearWarning;
785 }
786
787
788 int receiverCertificateDaysLeftToExpiry( const char* certificate )
789 {
790   GpgmeCtx ctx;
791   GpgmeError err;
792   GpgmeKey rKey;
793   time_t daysLeft = 0;
794
795   gpgme_new( &ctx );
796   gpgme_set_protocol( ctx, GPGMEPLUG_PROTOCOL );
797
798   err = gpgme_op_keylist_start( ctx, certificate, 0 );
799   if ( GPGME_No_Error == err ) {
800     err = gpgme_op_keylist_next( ctx, &rKey );
801     gpgme_op_keylist_end( ctx );
802     if ( GPGME_No_Error == err ) {
803       time_t expire_time = gpgme_key_get_ulong_attr(
804                              rKey,GPGME_ATTR_EXPIRE, NULL, 0 );
805       time_t cur_time = time (NULL);
806       daysLeft = expire_time - cur_time;
807       // convert seconds into days
808       daysLeft /= 43200;
809       gpgme_key_release( rKey );
810     }
811   }
812   gpgme_release( ctx );
813     
814   /*
815   fprintf( stderr, "gpgmeplug receiverCertificateDaysLeftToExpiry returned %d\n", daysLeft );
816   */
817
818   return daysLeft;
819     
820     
821     
822     /* PENDING(g10)
823        Please return the number of days that are left until the
824        certificate specified in the parameter certificate expires.
825     */
826   return 10; /* dummy that triggers a warning in the MUA */
827 }
828
829
830 void setReceiverCertificateExpiryNearWarningInterval( int interval )
831 {
832   config.receiverCertificateExpiryNearWarningInterval = interval;
833 }
834
835 int receiverCertificateExpiryNearWarningInterval()
836 {
837   return config.receiverCertificateExpiryNearWarningInterval;
838 }
839
840 void setCertificateInChainExpiryNearWarning( bool flag )
841 {
842   config.certificateInChainExpiryNearWarning = flag;
843 }
844
845 bool certificateInChainExpiryNearWarning()
846 {
847   return config.certificateInChainExpiryNearWarning;
848 }
849
850
851 int certificateInChainDaysLeftToExpiry( const char* certificate )
852 {
853     /* PENDING(g10)
854        Please return the number of days that are left until the
855        the first certificate in the chain of the specified certificate
856        expires.
857     */
858   return 10; /* dummy that triggers a warning in the MUA */
859 }
860
861
862 void setCertificateInChainExpiryNearWarningInterval( int interval )
863 {
864   config.certificateInChainExpiryNearWarningInterval = interval;
865 }
866
867 int certificateInChainExpiryNearWarningInterval()
868 {
869   return config.certificateInChainExpiryNearWarningInterval;
870 }
871
872 void setReceiverEmailAddressNotInCertificateWarning( bool flag )
873 {
874   config.receiverEmailAddressNotInCertificateWarning = flag;
875 }
876
877 bool receiverEmailAddressNotInCertificateWarning()
878 {
879   return config.receiverEmailAddressNotInCertificateWarning;
880 }
881
882
883
884
885
886
887
888
889 void setEncryptionUseCRLs( bool flag )
890 {
891   config.encryptionUseCRLs = flag;
892
893   /* PENDING(g10) Store this setting in gpgme and use it. If true,
894      every certificate used for encryption should be checked against
895      applicable CRLs.
896   */
897 }
898
899 bool encryptionUseCRLs()
900 {
901   return config.encryptionUseCRLs;
902 }
903
904
905 int encryptionCRLsDaysLeftToExpiry()
906 {
907     /* PENDING(g10)
908        Please return the number of days that are left until the
909        CRL used for encryption expires.
910     */
911   return 10; /* dummy that triggers a warning in the MUA */
912 }
913
914 void setEncryptionCRLExpiryNearWarning( bool flag )
915 {
916   config.encryptionCRLExpiryNearWarning = flag;
917 }
918
919 bool encryptionCRLExpiryNearWarning()
920 {
921   return config.encryptionCRLExpiryNearWarning;
922 }
923
924 void setEncryptionCRLNearExpiryInterval( int interval )
925 {
926   config.encryptionCRLNearExpiryInterval = interval;
927 }
928
929 int encryptionCRLNearExpiryInterval()
930 {
931   return config.encryptionCRLNearExpiryInterval;
932 }
933
934
935 const char* directoryServiceConfigurationDialog(){ return 0; }
936
937 void appendDirectoryServer( const char* servername,
938                             int         port,
939                             const char* description )
940 {
941   struct DirectoryServer *newServers = NULL;
942   newServers = realloc( config.directoryServers,
943                         (1+config.numDirectoryServers) * sizeof *newServers );
944   if( newServers ) {
945     config.directoryServers = newServers;
946     newServers[ config.numDirectoryServers ].servername =
947       malloc( 1+strlen( servername ) );
948     if( newServers[ config.numDirectoryServers ].servername ) {
949       strcpy( (char *)newServers[ config.numDirectoryServers ].servername,
950         servername );
951       newServers[ config.numDirectoryServers ].description =
952         malloc( 1+strlen(  description ) );
953       if( newServers[ config.numDirectoryServers ].description ) {
954         strcpy( (char *)newServers[ config.numDirectoryServers ].description,
955           description );
956         newServers[ config.numDirectoryServers ].port = port;
957         config.numDirectoryServers += 1;
958       }
959     }
960   }
961 }
962
963 void setDirectoryServers( struct DirectoryServer server[], unsigned int size )
964 {
965   unsigned int i;
966   int oldSize = config.numDirectoryServers;
967   struct DirectoryServer *newServers = NULL;
968   newServers = calloc ( size, sizeof *newServers );
969   if( newServers ) {
970     for( i=0; i < oldSize; ++i ) {
971       free( (char *)config.directoryServers[i].servername );
972       free( (char *)config.directoryServers[i].description );
973     }
974     free( config.directoryServers );
975     for( i=0; i < size; ++i ) {
976       newServers[ i ].servername = malloc( 1+strlen( server[i].servername ) );
977       if( newServers[ i ].servername ) {
978         strcpy( (char *)newServers[ i ].servername, server[i].servername );
979         newServers[ i ].description = malloc( 1+strlen( server[i].description ) );
980         if( newServers[ i ].description ) {
981           strcpy( (char *)newServers[ i ].description, server[i].description );
982           newServers[ i ].port = server[i].port;
983         }
984       }
985     }
986     config.directoryServers = newServers;
987     config.numDirectoryServers = size;
988   }
989 }
990
991 struct DirectoryServer * directoryServers( int* numServers )
992 {
993   if( numServers )
994     *numServers = config.numDirectoryServers;
995   return config.directoryServers;
996 };
997
998 void setCertificateSource( CertificateSource source )
999 {
1000   config.certificateSource = source;
1001 }
1002
1003 CertificateSource certificateSource()
1004 {
1005   return config.certificateSource;
1006 }
1007
1008 void setCRLSource( CertificateSource source )
1009 {
1010   config.cRLSource = source;
1011 }
1012
1013 CertificateSource crlSource()
1014 {
1015   return config.cRLSource;
1016 }
1017
1018
1019 bool certificateValidity( const char* certificate,
1020                           int* level ){ return true; }
1021
1022
1023 void storeNewCharPtr( char** dest, const char* src )
1024 {
1025   int sLen = strlen( src );
1026   *dest = malloc( sLen + 1 );
1027   strcpy( *dest, src );
1028   (*dest)[sLen] = '\0';
1029 }
1030
1031
1032 bool signMessage( const char*  cleartext,
1033                   char** ciphertext,
1034                   const size_t* cipherLen,
1035                   const char*  certificate,
1036                   struct StructuringInfo* structuring,
1037                   int* errId,
1038                   char** errTxt )
1039 {
1040   bool bIsOpaque;
1041   GpgmeCtx ctx;
1042   GpgmeError err;
1043   GpgmeKey rKey;
1044   GpgmeData data,  sig;
1045   char* rSig  = 0;
1046   bool  bOk   = false;
1047   int sendCerts = 1;
1048
1049   init_StructuringInfo( structuring );
1050
1051   if( !ciphertext )
1052     return false;
1053
1054   err = gpgme_new (&ctx);
1055   gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
1056
1057   gpgme_set_armor (ctx, __GPGMEPLUG_SIGNATURE_CODE_IS_BINARY ? 0 : 1);
1058   /*  gpgme_set_textmode (ctx, 1); */
1059
1060   switch ( config.sendCertificates ) {
1061     case SendCert_undef:
1062       break;
1063     case SendCert_DontSend:
1064       sendCerts = 0;
1065       break;
1066     case SendCert_SendOwn:
1067       sendCerts = 1;
1068       break;
1069     case SendCert_SendChainWithoutRoot:
1070       sendCerts = -2;
1071       break;
1072     case SendCert_SendChainWithRoot:
1073       sendCerts = -1;
1074       break;
1075     default:
1076       sendCerts = 0;
1077       break;
1078   }
1079   gpgme_set_include_certs (ctx, sendCerts);
1080
1081   /* select the signer's key if provided */
1082   if (certificate != 0) {
1083       err = gpgme_op_keylist_start(ctx, certificate, 0);
1084       if (err == GPGME_No_Error) {
1085           /* we only support one signer for now */
1086           err = gpgme_op_keylist_next(ctx, &rKey);
1087           if (err == GPGME_No_Error) {
1088               /* clear existing signers */
1089               gpgme_signers_clear(ctx);
1090               /* set the signing key */
1091               gpgme_signers_add(ctx, rKey);
1092           }
1093           gpgme_op_keylist_end(ctx);
1094       }
1095   }
1096
1097   /* PENDING(g10) Implement this
1098
1099      gpgme_set_signature_algorithm( ctx, config.signatureAlgorithm )
1100      --> This does not make sense.  The algorithm is a property of
1101      the certificate used [wk 2002-03-23] */
1102
1103   gpgme_data_new_from_mem (&data, cleartext,
1104                             strlen( cleartext ), 1 );
1105   gpgme_data_new ( &sig );
1106
1107   // NOTE: Currently we support Opaque signed messages only for S/MIME,
1108   //       but not for OpenPGP mode!
1109   if( GPGMEPLUG_PROTOCOL == GPGME_PROTOCOL_CMS )
1110     bIsOpaque = (SignatureCompoundMode_Opaque == signatureCompoundMode());
1111   else
1112     bIsOpaque = false;
1113
1114   err = gpgme_op_sign ( ctx,
1115                         data,
1116                         sig,
1117                         bIsOpaque
1118                         ? GPGME_SIG_MODE_NORMAL
1119                         : GPGME_SIG_MODE_DETACH );
1120
1121   if ( err == GPGME_No_Error ) {
1122     if( __GPGMEPLUG_SIGNATURE_CODE_IS_BINARY ) {
1123       *ciphertext = gpgme_data_release_and_get_mem( sig, (size_t*)cipherLen );
1124       bOk = true;
1125     }
1126     else {
1127       rSig = gpgme_data_release_and_get_mem( sig, (size_t*)cipherLen );
1128       *ciphertext = malloc( *cipherLen + 1 );
1129       if( *ciphertext ) {
1130         if( *cipherLen ) {
1131           bOk = true;
1132           strncpy((char*)*ciphertext, rSig, *cipherLen );
1133         }
1134         (*ciphertext)[*cipherLen] = '\0';
1135       }
1136       free( rSig );
1137     }
1138   }
1139   else {
1140     gpgme_data_release( sig );
1141 /*
1142 *ciphertext = malloc( 70 );
1143 strcpy((char*)*ciphertext, "xyz\nsig-dummy\nzyx" );
1144 (*ciphertext)[17] = '\0';
1145 err = 0;
1146 {
1147 */
1148     *ciphertext = 0;
1149     fprintf( stderr, "\n\n    gpgme_op_sign() returned this error code:  %i\n\n", err );
1150     if( errId )
1151       *errId = err;
1152     if( errTxt ) {
1153       const char* _errTxt = gpgme_strerror( err );
1154       *errTxt = malloc( strlen( _errTxt ) + 1 );
1155       if( *errTxt )
1156         strcpy(*errTxt, _errTxt );
1157     }
1158 /*
1159 }
1160 */
1161   }
1162   gpgme_data_release( data );
1163   gpgme_release (ctx);
1164
1165   if( bOk && structuring ) {
1166     if( bIsOpaque ) {
1167       structuring->includeCleartext = GPGMEPLUG_OPA_SIGN_INCLUDE_CLEARTEXT;
1168       structuring->makeMimeObject   = GPGMEPLUG_OPA_SIGN_MAKE_MIME_OBJECT;
1169       if( structuring->makeMimeObject ) {
1170         structuring->makeMultiMime  = GPGMEPLUG_OPA_SIGN_MAKE_MULTI_MIME;
1171         storeNewCharPtr( &structuring->contentTypeMain,
1172                         GPGMEPLUG_OPA_SIGN_CTYPE_MAIN );
1173         storeNewCharPtr( &structuring->contentDispMain,
1174                         GPGMEPLUG_OPA_SIGN_CDISP_MAIN );
1175         storeNewCharPtr( &structuring->contentTEncMain,
1176                         GPGMEPLUG_OPA_SIGN_CTENC_MAIN );
1177         if( structuring->makeMultiMime ) {
1178             storeNewCharPtr( &structuring->contentTypeVersion,
1179                             GPGMEPLUG_OPA_SIGN_CTYPE_VERSION );
1180             storeNewCharPtr( &structuring->contentDispVersion,
1181                             GPGMEPLUG_OPA_SIGN_CDISP_VERSION );
1182             storeNewCharPtr( &structuring->contentTEncVersion,
1183                             GPGMEPLUG_OPA_SIGN_CTENC_VERSION );
1184             storeNewCharPtr( &structuring->bodyTextVersion,
1185                             GPGMEPLUG_OPA_SIGN_BTEXT_VERSION );
1186             storeNewCharPtr( &structuring->contentTypeCode,
1187                             GPGMEPLUG_OPA_SIGN_CTYPE_CODE );
1188             storeNewCharPtr( &structuring->contentDispCode,
1189                             GPGMEPLUG_OPA_SIGN_CDISP_CODE );
1190             storeNewCharPtr( &structuring->contentTEncCode,
1191                             GPGMEPLUG_OPA_SIGN_CTENC_CODE );
1192         }
1193       } else {
1194         storeNewCharPtr( &structuring->flatTextPrefix,
1195                         GPGMEPLUG_OPA_SIGN_FLAT_PREFIX );
1196         storeNewCharPtr( &structuring->flatTextSeparator,
1197                         GPGMEPLUG_OPA_SIGN_FLAT_SEPARATOR );
1198         storeNewCharPtr( &structuring->flatTextPostfix,
1199                         GPGMEPLUG_OPA_SIGN_FLAT_POSTFIX );
1200       }
1201     } else {
1202       structuring->includeCleartext = GPGMEPLUG_DET_SIGN_INCLUDE_CLEARTEXT;
1203       structuring->makeMimeObject   = GPGMEPLUG_DET_SIGN_MAKE_MIME_OBJECT;
1204       if( structuring->makeMimeObject ) {
1205         structuring->makeMultiMime  = GPGMEPLUG_DET_SIGN_MAKE_MULTI_MIME;
1206         storeNewCharPtr( &structuring->contentTypeMain,
1207                         GPGMEPLUG_DET_SIGN_CTYPE_MAIN );
1208         storeNewCharPtr( &structuring->contentDispMain,
1209                         GPGMEPLUG_DET_SIGN_CDISP_MAIN );
1210         storeNewCharPtr( &structuring->contentTEncMain,
1211                         GPGMEPLUG_DET_SIGN_CTENC_MAIN );
1212         if( structuring->makeMultiMime ) {
1213             storeNewCharPtr( &structuring->contentTypeVersion,
1214                             GPGMEPLUG_DET_SIGN_CTYPE_VERSION );
1215             storeNewCharPtr( &structuring->contentDispVersion,
1216                             GPGMEPLUG_DET_SIGN_CDISP_VERSION );
1217             storeNewCharPtr( &structuring->contentTEncVersion,
1218                             GPGMEPLUG_DET_SIGN_CTENC_VERSION );
1219             storeNewCharPtr( &structuring->bodyTextVersion,
1220                             GPGMEPLUG_DET_SIGN_BTEXT_VERSION );
1221             storeNewCharPtr( &structuring->contentTypeCode,
1222                             GPGMEPLUG_DET_SIGN_CTYPE_CODE );
1223             storeNewCharPtr( &structuring->contentDispCode,
1224                             GPGMEPLUG_DET_SIGN_CDISP_CODE );
1225             storeNewCharPtr( &structuring->contentTEncCode,
1226                             GPGMEPLUG_DET_SIGN_CTENC_CODE );
1227         }
1228       } else {
1229         storeNewCharPtr( &structuring->flatTextPrefix,
1230                         GPGMEPLUG_DET_SIGN_FLAT_PREFIX );
1231         storeNewCharPtr( &structuring->flatTextSeparator,
1232                         GPGMEPLUG_DET_SIGN_FLAT_SEPARATOR );
1233         storeNewCharPtr( &structuring->flatTextPostfix,
1234                         GPGMEPLUG_DET_SIGN_FLAT_POSTFIX );
1235       }
1236     }
1237   }
1238   return bOk;
1239 }
1240
1241
1242 static const char*
1243 sig_status_to_string( GpgmeSigStat status )
1244 {
1245   const char *result;
1246
1247   switch (status) {
1248     case GPGME_SIG_STAT_NONE:
1249       result = "Oops: Signature not verified";
1250       break;
1251     case GPGME_SIG_STAT_NOSIG:
1252       result = "No signature found";
1253       break;
1254     case GPGME_SIG_STAT_GOOD:
1255       result = "Good signature";
1256       break;
1257     case GPGME_SIG_STAT_BAD:
1258       result = "BAD signature";
1259       break;
1260     case GPGME_SIG_STAT_NOKEY:
1261       result = "No public key to verify the signature";
1262       break;
1263     case GPGME_SIG_STAT_ERROR:
1264       result = "Error verifying the signature";
1265       break;
1266     case GPGME_SIG_STAT_DIFF:
1267       result = "Different results for signatures";
1268       break;
1269     default:
1270       result = "Error: Unknown status";
1271       break;
1272   }
1273
1274   return result;
1275 }
1276
1277
1278 bool checkMessageSignature( char** cleartext,
1279                             const char* signaturetext,
1280                             bool signatureIsBinary,
1281                             int signatureLen,
1282                             struct SignatureMetaData* sigmeta )
1283 {
1284   GpgmeCtx ctx;
1285   GpgmeSigStat status;
1286   unsigned long sumGPGME;
1287   SigStatusFlags sumPlug;
1288   GpgmeData datapart, sigpart;
1289   char* rClear = 0;
1290   size_t clearLen;
1291   GpgmeError err;
1292   GpgmeKey key;
1293   time_t created;
1294   int sig_idx = 0;
1295   const char* statusStr;
1296   const char* fpr;
1297   bool isOpaqueSigned;
1298
1299   if( !cleartext ) {
1300     if( sigmeta )
1301       storeNewCharPtr( &sigmeta->status,
1302                         __GPGMEPLUG_ERROR_CLEARTEXT_IS_ZERO );
1303
1304     return false;
1305   }
1306
1307   isOpaqueSigned = !*cleartext;
1308
1309   gpgme_new( &ctx );
1310   gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
1311   gpgme_set_armor (ctx,    signatureIsBinary ? 0 : 1);
1312   /*  gpgme_set_textmode (ctx, signatureIsBinary ? 0 : 1); */
1313
1314   if( isOpaqueSigned )
1315     gpgme_data_new( &datapart );
1316   else
1317     gpgme_data_new_from_mem( &datapart, *cleartext,
1318                              strlen( *cleartext ), 1 );
1319
1320   gpgme_data_new_from_mem( &sigpart,
1321                            signaturetext,
1322                            signatureIsBinary
1323                            ? signatureLen
1324                            : strlen( signaturetext ),
1325                            1 );
1326
1327   gpgme_op_verify( ctx, sigpart, datapart, &status );
1328
1329   if( isOpaqueSigned ) {
1330     rClear = gpgme_data_release_and_get_mem( datapart, &clearLen );
1331     *cleartext = malloc( clearLen + 1 );
1332     if( *cleartext ) {
1333       if( clearLen )
1334         strncpy(*cleartext, rClear, clearLen );
1335       (*cleartext)[clearLen] = '\0';
1336     }
1337     free( rClear );
1338   }
1339   else
1340     gpgme_data_release( datapart );
1341
1342   gpgme_data_release( sigpart );
1343
1344   /* Provide information in the sigmeta struct */
1345   /* the status string */
1346   statusStr = sig_status_to_string( status );
1347   sigmeta->status = malloc( strlen( statusStr ) + 1 );
1348   if( sigmeta->status ) {
1349     strcpy( sigmeta->status, statusStr );
1350     sigmeta->status[strlen( statusStr )] = '\0';
1351   } else
1352     ; /* nothing to do, is already 0 */
1353
1354   /* Extended information for any number of signatures. */
1355   fpr = gpgme_get_sig_status( ctx, sig_idx, &status, &created );
1356   sigmeta->extended_info = 0;
1357   while( fpr != NULL ) {
1358     struct tm* ctime_val;
1359     const char* sig_status;
1360
1361     void* realloc_return = realloc( sigmeta->extended_info,
1362                                     sizeof( struct SignatureMetaDataExtendedInfo ) * ( sig_idx + 1 ) );
1363     if( realloc_return ) {
1364       sigmeta->extended_info = realloc_return;
1365
1366       /* clear the data area */
1367       memset( &sigmeta->extended_info[sig_idx], 0, sizeof (struct SignatureMetaDataExtendedInfo) );
1368
1369       /* the creation time */
1370       sigmeta->extended_info[sig_idx].creation_time = malloc( sizeof( struct tm ) );
1371       if( sigmeta->extended_info[sig_idx].creation_time ) {
1372         ctime_val = localtime( &created );
1373         memcpy( sigmeta->extended_info[sig_idx].creation_time,
1374                 ctime_val, sizeof( struct tm ) );
1375       }
1376
1377       /* the extended signature verification status */
1378       sumGPGME = gpgme_get_sig_ulong_attr( ctx,
1379                                            sig_idx,
1380                                            GPGME_ATTR_SIG_SUMMARY,
1381                                            0 );
1382       // translate GPGME status flags to common CryptPlug status flags
1383       sumPlug = 0;
1384       if( sumGPGME & GPGME_SIGSUM_VALID       ) sumPlug |= SigStat_VALID      ;
1385       if( sumGPGME & GPGME_SIGSUM_GREEN       ) sumPlug |= SigStat_GREEN      ;
1386       if( sumGPGME & GPGME_SIGSUM_RED         ) sumPlug |= SigStat_RED        ;
1387       if( sumGPGME & GPGME_SIGSUM_KEY_REVOKED ) sumPlug |= SigStat_KEY_REVOKED;
1388       if( sumGPGME & GPGME_SIGSUM_KEY_EXPIRED ) sumPlug |= SigStat_KEY_EXPIRED;
1389       if( sumGPGME & GPGME_SIGSUM_SIG_EXPIRED ) sumPlug |= SigStat_SIG_EXPIRED;
1390       if( sumGPGME & GPGME_SIGSUM_KEY_MISSING ) sumPlug |= SigStat_KEY_MISSING;
1391       if( sumGPGME & GPGME_SIGSUM_CRL_MISSING ) sumPlug |= SigStat_CRL_MISSING;
1392       if( sumGPGME & GPGME_SIGSUM_CRL_TOO_OLD ) sumPlug |= SigStat_CRL_TOO_OLD;
1393       if( sumGPGME & GPGME_SIGSUM_BAD_POLICY  ) sumPlug |= SigStat_BAD_POLICY ;
1394       if( sumGPGME & GPGME_SIGSUM_SYS_ERROR   ) sumPlug |= SigStat_SYS_ERROR  ;
1395       if( !sumPlug )
1396         sumPlug = SigStat_NUMERICAL_CODE | sumGPGME;
1397       sigmeta->extended_info[sig_idx].sigStatusFlags = sumPlug;
1398
1399       sigmeta->extended_info[sig_idx].validity = GPGME_VALIDITY_UNKNOWN;
1400
1401       err = gpgme_get_sig_key (ctx, sig_idx, &key);
1402
1403       if ( err == GPGME_No_Error) {
1404         const char* attr_string;
1405         unsigned long attr_ulong;
1406
1407         /* extract key identidy */
1408         attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_KEYID, 0, 0);
1409         if (attr_string != 0)
1410             storeNewCharPtr( &sigmeta->extended_info[sig_idx].keyid, attr_string );
1411
1412         /* extract finger print */
1413         attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_FPR, 0, 0);
1414         if (attr_string != 0)
1415             storeNewCharPtr( &sigmeta->extended_info[sig_idx].fingerprint,
1416                             attr_string );
1417
1418         /* algorithms useable with this key */
1419         attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_ALGO, 0, 0);
1420         if (attr_string != 0)
1421             storeNewCharPtr( &sigmeta->extended_info[sig_idx].algo,
1422                             attr_string );
1423         attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_ALGO, 0, 0);
1424         sigmeta->extended_info[sig_idx].algo_num = attr_ulong;
1425
1426         /* extract key validity */
1427         attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_VALIDITY, 0, 0);
1428         sigmeta->extended_info[sig_idx].validity = attr_ulong;
1429
1430         /* extract user id, according to the documentation it's representable
1431         * as a number, but it seems that it also has a string representation
1432         */
1433         attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_USERID, 0, 0);
1434         if (attr_string != 0)
1435             storeNewCharPtr( &sigmeta->extended_info[sig_idx].userid,
1436                             attr_string );
1437         attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_USERID, 0, 0);
1438         sigmeta->extended_info[sig_idx].userid_num = attr_ulong;
1439
1440         /* extract the length */
1441         attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_LEN, 0, 0);
1442         sigmeta->extended_info[sig_idx].keylen = attr_ulong;
1443
1444         /* extract the creation time of the key */
1445         attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_CREATED, 0, 0);
1446         sigmeta->extended_info[sig_idx].key_created = attr_ulong;
1447
1448         /* extract the expiration time of the key */
1449         attr_ulong = gpgme_key_get_ulong_attr(key, GPGME_ATTR_EXPIRE, 0, 0);
1450         sigmeta->extended_info[sig_idx].key_expires = attr_ulong;
1451
1452         /* extract user name */
1453         attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_NAME, 0, 0);
1454         if (attr_string != 0)
1455             storeNewCharPtr( &sigmeta->extended_info[sig_idx].name,
1456                             attr_string );
1457
1458         /* extract email */
1459         attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_EMAIL, 0, 0);
1460         if (attr_string != 0)
1461             storeNewCharPtr( &sigmeta->extended_info[sig_idx].email,
1462                             attr_string );
1463
1464         /* extract the comment */
1465         attr_string = gpgme_key_get_string_attr(key, GPGME_ATTR_COMMENT, 0, 0);
1466         if (attr_string != 0)
1467             storeNewCharPtr( &sigmeta->extended_info[sig_idx].comment,
1468                             attr_string );
1469       }
1470       else
1471         storeNewCharPtr( &sigmeta->extended_info[sig_idx].fingerprint, fpr );
1472
1473       sig_status = sig_status_to_string( status );
1474       storeNewCharPtr( &sigmeta->extended_info[sig_idx].status_text,
1475                        sig_status );
1476
1477     } else
1478       break; /* if allocation fails once, it isn't likely to
1479                 succeed the next time either */
1480
1481     fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
1482   }
1483   sigmeta->extended_info_count = sig_idx;
1484   sigmeta->nota_xml = gpgme_get_notation( ctx );
1485   sigmeta->status_code = status;
1486
1487   gpgme_release( ctx );
1488   return ( status == GPGME_SIG_STAT_GOOD );
1489 }
1490
1491 bool storeCertificatesFromMessage(
1492         const char* ciphertext ){ return true; }
1493
1494
1495 /* returns address if address doesn't contain a <xxx> part
1496  * else it returns a new string xxx and frees address
1497  */
1498 static char* parseAddress( char* address )
1499 {
1500   char* result = address;
1501   char* i;
1502   char* j;
1503   if( !result ) return result;
1504   i = index( address, '<' );
1505   if( i ) {
1506     j = index( i+1, '>' );
1507     if( j == NULL ) j = address+strlen(address);
1508     result = malloc( j-i );
1509     strncpy( result, i+1, j-i-1 );
1510     result[j-i-1] = '\0';
1511     free( address );
1512   } else {
1513     i = address;
1514     j = i+strlen(address);
1515   }
1516   {
1517     /* remove surrounding whitespace */
1518     char* k = result+(j-i-1);
1519     char* l = result;
1520     while( isspace( *l ) ) ++l;
1521     while( isspace( *k ) ) --k;
1522     if( l != result || k != result+(j-i-1) ) {
1523       char* result2 = malloc( k-l+2 );
1524       strncpy( result2, l, k-l+1 );
1525       result2[k-l+1] = '\0';
1526       free(result);
1527       result = result2;
1528     }
1529   }
1530   return result;
1531 }
1532
1533 static char* nextAddress( const char** address )
1534 {
1535   const char *start = *address;
1536   char* result = NULL;
1537   int quote = 0;
1538   int comment = 0;
1539   int found = 0;
1540   if( *address == NULL ) return NULL;
1541   while( **address ) {
1542
1543     switch( **address ) {
1544     case '\\': /* escaped character */
1545       ++(*address);
1546       break;
1547     case '"':
1548       if( comment == 0 ) {
1549         if( quote > 0 ) --quote;
1550         else ++quote;
1551       }
1552       break;
1553     case '(': /* comment start */
1554       if( quote == 0 ) ++comment;
1555       break;
1556     case ')': /* comment end */
1557       if( quote == 0 ) --comment;
1558       break;
1559     case '\0':
1560     case '\1': /* delimiter */
1561       if( quote == 0 && comment == 0 ) {
1562         found = 1;
1563       }
1564       break;
1565     }
1566     ++(*address);
1567     if( found ) break;
1568   }
1569   if( found || **address == 0 ) {
1570     size_t len;
1571     len = *address - start;
1572     if( len > 0 ) {
1573       if( **address != 0 ) --len;
1574       result = malloc( len*sizeof(char)+1 );
1575       strncpy( result, start, len );
1576       result[len] = '\0';
1577     }
1578   }
1579   return parseAddress(result);
1580 }
1581
1582 bool encryptMessage( const char*  cleartext,
1583                      const char** ciphertext,
1584                      const size_t* cipherLen,
1585                      const char*  certificate,
1586                      struct StructuringInfo* structuring,
1587                      int* errId,
1588                      char** errTxt )
1589 {
1590   GpgmeCtx ctx;
1591   GpgmeError err;
1592   GpgmeData gCiphertext, gPlaintext;
1593   GpgmeRecipients rset;
1594   char*  rCiph = 0;
1595   bool   bOk   = false;
1596
1597   init_StructuringInfo( structuring );
1598
1599   gpgme_new (&ctx);
1600   gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
1601
1602   gpgme_set_armor (ctx, __GPGMEPLUG_ENCRYPTED_CODE_IS_BINARY ? 0 : 1);
1603   /*  gpgme_set_textmode (ctx, 1); */
1604
1605   gpgme_data_new_from_mem (&gPlaintext, cleartext,
1606                             1+strlen( cleartext ), 1 );
1607   err = gpgme_data_new ( &gCiphertext );
1608
1609   gpgme_recipients_new (&rset);
1610
1611   /*
1612   if( GPGMEPLUG_PROTOCOL == GPGME_PROTOCOL_CMS )
1613   {
1614     gpgme_recipients_add_name (rset,
1615       "/CN=test cert 1,OU=Aegypten Project,O=g10 Code GmbH,L=DÃ?sseldorf,C=DE" );
1616
1617     fputs( "\nGPGSMPLUG encryptMessage() using test key of Aegypten Project\n", stderr );
1618   }
1619   else
1620   */
1621   {
1622     const char* p = certificate;
1623     char* tok;
1624     while( (tok = nextAddress( &p ) ) != 0 ) {
1625       gpgme_recipients_add_name (rset, tok );
1626       fprintf( stderr, "\nGPGMEPLUG encryptMessage() using addressee %s\n", tok );
1627       free(tok);
1628     }
1629   }
1630
1631   /* PENDING(g10) Implement this
1632      Possible values: RSA = 1, SHA1 = 2, TripleDES = 3
1633      gpgme_set_encryption_algorithm( ctx, config.encryptionAlgorithm );
1634
1635      -> Your are mixing public key and symmetric algorithms.  The
1636      latter may be configured but the sphix specifications do opnly
1637      allow 3-DES so this is not nothing we need to do.  The proper way
1638      to select the symmetric algorithm is anyway by looking at the
1639      capabilities of the certificate because this is the only way to
1640      know what the recipient can accept. [wk 2002-03-23]
1641
1642      PENDING(g10) Implement this
1643      gpgme_set_encryption_check_certificate_path(
1644      config.checkCertificatePath )
1645
1646      PENDING(g10) Implement this
1647      gpgme_set_encryption_check_certificate_path_to_root(
1648      config.checkEncryptionCertificatePathToRoot )
1649
1650      -> Not checking a certificate up to the ROOT CA is dangerous and
1651      stupid. There is no need for those options.  [wk 2002-03-23] */
1652
1653
1654
1655   err = gpgme_op_encrypt (ctx, rset, gPlaintext, gCiphertext );
1656   if( err ) {
1657     fprintf( stderr, "\ngpgme_op_encrypt() returned this error code:  %i\n\n", err );
1658     if( errId )
1659       *errId = err;
1660     if( errTxt ) {
1661       const char* _errTxt = gpgme_strerror( err );
1662       *errTxt = malloc( strlen( _errTxt ) + 1 );
1663       if( *errTxt )
1664         strcpy(*errTxt, _errTxt );
1665     }
1666   }
1667
1668   gpgme_recipients_release (rset);
1669   gpgme_data_release (gPlaintext);
1670
1671   if( err == GPGME_No_Error ) {
1672     if( __GPGMEPLUG_ENCRYPTED_CODE_IS_BINARY ) {
1673       *ciphertext = gpgme_data_release_and_get_mem( gCiphertext, (size_t*)cipherLen );
1674       bOk = true;
1675     }
1676     else {
1677       rCiph = gpgme_data_release_and_get_mem( gCiphertext, (size_t*)cipherLen );
1678       *ciphertext = malloc( *cipherLen + 1 );
1679       if( *ciphertext ) {
1680         if( *cipherLen ) {
1681           bOk = true;
1682           strncpy((char*)*ciphertext, rCiph, *cipherLen );
1683         }
1684         ((char*)(*ciphertext))[*cipherLen] = 0;
1685       }
1686       free( rCiph );
1687     }
1688   }
1689   else {
1690     gpgme_data_release ( gCiphertext );
1691     *ciphertext = 0;
1692     /* error handling is missing: if only one untrusted key was found
1693       (or none at all), gpg won't sign the message.  (hier fehlt eine
1694       Fehlerbehandlung: fuer einen Recipient nur ein untrusted key
1695       (oder gar keiner) gefunden wurde, verweigert gpg das signieren.)
1696     */
1697   }
1698
1699   gpgme_release (ctx);
1700
1701   fflush( stderr );
1702
1703   if( bOk && structuring ) {
1704     structuring->includeCleartext = GPGMEPLUG_ENC_INCLUDE_CLEARTEXT;
1705     structuring->makeMimeObject   = GPGMEPLUG_ENC_MAKE_MIME_OBJECT;
1706     if( structuring->makeMimeObject ) {
1707       structuring->makeMultiMime  = GPGMEPLUG_ENC_MAKE_MULTI_MIME;
1708       storeNewCharPtr( &structuring->contentTypeMain,
1709                        GPGMEPLUG_ENC_CTYPE_MAIN );
1710       storeNewCharPtr( &structuring->contentDispMain,
1711                        GPGMEPLUG_ENC_CDISP_MAIN );
1712       storeNewCharPtr( &structuring->contentTEncMain,
1713                        GPGMEPLUG_ENC_CTENC_MAIN );
1714       if( structuring->makeMultiMime ) {
1715         storeNewCharPtr( &structuring->contentTypeVersion,
1716                          GPGMEPLUG_ENC_CTYPE_VERSION );
1717         storeNewCharPtr( &structuring->contentDispVersion,
1718                          GPGMEPLUG_ENC_CDISP_VERSION );
1719         storeNewCharPtr( &structuring->contentTEncVersion,
1720                          GPGMEPLUG_ENC_CTENC_VERSION );
1721         storeNewCharPtr( &structuring->bodyTextVersion,
1722                          GPGMEPLUG_ENC_BTEXT_VERSION );
1723         storeNewCharPtr( &structuring->contentTypeCode,
1724                          GPGMEPLUG_ENC_CTYPE_CODE );
1725         storeNewCharPtr( &structuring->contentDispCode,
1726                          GPGMEPLUG_ENC_CDISP_CODE );
1727         storeNewCharPtr( &structuring->contentTEncCode,
1728                          GPGMEPLUG_ENC_CTENC_CODE );
1729       }
1730     } else {
1731       storeNewCharPtr( &structuring->flatTextPrefix,
1732                        GPGMEPLUG_ENC_FLAT_PREFIX );
1733       storeNewCharPtr( &structuring->flatTextSeparator,
1734                        GPGMEPLUG_ENC_FLAT_SEPARATOR );
1735       storeNewCharPtr( &structuring->flatTextPostfix,
1736                        GPGMEPLUG_ENC_FLAT_POSTFIX );
1737     }
1738   }
1739   return bOk;
1740 }
1741
1742
1743 bool encryptAndSignMessage( const char* cleartext,
1744                             const char** ciphertext,
1745                             const char* certificate,
1746                             struct StructuringInfo* structuring )
1747 {
1748   bool bOk;
1749
1750   init_StructuringInfo( structuring );
1751
1752   bOk = false;
1753
1754   /* implementation of this function is still missing */
1755
1756   if( bOk && structuring ) {
1757     structuring->includeCleartext = GPGMEPLUG_ENCSIGN_INCLUDE_CLEARTEXT;
1758     structuring->makeMimeObject   = GPGMEPLUG_ENCSIGN_MAKE_MIME_OBJECT;
1759     if( structuring->makeMimeObject ) {
1760       structuring->makeMultiMime  = GPGMEPLUG_ENCSIGN_MAKE_MULTI_MIME;
1761       storeNewCharPtr( &structuring->contentTypeMain,
1762                        GPGMEPLUG_ENCSIGN_CTYPE_MAIN );
1763       storeNewCharPtr( &structuring->contentDispMain,
1764                        GPGMEPLUG_ENCSIGN_CDISP_MAIN );
1765       storeNewCharPtr( &structuring->contentTEncMain,
1766                        GPGMEPLUG_ENCSIGN_CTENC_MAIN );
1767       if( structuring->makeMultiMime ) {
1768         storeNewCharPtr( &structuring->contentTypeVersion,
1769                          GPGMEPLUG_ENCSIGN_CTYPE_VERSION );
1770         storeNewCharPtr( &structuring->contentDispVersion,
1771                          GPGMEPLUG_ENCSIGN_CDISP_VERSION );
1772         storeNewCharPtr( &structuring->contentTEncVersion,
1773                          GPGMEPLUG_ENCSIGN_CTENC_VERSION );
1774         storeNewCharPtr( &structuring->bodyTextVersion,
1775                          GPGMEPLUG_ENCSIGN_BTEXT_VERSION );
1776         storeNewCharPtr( &structuring->contentTypeCode,
1777                          GPGMEPLUG_ENCSIGN_CTYPE_CODE );
1778         storeNewCharPtr( &structuring->contentDispCode,
1779                          GPGMEPLUG_ENCSIGN_CDISP_CODE );
1780         storeNewCharPtr( &structuring->contentTEncCode,
1781                          GPGMEPLUG_ENCSIGN_CTENC_CODE );
1782       }
1783     } else {
1784       storeNewCharPtr( &structuring->flatTextPrefix,
1785                        GPGMEPLUG_ENCSIGN_FLAT_PREFIX );
1786       storeNewCharPtr( &structuring->flatTextSeparator,
1787                        GPGMEPLUG_ENCSIGN_FLAT_SEPARATOR );
1788       storeNewCharPtr( &structuring->flatTextPostfix,
1789                        GPGMEPLUG_ENCSIGN_FLAT_POSTFIX );
1790     }
1791   }
1792   return bOk;
1793 }
1794
1795
1796 bool decryptMessage( const char* ciphertext,
1797                      bool        cipherIsBinary,
1798                      int         cipherLen,
1799                      const char** cleartext,
1800                      const char* certificate )
1801 {
1802   GpgmeCtx ctx;
1803   GpgmeError err;
1804   GpgmeData gCiphertext, gPlaintext;
1805   size_t rCLen = 0;
1806   char*  rCiph = 0;
1807   bool bOk = false;
1808
1809   if( !ciphertext )
1810     return false;
1811
1812   err = gpgme_new (&ctx);
1813   gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
1814   
1815   gpgme_set_armor (ctx, cipherIsBinary ? 0 : 1);
1816   /*  gpgme_set_textmode (ctx, cipherIsBinary ? 0 : 1); */
1817
1818   /*
1819   gpgme_data_new_from_mem( &gCiphertext, ciphertext,
1820                            1+strlen( ciphertext ), 1 ); */
1821   gpgme_data_new_from_mem( &gCiphertext,
1822                            ciphertext,
1823                            cipherIsBinary
1824                            ? cipherLen
1825                            : strlen( ciphertext ),
1826                            1 );
1827
1828   gpgme_data_new( &gPlaintext );
1829
1830   gpgme_op_decrypt( ctx, gCiphertext, gPlaintext );
1831   gpgme_data_release( gCiphertext );
1832
1833   rCiph = gpgme_data_release_and_get_mem( gPlaintext,  &rCLen );
1834
1835   *cleartext = malloc( rCLen + 1 );
1836   if( *cleartext ) {
1837       if( rCLen ) {
1838           bOk = true;
1839           strncpy((char*)*cleartext, rCiph, rCLen );
1840       }
1841       ((char*)(*cleartext))[rCLen] = 0;
1842   }
1843
1844   free( rCiph );
1845   gpgme_release( ctx );
1846   return bOk;
1847 }
1848
1849 bool decryptAndCheckMessage( const char* ciphertext,
1850           const char** cleartext, const char* certificate,
1851           struct SignatureMetaData* sigmeta ){ return true; }
1852
1853
1854 const char* requestCertificateDialog(){ return 0; }
1855
1856 bool requestDecentralCertificate( const char* certparms, 
1857                                   char** generatedKey, int* length )
1858 {
1859     GpgmeError err;
1860     GpgmeCtx ctx;
1861     GpgmeData pub;
1862     int len;
1863
1864     err = gpgme_data_new (&pub);
1865     fprintf( stderr,  "1: gpgme returned %d\n", err );
1866     if( err != GPGME_No_Error )
1867         return false;
1868
1869     err = gpgme_new (&ctx);
1870     fprintf( stderr,  "2: gpgme returned %d\n", err );
1871     if( err != GPGME_No_Error ) {
1872         gpgme_data_release( pub );
1873         return false;
1874     }
1875
1876     gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
1877     /* Don't ASCII-armor, the MUA will use base64 encoding */
1878     /*    gpgme_set_armor (ctx, 1); */
1879     err = gpgme_op_genkey (ctx, certparms, pub, NULL );
1880     fprintf( stderr,  "3: gpgme returned %d\n", err );
1881     if( err != GPGME_No_Error ) {
1882         gpgme_data_release( pub );
1883         gpgme_release( ctx );
1884         return false;
1885     }
1886
1887     gpgme_release (ctx);
1888     *generatedKey = gpgme_data_release_and_get_mem (pub, &len);
1889     *length = len;
1890
1891     /* The buffer generatedKey contains the LEN bytes you want */
1892     // Caller is responsible for freeing
1893     return true;
1894 }
1895
1896 bool requestCentralCertificateAndPSE( const char* name,
1897           const char* email, const char* organization, const char* department,
1898           const char* ca_address ){ return true; }
1899
1900 bool createPSE(){ return true; }
1901
1902 bool registerCertificate( const char* certificate ){ return true; }
1903
1904 bool requestCertificateProlongation( const char* certificate,
1905                                      const char* ca_address ){ return true; }
1906
1907 const char* certificateChain(){ return 0; }
1908
1909 bool deleteCertificate( const char* certificate ){ return true; }
1910
1911 bool archiveCertificate( const char* certificate ){ return true; }
1912
1913
1914 const char* displayCRL(){ return 0; }
1915
1916 void updateCRL(){}
1917
1918 /*
1919  * Copyright (C) 2002 g10 Code GmbH
1920  * 
1921  *     This program is free software; you can redistribute it
1922  *     and/or modify it under the terms of the GNU General Public
1923  *     License as published by the Free Software Foundation; either
1924  *     version 2 of the License, or (at your option) any later
1925  *     version.
1926  * 
1927  *     This program is distributed in the hope that it will be
1928  *     useful, but WITHOUT ANY WARRANTY; without even the implied
1929  *     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
1930  *     PURPOSE.  See the GNU General Public License for more
1931  *     details.
1932  * 
1933  *     You should have received a copy of the GNU General Public
1934  *     License along with this program; if not, write to the Free
1935  *     Software Foundation, Inc., 59 Temple Place - Suite 330,
1936  *     Boston, MA  02111, USA.
1937  */
1938
1939 /* Max number of parts in a DN */
1940 #define MAX_GPGME_IDX 20
1941
1942 /* some macros to replace ctype ones and avoid locale problems */
1943 #define spacep(p)   (*(p) == ' ' || *(p) == '\t')
1944 #define digitp(p)   (*(p) >= '0' && *(p) <= '9')
1945 #define hexdigitp(a) (digitp (a)                     \
1946                       || (*(a) >= 'A' && *(a) <= 'F')  \
1947                       || (*(a) >= 'a' && *(a) <= 'f'))
1948 /* the atoi macros assume that the buffer has only valid digits */
1949 #define atoi_1(p)   (*(p) - '0' )
1950 #define atoi_2(p)   ((atoi_1(p) * 10) + atoi_1((p)+1))
1951 #define atoi_4(p)   ((atoi_2(p) * 100) + atoi_2((p)+2))
1952 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
1953                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
1954 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
1955
1956 #define safe_malloc( x ) malloc( x )
1957 #define xstrdup( x ) (x)?strdup(x):0
1958
1959 static void 
1960 safe_free( void** x ) 
1961 {
1962   free( *x );
1963   *x = 0;
1964 }
1965 char *
1966 trim_trailing_spaces( char *string )
1967 {
1968     char *p, *mark;
1969
1970     for( mark = NULL, p = string; *p; p++ ) {
1971         if( isspace( *p ) ) {
1972             if( !mark )
1973                 mark = p;
1974         }
1975         else
1976             mark = NULL;
1977     }
1978     if( mark )
1979         *mark = '\0' ;
1980
1981     return string ;
1982 }
1983 /*#define safe_free( x ) free( x )*/
1984
1985 /* Parse a DN and return an array-ized one.  This is not a validating
1986    parser and it does not support any old-stylish syntax; gpgme is
1987    expected to return only rfc2253 compatible strings. */
1988 static const unsigned char *
1989 parse_dn_part (struct DnPair *array, const unsigned char *string)
1990 {
1991   const unsigned char *s, *s1;
1992   size_t n;
1993   unsigned char *p;
1994
1995   /* parse attributeType */
1996   for (s = string+1; *s && *s != '='; s++)
1997     ;
1998   if (!*s)
1999     return NULL; /* error */
2000   n = s - string;
2001   if (!n)
2002     return NULL; /* empty key */
2003   array->key = p = safe_malloc (n+1);
2004   
2005   
2006   memcpy (p, string, n);
2007   p[n] = 0;
2008   trim_trailing_spaces (p);
2009   if ( !strcmp (p, "1.2.840.113549.1.9.1") )
2010     strcpy (p, "EMail");
2011   string = s + 1;
2012
2013   if (*string == '#')
2014     { /* hexstring */
2015       string++;
2016       for (s=string; hexdigitp (s); s++)
2017         s++;
2018       n = s - string;
2019       if (!n || (n & 1))
2020         return NULL; /* empty or odd number of digits */
2021       n /= 2;
2022       array->value = p = safe_malloc (n+1);
2023       
2024       
2025       for (s1=string; n; s1 += 2, n--)
2026         *p++ = xtoi_2 (s1);
2027       *p = 0;
2028    }
2029   else
2030     { /* regular v3 quoted string */
2031       for (n=0, s=string; *s; s++)
2032         {
2033           if (*s == '\\')
2034             { /* pair */
2035               s++;
2036               if (*s == ',' || *s == '=' || *s == '+'
2037                   || *s == '<' || *s == '>' || *s == '#' || *s == ';' 
2038                   || *s == '\\' || *s == '\"' || *s == ' ')
2039                 n++;
2040               else if (hexdigitp (s) && hexdigitp (s+1))
2041                 {
2042                   s++;
2043                   n++;
2044                 }
2045               else
2046                 return NULL; /* invalid escape sequence */
2047             }
2048           else if (*s == '\"')
2049             return NULL; /* invalid encoding */
2050           else if (*s == ',' || *s == '=' || *s == '+'
2051                    || *s == '<' || *s == '>' || *s == '#' || *s == ';' )
2052             break; 
2053           else
2054             n++;
2055         }
2056
2057       array->value = p = safe_malloc (n+1);
2058       
2059       
2060       for (s=string; n; s++, n--)
2061         {
2062           if (*s == '\\')
2063             { 
2064               s++;
2065               if (hexdigitp (s))
2066                 {
2067                   *p++ = xtoi_2 (s);
2068                   s++;
2069                 }
2070               else
2071                 *p++ = *s;
2072             }
2073           else
2074             *p++ = *s;
2075         }
2076       *p = 0;
2077     }
2078   return s;
2079 }
2080
2081
2082 /* Parse a DN and return an array-ized one.  This is not a validating
2083    parser and it does not support any old-stylish syntax; gpgme is
2084    expected to return only rfc2253 compatible strings. */
2085 static struct DnPair *
2086 parse_dn (const unsigned char *string)
2087 {
2088   struct DnPair *array;
2089   size_t arrayidx, arraysize;
2090   int i;
2091
2092   if( !string )
2093     return NULL;
2094
2095   arraysize = 7; /* C,ST,L,O,OU,CN,email */
2096   arrayidx = 0;
2097   array = safe_malloc ((arraysize+1) * sizeof *array);
2098   
2099   
2100   while (*string)
2101     {
2102       while (*string == ' ')
2103         string++;
2104       if (!*string)
2105         break; /* ready */
2106       if (arrayidx >= arraysize)
2107         { /* mutt lacks a real safe_realoc - so we need to copy */
2108           struct DnPair *a2;
2109
2110           arraysize += 5;
2111           a2 = safe_malloc ((arraysize+1) * sizeof *array);
2112           for (i=0; i < arrayidx; i++)
2113             {
2114               a2[i].key = array[i].key;
2115               a2[i].value = array[i].value;
2116             }
2117           safe_free ((void **)&array);
2118           array = a2;
2119         }
2120       array[arrayidx].key = NULL;
2121       array[arrayidx].value = NULL;
2122       string = parse_dn_part (array+arrayidx, string);
2123       arrayidx++;
2124       if (!string)
2125         goto failure;
2126       while (*string == ' ')
2127         string++;
2128       if (*string && *string != ',' && *string != ';' && *string != '+')
2129         goto failure; /* invalid delimiter */
2130       if (*string)
2131         string++;
2132     }
2133   array[arrayidx].key = NULL;
2134   array[arrayidx].value = NULL;
2135   return array;
2136
2137  failure:
2138   for (i=0; i < arrayidx; i++)
2139     {
2140       safe_free ((void**)&array[i].key);
2141       safe_free ((void**)&array[i].value);
2142     }
2143   safe_free ((void**)&array);
2144   return NULL;
2145 }
2146
2147 static int 
2148 add_dn_part( char* result, struct DnPair* dn, const char* part )
2149 {
2150   int any = 0;
2151
2152   if( dn ) {
2153     for(; dn->key; ++dn ) {
2154       if( !strcmp( dn->key, part ) ) {
2155         if( any ) strcat( result, "+" );
2156         /* email hack */
2157         if( !strcmp( part, "1.2.840.113549.1.9.1" ) ) strcat( result, "EMail" );
2158         else strcat( result, part );
2159         strcat( result, "=" );
2160         strcat( result, dn->value );
2161         any = 1;
2162       }
2163     }
2164   }
2165   return any;
2166 }
2167
2168 static char* 
2169 reorder_dn( struct DnPair *dn )
2170 {
2171   // note: The must parts are: CN, L, OU, O, C
2172   const char* stdpart[] = {
2173     "CN", "S", "SN", "GN", "T", "UID",
2174           "MAIL", "EMAIL", "MOBILE", "TEL", "FAX", "STREET",
2175     "L",  "PC", "SP", "ST",
2176     "OU",
2177     "O",
2178     "C",
2179     NULL
2180   };
2181   int any=0, any2=0, len=0, i;
2182   char* result;
2183   if( dn ) {
2184     for( i = 0; dn[i].key; ++i ) {
2185       len += strlen( dn[i].key );
2186       len += strlen( dn[i].value );
2187       len += 4; /* ',' and '=', and possibly "(" and ")" */
2188     }
2189   }
2190   result = (char*)safe_malloc( (len+1)*sizeof(char) );
2191   *result = 0;
2192
2193   /* add standard parts */
2194   for( i = 0; stdpart[i]; ++i ) {
2195     if( any ) {
2196       strcat( result, "," );
2197     }
2198     any = add_dn_part( result, dn, stdpart[i] );
2199   }
2200
2201   /* add remaining parts in no particular order */
2202   if( dn ) {
2203     for(; dn->key; ++dn ) {
2204       for( i = 0; stdpart[i]; ++i ) {
2205         if( !strcmp( dn->key, stdpart[i] ) ) {
2206           break;
2207         }
2208       }
2209       if( !stdpart[i] ) {
2210         if( any ) strcat( result, "," );
2211         if( !any2 ) strcat( result, "(");
2212         any = add_dn_part( result, dn, dn->key );
2213         any2 = 1;
2214       }
2215     }
2216   }
2217   if( any2 ) strcat( result, ")");
2218   return result;
2219 }
2220
2221 struct CertIterator {
2222   GpgmeCtx ctx;  
2223   struct CertificateInfo info;
2224 };
2225
2226 struct CertIterator* 
2227 startListCertificates( const char* pattern, int remote )
2228 {
2229     GpgmeError err;
2230     struct CertIterator* it;
2231     /*fprintf( stderr,  "startListCertificates()" );*/
2232
2233     it = (struct CertIterator*)safe_malloc( sizeof( struct CertIterator ) );
2234
2235     err = gpgme_new (&(it->ctx));
2236     /*fprintf( stderr,  "2: gpgme returned %d\n", err );*/
2237     if( err != GPGME_No_Error ) {
2238       free( it );
2239       return NULL;
2240     }
2241
2242     gpgme_set_protocol (it->ctx, GPGME_PROTOCOL_CMS);
2243     if( remote ) gpgme_set_keylist_mode ( it->ctx, GPGME_KEYLIST_MODE_EXTERN ); 
2244     else gpgme_set_keylist_mode ( it->ctx, GPGME_KEYLIST_MODE_LOCAL );
2245     err =  gpgme_op_keylist_start ( it->ctx, pattern, 0);
2246     if( err != GPGME_No_Error ) {
2247       endListCertificates( it );
2248       return NULL;
2249     }
2250     memset( &(it->info), 0, sizeof( struct CertificateInfo ) );
2251     return it;
2252 }
2253
2254 /* free() each string in a char*[] and the array itself */
2255 static void 
2256 freeStringArray( char** c )
2257 {
2258     char** _c = c;
2259     while( c && *c ) {
2260       /*fprintf( stderr, "freeing \"%s\"\n", *c );*/
2261       safe_free( (void**)&(*c) );
2262       ++c;
2263     }
2264     safe_free( (void**)&_c );
2265 }
2266
2267 /* free all malloc'ed data in a struct CertificateInfo */
2268 static void 
2269 freeInfo( struct CertificateInfo* info )
2270 {
2271   struct DnPair* a = info->dnarray;
2272   assert( info );
2273   if( info->userid ) freeStringArray( info->userid );
2274   if( info->serial ) safe_free( (void**)&(info->serial) );
2275   if( info->fingerprint ) safe_free( (void**)&(info->fingerprint) );
2276   if( info->issuer ) safe_free( (void**)&(info->issuer) );
2277   if( info->chainid ) safe_free( (void**)&(info->chainid) );
2278   if( info->caps ) safe_free( (void**)&(info->caps) );
2279   while( a && a->key && a->value ) {
2280     safe_free ((void**)&(a->key));
2281     safe_free ((void**)&(a->value));
2282     ++a;
2283   }
2284   if( info->dnarray ) safe_free ((void**)&(info->dnarray));
2285   memset( info, 0, sizeof( *info ) );
2286 }
2287
2288 /* Format the fingerprint nicely. The caller should
2289    free the returned value with safe_free() */
2290 static char* make_fingerprint( const char* fpr )
2291 {
2292   int len = strlen(fpr);
2293   int i = 0;
2294   char* result = safe_malloc( (len + len/2 + 1)*sizeof(char) );
2295   if( !result ) return NULL;
2296   for(; *fpr; ++fpr, ++i ) {
2297     if( i%3 == 2) {
2298       result[i] = ':'; ++i;
2299     }
2300     result[i] = *fpr;
2301   }
2302   result[i] = 0;
2303   return result;
2304 }
2305
2306 int 
2307 nextCertificate( struct CertIterator* it, struct CertificateInfo** result )
2308 {
2309   GpgmeError err;
2310   GpgmeKey   key;
2311   int retval = GPGME_No_Error;
2312   assert( it );
2313   err = gpgme_op_keylist_next ( it->ctx, &key);
2314   if( err != GPGME_EOF ) {   
2315     int idx;
2316     const char* s;
2317     unsigned long u;
2318     char* names[MAX_GPGME_IDX+1];
2319     struct DnPair *issuer_dn, *tmp_dn;
2320     retval = err;
2321     memset( names, 0, sizeof( names ) );
2322     freeInfo( &(it->info) );
2323
2324     for( idx = 0; (s = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, 0, idx)) && idx < MAX_GPGME_IDX; 
2325          ++idx ) {
2326       names[idx] = xstrdup( s );
2327     }
2328     
2329     it->info.userid = safe_malloc( sizeof( char* ) * (idx+1) );
2330     memset( it->info.userid, 0, sizeof( char* ) * (idx+1) );
2331     it->info.dnarray = 0;
2332     for( idx = 0; names[idx] != 0; ++idx ) {
2333       struct DnPair* a = parse_dn( names[idx] ); 
2334       if( idx == 0 ) {
2335         it->info.userid[idx] = reorder_dn( a );
2336         it->info.dnarray = a;
2337         safe_free( (void **)&(names[idx]) );
2338       } else {
2339         it->info.userid[idx] = names[idx];
2340       }
2341     }
2342     it->info.userid[idx] = 0;
2343
2344     s = gpgme_key_get_string_attr (key, GPGME_ATTR_SERIAL, 0, 0); 
2345     it->info.serial = xstrdup(s);
2346
2347     s = gpgme_key_get_string_attr (key, GPGME_ATTR_FPR, 0, 0); 
2348     it->info.fingerprint = make_fingerprint( s );
2349
2350     s = gpgme_key_get_string_attr (key, GPGME_ATTR_ISSUER, 0, 0); 
2351     if( s ) {
2352       issuer_dn = tmp_dn = parse_dn( s );     
2353       /*it->info.issuer = xstrdup(s);*/
2354       it->info.issuer = reorder_dn( issuer_dn );
2355       while( tmp_dn->key ) {
2356         safe_free( (void**)&issuer_dn->key );
2357         safe_free( (void**)&issuer_dn->value );
2358         ++tmp_dn;
2359       }
2360       safe_free( (void**)&issuer_dn );
2361     } else {
2362       it->info.issuer = NULL;
2363     }
2364     s = gpgme_key_get_string_attr (key, GPGME_ATTR_CHAINID, 0, 0); 
2365     it->info.chainid = xstrdup(s);
2366
2367     s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEY_CAPS, 0, 0); 
2368     it->info.caps = xstrdup(s);
2369
2370     u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_CREATED, 0, 0); 
2371     it->info.created = u;
2372
2373     u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_EXPIRE, 0, 0); 
2374     it->info.expire = u;
2375
2376     u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_IS_SECRET, 0, 0); 
2377     it->info.secret = u;
2378
2379     u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_UID_INVALID, 0, 0); 
2380     it->info.invalid = u;
2381
2382     u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_KEY_EXPIRED, 0, 0); 
2383     it->info.expired = u;
2384
2385     u = gpgme_key_get_ulong_attr (key, GPGME_ATTR_KEY_DISABLED, 0, 0); 
2386     it->info.disabled = u;
2387
2388     gpgme_key_release (key);
2389     /*return &(it->info);*/
2390     *result =  &(it->info);
2391   } else *result = NULL;
2392   return retval;
2393 }
2394
2395 void 
2396 endListCertificates( struct CertIterator* it )
2397 {
2398   /*fprintf( stderr,  "endListCertificates()\n" );*/
2399   assert(it);
2400   freeInfo( &(it->info) );
2401   gpgme_op_keylist_end(it->ctx);
2402   gpgme_release (it->ctx);
2403   free( it );
2404 }
2405
2406 int
2407 importCertificate( const char* fingerprint )
2408 {
2409   GpgmeError err;
2410   GpgmeCtx  ctx;
2411   GpgmeData keydata;
2412   GpgmeRecipients recips;
2413   char* buf;
2414   const char* tmp1;
2415   char* tmp2;
2416
2417   err = gpgme_new( &ctx );
2418   /*fprintf( stderr,  "2: gpgme returned %d\n", err );*/
2419   if( err != GPGME_No_Error ) {
2420     return err;
2421   }
2422   gpgme_set_protocol( ctx, GPGME_PROTOCOL_CMS );
2423   gpgme_set_keylist_mode( ctx, GPGME_KEYLIST_MODE_LOCAL );
2424
2425   err = gpgme_data_new( &keydata );
2426   if( err ) {
2427     fprintf( stderr,  "gpgme_data_new returned %d\n", err );
2428     gpgme_release( ctx );
2429     return err;
2430   }
2431
2432   err = gpgme_recipients_new( &recips );
2433   if( err ) {
2434     fprintf( stderr,  "gpgme_recipients_new returned %d\n", err );
2435     gpgme_data_release( keydata );
2436     gpgme_release( ctx );
2437     return err;
2438   }
2439   
2440   buf = safe_malloc( sizeof(char)*( strlen( fingerprint ) + 1 ) );
2441   if( !buf ) {
2442     gpgme_recipients_release( recips );
2443     gpgme_data_release( keydata );    
2444     gpgme_release( ctx );
2445     return GPGME_Out_Of_Core;
2446   }
2447   tmp1 = fingerprint;
2448   tmp2 = buf;
2449   while( *tmp1 ) {
2450     if( *tmp1 != ':' ) *tmp2++ = *tmp1;
2451     tmp1++;
2452   }
2453   *tmp2 = 0;
2454   fprintf( stderr,  "calling gpgme_recipients_add_name( %s )\n", buf );  
2455   err = gpgme_recipients_add_name( recips, buf ); 
2456   if( err ) {
2457     fprintf( stderr,  "gpgme_recipients_add_name returned %d\n", err );
2458     safe_free( (void**)&buf );
2459     gpgme_recipients_release( recips );
2460     gpgme_data_release( keydata );    
2461     gpgme_release( ctx );
2462     return err;
2463   }
2464
2465   err = gpgme_op_export( ctx, recips, keydata );
2466   if( err ) {
2467     fprintf( stderr,  "gpgme_op_export returned %d\n", err );
2468     safe_free( (void**)&buf );
2469     gpgme_recipients_release( recips );
2470     gpgme_data_release( keydata );    
2471     gpgme_release( ctx );
2472     return err;
2473   }
2474   safe_free( (void**)&buf );
2475
2476   err = gpgme_op_import( ctx, keydata );
2477   if( err ) {    
2478     fprintf( stderr,  "gpgme_op_import returned %d\n", err );
2479     gpgme_recipients_release( recips );
2480     gpgme_data_release( keydata );    
2481     gpgme_release( ctx );
2482     return err;
2483   }
2484
2485   gpgme_recipients_release( recips );
2486   gpgme_data_release( keydata );    
2487   gpgme_release( ctx );
2488   return 0;
2489 }
2490
2491     // // // // // // // // // // // // // // // // // // // // // // // // //
2492    //                                                                      //
2493   //         Continuation of CryptPlug code                               //
2494  //                                                                      //
2495 // // // // // // // // // // // // // // // // // // // // // // // // //
2496
2497
2498 /*
2499   Find all certificate for a given addressee and return them in a
2500   '\1' separated list.
2501   NOTE: The certificate parameter must point to a not-yet allocated
2502         char*.  The function will allocate the memory needed and
2503         return the size in newSize.
2504   If secretOnly is true, only secret keys are returned.
2505 */
2506 bool findCertificates( const char* addressee,
2507                        char** certificates,
2508                        int* newSize,
2509                        bool secretOnly )
2510 {
2511   static int maxCerts = 1024;
2512   // use const char declarations since all of them are needed twice
2513   const char* delimiter = "\1";
2514   const char* openBracket = "    (";
2515   const char* closeBracket = ")";
2516
2517   GpgmeCtx ctx;
2518   GpgmeError err;
2519   GpgmeKey rKey;
2520   const char *s;
2521   const char *s2;
2522   char* dn;
2523   struct DnPair* a;
2524   int nFound = 0;
2525   int iFound = 0;
2526   int siz = 0;
2527   char* DNs[maxCerts];
2528   char* FPRs[maxCerts];
2529   
2530   if( ! certificates ){
2531     fprintf( stderr, "gpgme: findCertificates called with invalid *certificates pointer\n" );
2532     return false;
2533   }
2534
2535   if( ! newSize ){
2536     fprintf( stderr, "gpgme: findCertificates called with invalid newSize pointer\n" );
2537     return false;
2538   }
2539
2540   *certificates = 0;
2541   *newSize = 0;
2542   
2543   // calculate length of buffer needed for certs plus fingerprints
2544   memset( DNs,  0, sizeof( DNs  ) );
2545   memset( FPRs, 0, sizeof( FPRs ) );
2546   gpgme_new (&ctx);
2547   gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
2548   err = gpgme_op_keylist_start(ctx, addressee, secretOnly ? 1 : 0);
2549   while( GPGME_No_Error == err ) {
2550     err = gpgme_op_keylist_next(ctx, &rKey);
2551     if( GPGME_No_Error == err ) {
2552       s = gpgme_key_get_string_attr (rKey, GPGME_ATTR_USERID, NULL, 0);
2553       if( s ) {
2554         dn = xstrdup( s );
2555         s2 = gpgme_key_get_string_attr (rKey, GPGME_ATTR_FPR, NULL, 0);
2556         if( s2 ) {
2557           if( nFound )
2558             siz += strlen( delimiter );
2559           a = parse_dn( dn );
2560           free( dn );
2561           dn = reorder_dn( a );
2562           siz += strlen( dn );
2563           siz += strlen( openBracket );
2564           siz += strlen( s2 );
2565           siz += strlen( closeBracket );
2566           DNs[ nFound ] = xstrdup( dn );
2567           FPRs[nFound ] = xstrdup( s2 );
2568           ++nFound;
2569           if( nFound >= maxCerts ) {
2570             fprintf( stderr,
2571                      "gpgme: findCertificates found too many certificates (%d)\n",
2572                      maxCerts );
2573             break;
2574           }
2575         }
2576         free( dn );
2577       }
2578     }
2579   }
2580   gpgme_op_keylist_end( ctx );
2581   gpgme_release (ctx);
2582   
2583   
2584   if( 0 < siz ) {
2585     // add one for trailing ZERO char
2586     ++siz;
2587     *newSize = siz;
2588     // allocate the buffer
2589     *certificates = malloc(   sizeof(char) * siz );
2590     memset( *certificates, 0, sizeof(char) * siz );
2591     // fill the buffer
2592     for( iFound=0; iFound < nFound; ++iFound ) {
2593       if( !iFound )
2594         strcpy(*certificates, DNs[iFound] );
2595       else {
2596         strcat(*certificates, delimiter );
2597         strcat(*certificates, DNs[iFound] );
2598       }
2599       strcat(  *certificates, openBracket );
2600       strcat(  *certificates, FPRs[iFound] );
2601       strcat(  *certificates, closeBracket );
2602       ++iFound;
2603       free( DNs[ iFound ] );
2604       free( FPRs[iFound ] );
2605     }
2606   }
2607     
2608   return ( 0 < nFound );
2609 }