Signing with attachments does now work. w/o temp files.
[gpgol.git] / src / MapiGPGME.cpp
1 /* MapiGPGME.cpp - Mapi support with GPGME
2  *      Copyright (C) 2005 g10 Code GmbH
3  *
4  * This file is part of OutlGPG.
5  * 
6  * OutlGPG is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  * 
11  * OutlGPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  * 
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <windows.h>
27 #include <time.h>
28 #include <assert.h>
29 #include <string.h>
30
31 #ifdef __MINGW32__
32 # include "mymapi.h"
33 # include "mymapitags.h"
34 #else /* !__MINGW32__ */
35 # include <atlbase.h>
36 # include <mapidefs.h>
37 # include <mapiutil.h>
38 # include <initguid.h>
39 # include <mapiguid.h>
40 #endif /* !__MINGW32__ */
41
42 #include "gpgme.h"
43 #include "keycache.h"
44 #include "intern.h"
45 #include "HashTable.h"
46 #include "MapiGPGME.h"
47 #include "engine.h"
48
49
50 /* Attachment information. */
51 #define ATT_SIGN(action) ((action) & GPG_ATTACH_SIGN)
52 #define ATT_ENCR(action) ((action) & GPG_ATTACH_ENCRYPT)
53 #define ATT_PREFIX ".pgpenc"
54
55 #define DEFAULT_ATTACHMENT_FORMAT GPG_FMT_CLASSIC
56
57
58 /* default extension for attachments */
59 #define EXT_MSG "pgp"
60 #define EXT_SIG "sig"
61
62
63
64 /* Given that we are only able to configure one log file, it does not
65    make much sense t bind a log file to a specific instance of this
66    class. We use a global variable instead which will be set by any
67    instance.  The same goes for the respecive file pointer and the
68    enable_logging state. */
69 static char *logfile;
70 static FILE *logfp;
71 static bool enable_logging;
72
73 /* For certain operations we need to acquire a log on the logging
74    functions.  This lock is controlled by this Mutex. */
75 static HANDLE log_mutex;
76
77
78 static int lock_log (void);
79 static void unlock_log (void);
80
81
82 static HWND find_message_window (HWND parent, GpgMsg *msg);
83 static void log_key_info (MapiGPGME *g, const char *prefix,
84                           gpgme_key_t *keys, gpgme_key_t locusr);
85
86
87
88
89
90 /*
91    The implementation class of MapiGPGME.  
92  */
93 class MapiGPGMEImpl : public MapiGPGME
94 {
95 public:    
96   MapiGPGMEImpl () 
97   {
98     clearConfig ();
99     clearObject ();
100     this->passCache = new HashTable ();
101     op_init ();
102     prepareLogging ();
103     log_debug ("MapiGPGME.constructor: ready\n");
104   }
105
106   ~MapiGPGMEImpl ()
107   {
108     unsigned int i=0;
109
110     log_debug ("MapiGPGME.destructor: called\n");
111     op_deinit ();
112     xfree (defaultKey);
113     log_debug ("hash entries %d\n", passCache->size ());
114     for (i = 0; i < passCache->size (); i++) 
115       {
116         cache_item_t t = (cache_item_t)passCache->get (i);
117         if (t != NULL)
118           cache_item_free (t);
119       }
120     delete passCache; 
121     passCache = NULL;
122     freeAttachments ();
123     cleanupTempFiles ();
124   }
125
126   void __stdcall destroy ()
127   {
128     delete this;
129   }
130
131   void operator delete (void *p) 
132   {
133     ::operator delete (p);
134   }  
135
136   
137 public:
138   const char * __stdcall versionString (void)
139   {
140     return PACKAGE_VERSION;
141   }
142     
143   int __stdcall encrypt (HWND hwnd, GpgMsg *msg);
144   int __stdcall decrypt (HWND hwnd, GpgMsg *msg);
145   int __stdcall sign (HWND hwnd, GpgMsg *msg);
146   int __stdcall signEncrypt (HWND hwnd, GpgMsg *msg);
147   int __stdcall verify (HWND hwnd, GpgMsg *msg);
148   int __stdcall attachPublicKey (const char *keyid);
149
150   int __stdcall doCmdAttach(int action);
151   int __stdcall doCmdFile(int action, const char *in, const char *out);
152
153   const char* __stdcall getLogFile (void) { return logfile; }
154   void __stdcall setLogFile (const char *name)
155   { 
156     if (!lock_log ())
157       {
158         if (logfp)
159           {
160             fclose (logfp);
161             logfp = NULL;
162           }
163     
164         xfree (logfile);
165         logfile = name? xstrdup (name) : NULL;
166         unlock_log ();
167       }
168   }
169
170   int __stdcall getStorePasswdTime (void)
171   {
172     return nstorePasswd;
173   }
174
175   void __stdcall setStorePasswdTime (int nCacheTime)
176   {
177     this->nstorePasswd = nCacheTime; 
178   }
179
180   bool __stdcall getEncryptDefault (void)
181   {
182     return doEncrypt;
183   }
184
185   void __stdcall setEncryptDefault (bool doEncrypt)
186   {
187     this->doEncrypt = doEncrypt; 
188   }
189
190   bool __stdcall getSignDefault (void)
191   { 
192     return doSign; 
193   }
194
195   void __stdcall setSignDefault (bool doSign)
196   {
197     this->doSign = doSign;
198   }
199
200   bool __stdcall getEncryptWithDefaultKey (void)
201   {
202     return encryptDefault;
203   }
204   
205   void __stdcall setEncryptWithDefaultKey (bool encryptDefault)
206   {
207     this->encryptDefault = encryptDefault;
208   }
209
210   bool __stdcall getSaveDecryptedAttachments (void) 
211   { 
212     return saveDecryptedAtt;
213   }
214
215   void __stdcall setSaveDecryptedAttachments (bool saveDecrAtt)
216   {
217     this->saveDecryptedAtt = saveDecrAtt;
218   }
219
220   void __stdcall setEncodingFormat (int fmt)
221   {
222     encFormat = fmt; 
223   }
224
225   int __stdcall getEncodingFormat (void) 
226   {
227     return encFormat;
228   }
229
230   void __stdcall setSignAttachments (bool signAtt)
231   {
232     this->autoSignAtt = signAtt; 
233   }
234
235   bool __stdcall getSignAttachments (void)
236   {
237     return autoSignAtt;
238   }
239
240   void __stdcall setEnableLogging (bool val)
241   {
242     enable_logging = val;
243   }
244
245   bool __stdcall getEnableLogging (void)
246   {
247     return enable_logging;
248   }
249
250   int __stdcall readOptions (void);
251   int __stdcall writeOptions (void);
252
253   const char* __stdcall getAttachmentExtension (const char *fname);
254   void __stdcall freeAttachments (void);
255   
256
257   bool __stdcall deleteAttachment (GpgMsg * msg, int pos)
258   {
259 //     if (msg->DeleteAttach (pos, 0, NULL, 0) == S_OK)
260 //       return true;
261 //     return false;
262   }
263
264   LPATTACH __stdcall createAttachment (GpgMsg * msg, int &pos)
265   {
266     ULONG attnum;       
267     LPATTACH newatt = NULL;
268     
269 //     if (msg->CreateAttach (NULL, 0, &attnum, &newatt) == S_OK)
270 //       {
271 //         pos = attnum;
272 //         return newatt;
273 //       }
274     return NULL;
275   }
276
277   void  __stdcall showVersion (void);
278
279   int __stdcall startKeyManager ();
280   void __stdcall startConfigDialog (HWND parent);
281
282   void __stdcall setDefaultKey (const char *key);
283   const char * __stdcall getDefaultKey (void);
284
285   void __stdcall clearPassphrase (void) 
286   {
287     if (passCache != NULL)
288       passCache->clear ();
289   }
290
291
292 private:
293   char        *defaultKey; /* Malloced default key or NULL. */
294   HashTable   *passCache;
295   LPMAPITABLE attachTable;
296   LPSRowSet   attachRows;
297   void        *recipSet;
298
299   /* Options */
300   int     nstorePasswd;  /* Time in seconds the passphrase is stored. */
301   bool    encryptDefault;
302   bool    doEncrypt;
303   bool    doSign;
304   bool    saveDecryptedAtt; /* Save decrypted attachments. */
305   bool    autoSignAtt;      /* Sign all outgoing attachments. */
306   int     encFormat;        /* Encryption format for attachments. */
307
308   void displayError (HWND root, const char *title);
309   void prepareLogging (void)
310   {
311     char *val = NULL;
312     
313     load_extension_value ("logFile", &val);
314     if (val && *val != '\"' && *val)
315       {
316         setLogFile (val);
317         setEnableLogging (true);
318       }
319     xfree (val);        
320   }
321
322   void clearObject (void)
323   {
324     this->attachRows = NULL;
325     this->attachTable = NULL;
326     this->defaultKey = NULL;
327     this->recipSet = NULL;
328   }
329
330   void clearConfig (void)
331   {
332     nstorePasswd = 0;
333     doEncrypt = false;
334     doSign = false;
335     encryptDefault = false;
336     saveDecryptedAtt = false;
337     autoSignAtt = false;
338     encFormat = DEFAULT_ATTACHMENT_FORMAT;
339   }
340
341   void cleanupTempFiles ();
342   void freeUnknownKeys (char **unknown, int n);
343   void freeKeyArray (void **key);
344
345   bool  setAttachMethod (LPATTACH obj, int mode);
346   int   getAttachMethod (LPATTACH obj);
347   char* getAttachFilename (LPATTACH obj);
348   char* getAttachPathname (LPATTACH obj);
349   bool  setAttachFilename (LPATTACH obj, const char *name, bool islong);
350   int   getMessageFlags (GpgMsg *msg);
351   int   getMessageHasAttachments (GpgMsg *msg);
352   bool  setXHeader (GpgMsg *msg, const char *name, const char *val);
353   char* getXHeader (GpgMsg *msg, const char *name);
354   bool  checkAttachmentExtension (const char *ext);
355   const char* getPGPExtension (int action);
356   char* generateTempname (const char *name);
357   int   streamOnFile (const char *file, LPATTACH att);
358   int   streamFromFile (const char *file, LPATTACH att);
359   int   encryptAttachments (HWND hwnd);
360   LPATTACH openAttachment (GpgMsg *msg, int pos);
361   void  releaseAttachment (LPATTACH att);
362   int   processAttachment (LPATTACH *att, HWND hwnd, int pos, int action);
363   bool  signAttachment (const char *datfile);
364
365 };
366
367
368 /* Create an instance of the MapiGPGME class. */
369 MapiGPGME *
370 CreateMapiGPGME (void)
371 {
372   return new MapiGPGMEImpl ();
373 }
374
375
376 /* Early initialization of this module.  This is done right at startup
377    with only one thread running.  Should be called only once. Returns
378    0 on success. */
379 int
380 initialize_mapi_gpgme (void)
381 {
382   SECURITY_ATTRIBUTES sa;
383   
384   memset (&sa, 0, sizeof sa);
385   sa.bInheritHandle = FALSE;
386   sa.lpSecurityDescriptor = NULL;
387   sa.nLength = sizeof sa;
388   log_mutex = CreateMutex (&sa, FALSE, NULL);
389   return log_mutex? 0 : -1;
390 }
391
392 /* Acquire the mutex for logging.  Returns 0 on success. */
393 static int 
394 lock_log (void)
395 {
396   int code = WaitForSingleObject (log_mutex, INFINITE);
397   return code != WAIT_OBJECT_0;
398 }
399
400 /* Release the mutex for logging. No error return is done because this
401    is a fatal error anyway and we have no means for proper
402    notification. */
403 static void
404 unlock_log (void)
405 {
406   ReleaseMutex (log_mutex);
407 }
408
409
410
411
412 static void
413 do_log (const char *fmt, va_list a, int w32err, int err,
414         const void *buf, size_t buflen)
415 {
416   if (enable_logging == false || !logfile)
417     return;
418
419   if (!logfp)
420     {
421       if ( !lock_log ())
422         {
423           if (!logfp)
424             logfp = fopen (logfile, "a+");
425           unlock_log ();
426         }
427     }
428   if (!logfp)
429     return;
430   fprintf (logfp, "%lu/", (unsigned long)GetCurrentThreadId ());
431   if (err == 1)
432     fputs ("ERROR/", logfp);
433   vfprintf (logfp, fmt, a);
434   if (w32err) 
435     {
436       char buf[256];
437       
438       FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32err, 
439                      MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 
440                      buf, sizeof (buf)-1, NULL);
441       fputs (": ", logfp);
442       fputs (buf, logfp);
443     }
444   if (buf)
445     {
446       const unsigned char *p = (const unsigned char*)buf;
447
448       for ( ; buflen; buflen--, p++)
449         fprintf (logfp, "%02X", *p);
450       putc ('\n', logfp);
451     }
452   else if ( *fmt && fmt[strlen (fmt) - 1] != '\n')
453     putc ('\n', logfp);
454
455   fflush (logfp);
456 }
457
458
459 void 
460 log_debug (const char *fmt, ...)
461 {
462   va_list a;
463   
464   va_start (a, fmt);
465   do_log (fmt, a, 0, 0, NULL, 0);
466   va_end (a);
467 }
468
469 void 
470 log_error (const char *fmt, ...)
471 {
472   va_list a;
473   
474   va_start (a, fmt);
475   do_log (fmt, a, 0, 1, NULL, 0);
476   va_end (a);
477 }
478
479 void 
480 log_vdebug (const char *fmt, va_list a)
481 {
482   do_log (fmt, a, 0, 0, NULL, 0);
483 }
484
485
486 void 
487 log_debug_w32 (int w32err, const char *fmt, ...)
488 {
489   va_list a;
490
491   if (w32err == -1)
492       w32err = GetLastError ();
493   
494   va_start (a, fmt);
495   do_log (fmt, a, w32err, 0, NULL, 0);
496   va_end (a);
497 }
498
499 void 
500 log_error_w32 (int w32err, const char *fmt, ...)
501 {
502   va_list a;
503
504   if (w32err == -1)
505       w32err = GetLastError ();
506   
507   va_start (a, fmt);
508   do_log (fmt, a, w32err, 1, NULL, 0);
509   va_end (a);
510 }
511
512
513 void 
514 log_hexdump (const void *buf, size_t buflen, const char *fmt, ...)
515 {
516   va_list a;
517
518   va_start (a, fmt);
519   do_log (fmt, a, 0, 2, buf, buflen);
520   va_end (a);
521 }
522
523
524
525
526
527
528
529 void 
530 MapiGPGMEImpl::cleanupTempFiles (void)
531 {
532   HANDLE hd;
533   WIN32_FIND_DATA fnd;
534   char path[MAX_PATH+32], tmp[MAX_PATH+32];
535   
536   assert (strlen (ATT_PREFIX) + 2 < 16 /* just a reasonable value*/ );
537   
538   *path = 0;
539   GetTempPath (sizeof (path)-4, path);
540   if (*path && path[strlen (path)-1] != '\\')
541     strcat (path, "\\");
542   strcpy (tmp, path);
543   strcat (path, "*"ATT_PREFIX"*");
544   hd = FindFirstFile (path, &fnd);
545   if (hd == INVALID_HANDLE_VALUE)
546     return;
547
548   do
549     {
550       char *p = (char *)xmalloc (strlen (tmp) + strlen (fnd.cFileName) + 2);
551       strcpy (stpcpy (p, tmp), fnd.cFileName);
552       log_debug ("deleting temp file `%s'\n", p);
553       DeleteFile (p);
554       xfree (p);
555     } 
556   while (FindNextFile (hd, &fnd) == TRUE);
557   FindClose (hd);
558 }
559
560
561 /* Update the display using the message MSG.  Return 0 on success. */
562 static int
563 update_display (HWND hwnd, GpgMsg *msg)
564 {
565   HWND window;
566
567   window = find_message_window (hwnd, msg);
568   if (window)
569     {
570       log_debug ("%s:%s: window handle %p\n", __FILE__, __func__, window);
571       SetWindowText (window, msg->getDisplayText ());
572       log_debug ("%s:%s: window text is now `%s'",
573                  __FILE__, __func__, msg->getDisplayText ());
574       return 0;
575     }
576   else
577     {
578       log_debug ("%s: window handle not found for parent %p\n",
579                  __func__, hwnd);
580       return -1;
581     }
582 }
583
584
585 static bool 
586 is_html_body (const char *body)
587 {
588   char *p1, *p2;
589   
590   /* XXX: it is possible but unlikely that the message text
591      contains the used keywords. */
592   p1 = strstr (body, "<HTML>");
593   p2 = strstr (body, "</HTML>");
594   if (p1 && p2)
595     return true;
596   p1 = strstr (body, "<html>");
597   p2 = strstr (body, "</html>");
598   if (p1 && p2)
599         return true;
600   /* XXX: use case insentensive strstr version. */
601   return false;
602 }
603
604
605
606
607 void 
608 MapiGPGMEImpl::freeKeyArray (void **key)
609 {
610     gpgme_key_t *buf = (gpgme_key_t *)key;
611     int i=0;
612
613     if (buf == NULL)
614         return;
615     for (i = 0; buf[i] != NULL; i++) {
616         gpgme_key_release (buf[i]);
617         buf[i] = NULL;
618     }
619     xfree (buf);
620 }
621
622
623 /* Return the number of recipients in the array RECIPIENTS. */
624 static int 
625 count_recipients (char **recipients)
626 {
627   int i;
628   
629   for (i=0; recipients[i] != NULL; i++)
630     ;
631   return i;
632 }
633
634
635
636 void
637 MapiGPGMEImpl::freeUnknownKeys (char **unknown, int n)
638 {    
639     for (int i=0; i < n; i++) {
640         if (unknown[i] != NULL) {
641             xfree (unknown[i]);
642             unknown[i] = NULL;
643         }
644     }
645     if (n > 0)
646         xfree (unknown);
647 }
648
649
650 /* Release an array of strings with recipient names. */
651 static void
652 free_recipient_array (char **recipients)
653 {
654   int i;
655
656   if (recipients)
657     {
658       for (i=0; recipients[i]; i++) 
659         xfree (recipients[i]);  
660       xfree (recipients);
661     }
662 }
663
664
665
666 /* Create a new body from body wth suitable line endings. aller must
667    release the result. */
668 static char *
669 add_html_line_endings (const char *body)
670 {
671   size_t count;
672   const char *s;
673   char *p, *result;
674
675   for (count=0, s = body; *s; s++)
676     if (*s == '\n')
677       count++;
678   
679   result = (char*)xmalloc ((s - body) + count*10 + 1);
680   
681   for (s=body, p = result; *s; s++ )
682     if (*s == '\n')
683       p = stpcpy (p, "&nbsp;<br>\n");
684     else
685       *p++ = *s;
686   *p = 0;
687   
688   return result;
689   
690 }
691
692
693
694 int 
695 MapiGPGMEImpl::encrypt (HWND hwnd, GpgMsg *msg)
696 {
697   log_debug ("%s:%s: enter\n", __FILE__, __func__);
698   gpgme_key_t *keys = NULL;
699   gpgme_key_t *keys2 = NULL;
700   bool is_html;
701   const char *plaintext;
702   char *ciphertext;
703   char **recipients = msg->getRecipients ();
704   char **unknown = NULL;
705   int opts = 0;
706   int err = 0;
707   size_t all = 0;
708   int n;
709   
710
711   if (!msg || !*(plaintext = msg->getOrigText ())) 
712     {
713       free_recipient_array (recipients);
714       return 0;  /* Empty message - Nothing to encrypt. */
715     }
716
717   n = op_lookup_keys (recipients, &keys, &unknown, &all);
718   log_debug ("%s: found %d need %d (%p)\n", __func__, n, all, unknown);
719
720   if (n != count_recipients (recipients))
721     {
722       log_debug ("recipient_dialog_box2\n");
723       recipient_dialog_box2 (keys, unknown, all, &keys2, &opts);
724       xfree (keys);
725       keys = keys2;
726       if (opts & OPT_FLAG_CANCEL) 
727         {
728           free_recipient_array (recipients);
729           return 0;
730         }
731     }
732
733   err = op_encrypt ((void*)keys, plaintext, &ciphertext);
734   if (err)
735     MessageBox (NULL, op_strerror (err),
736                 "GPG Encryption", MB_ICONERROR|MB_OK);
737   else 
738     {
739       if (is_html) 
740         {
741           msg->setCipherText (add_html_line_endings (ciphertext), true);
742           ciphertext = NULL;
743         }
744       else
745         msg->setCipherText (ciphertext, false);
746       xfree (ciphertext);
747   }
748
749   free_recipient_array (recipients);
750   freeUnknownKeys (unknown, n);
751   if (!err && msg->hasAttachments ())
752     {
753       log_debug ("encrypt attachments\n");
754       recipSet = (void *)keys;
755       encryptAttachments (hwnd);
756     }
757   freeKeyArray ((void **)keys);
758   return err;
759 }
760
761
762 /* Decrypt the message MSG and update the window.  HWND identifies the
763    current window. */
764 int 
765 MapiGPGMEImpl::decrypt (HWND hwnd, GpgMsg * msg)
766 {
767   log_debug ("%s:%s: enter\n", __FILE__, __func__);
768   openpgp_t mtype;
769   char *plaintext = NULL;
770   int has_attach;
771   int err;
772
773   mtype = msg->getMessageType ();
774   if (mtype == OPENPGP_CLEARSIG)
775     {
776       log_debug ("%s:%s: leave (passing on to verify)\n", __FILE__, __func__);
777       return verify (hwnd, msg);
778     }
779
780   /* Check whether this possibly encrypted message as attachments.  We
781      check right now because we need to get into the decryptio code
782      even if the body is not encrypted but attachments are
783      available. FIXME: I am not sure whether this is the best
784      solution, we might want to skip the decryption step later and
785      also test for encrypted attachments right now.*/
786   has_attach = msg->hasAttachments ();
787
788   if (mtype == OPENPGP_NONE && !has_attach ) 
789     {
790       MessageBox (NULL, "No valid OpenPGP data found.",
791                   "GPG Decryption", MB_ICONERROR|MB_OK);
792       log_debug ("MapiGPGME.decrypt: leave (no OpenPGP data)\n");
793       return 0;
794     }
795
796   err = op_decrypt_start (msg->getOrigText (), &plaintext, nstorePasswd);
797
798   if (err)
799     {
800       if (has_attach && gpg_err_code (err) == GPG_ERR_NO_DATA)
801         ;
802       else
803         MessageBox (NULL, op_strerror (err),
804                     "GPG Decryption", MB_ICONERROR|MB_OK);
805     }
806   else if (plaintext && *plaintext)
807     {   
808       int is_html = is_html_body (plaintext);
809
810       msg->setPlainText (plaintext);
811       plaintext = NULL;
812
813       /* Also set PR_BODY but do not use 'SaveChanges' to make it
814          permanently.  This way the user can reply with the
815          plaintext but the ciphertext is still stored. */
816       log_debug ("decrypt isHtml=%d\n", is_html);
817
818       /* XXX: find a way to handle text/html message in a better way! */
819       if (is_html || update_display (hwnd, msg))
820         {
821           const char s[] = 
822             "The message text cannot be displayed.\n"
823             "You have to save the decrypted message to view it.\n"
824             "Then you need to re-open the message.\n\n"
825             "Do you want to save the decrypted message?";
826           int what;
827           
828           what = MessageBox (NULL, s, "GPG Decryption",
829                              MB_YESNO|MB_ICONWARNING);
830           if (what == IDYES) 
831             {
832               log_debug ("decrypt: saving plaintext message.\n");
833               msg->saveChanges (1);
834             }
835         }
836       else
837         msg->saveChanges (0);
838     }
839
840   if (has_attach)
841     {
842       unsigned int n;
843       
844       n = msg->getAttachments ();
845       log_debug ("%s:%s: message has %u attachments\n", __FILE__, __func__, n);
846       for (int i=0; i < n; i++) 
847         msg->decryptAttachment (hwnd, i, true);
848     }
849
850   log_debug ("%s:%s: leave (rc=%d)\n", __FILE__, __func__, err);
851   return err;
852 }
853
854
855 /* Sign the current message. Returns 0 on success. */
856 int
857 MapiGPGMEImpl::sign (HWND hwnd, GpgMsg * msg)
858 {
859   log_debug ("%s.%s: enter\n", __FILE__, __func__);
860   char *signedtext;
861   int err = 0;
862
863   /* We don't sign an empty body - a signature on a zero length string
864      is pretty much useless. */
865   if (!msg || !*msg->getOrigText ()) 
866     {
867       log_debug ("MapiGPGME.sign: leave (empty message)\n");
868       return 0;
869     }
870
871   err = op_sign_start (msg->getOrigText (), &signedtext);
872   if (err)
873     MessageBox (NULL, op_strerror (err), "GPG Sign", MB_ICONERROR|MB_OK);
874   else
875     {
876       msg->setSignedText (signedtext);
877     }
878   
879   if (autoSignAtt && msg->hasAttachments ())
880     {
881       unsigned int n;
882       
883       n = msg->getAttachments ();
884       log_debug ("%s:%s: message has %u attachments\n", __FILE__, __func__, n);
885       for (int i=0; i < n; i++) 
886         msg->signAttachment (hwnd, i, true);
887       /* FIXME: we should throw an error if signing of any attachment
888          failed. */
889     }
890
891
892   log_debug ("%s:%s: leave (err=%d)\n", __FILE__, __func__, err);
893   return err;
894 }
895
896
897 /* Perform a sign+encrypt operation using the data already store in
898    the instance variables. Return 0 on success. */
899 int
900 MapiGPGMEImpl::signEncrypt (HWND hwnd, GpgMsg *msg)
901 {
902   log_debug ("%s:%s: enter\n", __FILE__, __func__);
903   char **recipients = msg->getRecipients ();
904   char **unknown = NULL;
905   gpgme_key_t locusr=NULL, *keys = NULL, *keys2 =NULL;
906   char *ciphertext = NULL;
907   int is_html;
908
909   /* Check for trivial case: We don't encrypt or sign an empty
910      message. */
911   if (!msg || !*msg->getOrigText ()) 
912     {
913       free_recipient_array (recipients);
914       log_debug ("%s.%s: leave (empty message)\n", __FILE__, __func__);
915       return 0;  /* Empty message - Nothing to encrypt. */
916     }
917
918   /* Pop up a dialog box to ask for the signer of the message. */
919   if (signer_dialog_box (&locusr, NULL) == -1)
920     {
921       free_recipient_array (recipients);
922       log_debug ("%s.%s: leave (dialog failed)\n", __FILE__, __func__);
923       return 0;
924     }
925
926   /* Now lookup the keys of all recipients. */
927   size_t all;
928   int n = op_lookup_keys (recipients, &keys, &unknown, &all);
929   if (n != count_recipients (recipients)) 
930     {
931       /* FIXME: The implementation is not correct: op_lookup_keys
932          returns the number of missing keys but we compare against the
933          total number of keys; thus the box would pop up even when all
934          have been found. */
935         recipient_dialog_box2 (keys, unknown, all, &keys2, NULL);
936         xfree (keys);
937         keys = keys2;
938     }
939   
940   log_key_info (this, "signEncrypt", keys, locusr);
941
942   is_html = is_html_body (msg->getOrigText ());
943
944     /* FIXME: Remove the cast and use proper types. */
945   int err = op_sign_encrypt ((void *)keys, (void*)locusr,
946                              msg->getOrigText (), &ciphertext);
947   if (err)
948     MessageBox (NULL, op_strerror (err),
949                 "GPG Sign Encrypt", MB_ICONERROR|MB_OK);
950   else 
951     {
952       if (is_html) 
953         {
954           msg->setCipherText (add_html_line_endings (ciphertext), true);
955           ciphertext = NULL;
956         }
957       else
958         msg->setCipherText (ciphertext, false);
959       xfree (ciphertext);
960     }
961
962   freeUnknownKeys (unknown, n);
963   if (!err && msg->hasAttachments ())
964     {
965       log_debug ("%s:%s: have attachments\n", __FILE__, __func__);
966       recipSet = (void *)keys;
967       encryptAttachments (hwnd);
968     }
969   freeKeyArray ((void **)keys);
970   gpgme_key_release (locusr);
971   free_recipient_array (recipients);
972   log_debug ("%s:%s: leave (rc=%d)\n", err);
973   return err;
974 }
975
976
977 int 
978 MapiGPGMEImpl::verify (HWND hwnd, GpgMsg *msg)
979 {
980   log_debug ("%s:%s: enter\n", __FILE__, __func__);
981   openpgp_t mtype;
982   int has_attach;
983   
984   mtype = msg->getMessageType ();
985   has_attach = msg->hasAttachments ();
986   if (mtype == OPENPGP_NONE && !has_attach ) 
987     {
988       log_debug ("%s:%s: leave (no OpenPGP data)\n", __FILE__, __func__);
989       return 0;
990     }
991
992   int err = op_verify_start (msg->getOrigText (), NULL);
993   if (err)
994     MessageBox (NULL, op_strerror (err), "GPG Verify", MB_ICONERROR|MB_OK);
995   else
996     update_display (hwnd, msg);
997
998   log_debug ("%s:%s: leave (rc=%d)\n", __FILE__, __func__, err);
999   return err;
1000 }
1001
1002
1003
1004
1005 int
1006 MapiGPGMEImpl::doCmdFile(int action, const char *in, const char *out)
1007 {
1008     log_debug ( "doCmdFile action=%d in=%s out=%s\n", action, in, out);
1009     if (ATT_SIGN (action) && ATT_ENCR (action))
1010         return !op_sign_encrypt_file (recipSet, in, out);
1011     if (ATT_SIGN (action) && !ATT_ENCR (action))
1012         return !op_sign_file (OP_SIG_NORMAL, in, out, nstorePasswd);
1013     if (!ATT_SIGN (action) && ATT_ENCR (action))
1014         return !op_encrypt_file (recipSet, in, out);
1015     return !op_decrypt_file (in, out);    
1016 }
1017
1018
1019 int
1020 MapiGPGMEImpl::doCmdAttach (int action)
1021 {
1022 //     log_debug ("doCmdAttach action=%d\n", action);
1023 //     if (ATT_SIGN (action) && ATT_ENCR (action))
1024 //      return signEncrypt ();
1025 //     if (ATT_SIGN (action) && !ATT_ENCR (action))
1026 //      return sign ();
1027 //     if (!ATT_SIGN (action) && ATT_ENCR (action))
1028 //      return encrypt ();
1029 // FIXME!!!
1030   return 0; /*decrypt ();*/
1031 }
1032
1033
1034
1035 static const char *
1036 userid_from_key (gpgme_key_t k)
1037 {
1038   if (k && k->uids && k->uids->uid)
1039     return k->uids->uid;
1040   else
1041     return "?";
1042 }
1043
1044 static const char *
1045 keyid_from_key (gpgme_key_t k)
1046 {
1047   
1048   if (k && k->subkeys && k->subkeys->keyid)
1049     return k->subkeys->keyid;
1050   else
1051     return "????????";
1052 }
1053
1054 /* Print information on the keys in KEYS and LOCUSR. */
1055 static void 
1056 log_key_info (MapiGPGME *g, const char *prefix,
1057               gpgme_key_t *keys, gpgme_key_t locusr)
1058 {
1059     if (locusr)
1060       log_debug ("MapiGPGME.%s: signer: 0x%s %s\n", prefix,
1061                    keyid_from_key (locusr), userid_from_key (locusr));
1062     
1063     else
1064       log_debug ("MapiGPGME.%s: no signer\n", prefix);
1065     
1066     gpgme_key_t n;
1067     int i;
1068
1069     if (keys == NULL)
1070         return;
1071     i=0;
1072     for (n=keys[0]; keys[i] != NULL; i++)
1073       log_debug ("MapiGPGME.%s: recp.%d 0x%s %s\n", prefix,
1074                    i, keyid_from_key (keys[i]), userid_from_key (keys[i]));
1075 }
1076             
1077
1078
1079 void 
1080 MapiGPGMEImpl::setDefaultKey (const char *key)
1081 {
1082     xfree (defaultKey);
1083     defaultKey = xstrdup (key);
1084 }
1085
1086
1087 const char* 
1088 MapiGPGMEImpl::getDefaultKey (void)
1089 {
1090   return defaultKey;
1091 }
1092
1093
1094
1095 /* We need this to find the mailer window because we directly change
1096    the text of the window instead of the MAPI object itself. */
1097 static HWND
1098 find_message_window (HWND parent, GpgMsg *msg)
1099 {
1100   HWND child;
1101
1102   if (!parent)
1103     return NULL;
1104
1105   child = GetWindow (parent, GW_CHILD);
1106   while (child)
1107     {
1108       char buf[1024+1];
1109       HWND w;
1110
1111       memset (buf, 0, sizeof (buf));
1112       GetWindowText (child, buf, sizeof (buf)-1);
1113       if (msg->matchesString (buf))
1114         return child;
1115       w = find_message_window (child, msg);
1116       if (w)
1117         return w;
1118       child = GetNextWindow (child, GW_HWNDNEXT);       
1119     }
1120
1121   return NULL;
1122 }
1123
1124
1125 int
1126 MapiGPGMEImpl::streamFromFile (const char *file, LPATTACH att)
1127 {
1128     HRESULT hr;
1129     LPSTREAM to = NULL, from = NULL;
1130     STATSTG statInfo;
1131
1132     hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 0,
1133                               MAPI_CREATE | MAPI_MODIFY, (LPUNKNOWN*) &to);
1134     if (FAILED (hr))
1135         return FALSE;
1136
1137     hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
1138                            STGM_READ, (char*)file, NULL, &from);
1139     if (!SUCCEEDED (hr)) {
1140         to->Release ();
1141         log_debug ( "streamFromFile %s failed.\n", file);
1142         return FALSE;
1143     }
1144     from->Stat (&statInfo, STATFLAG_NONAME);
1145     from->CopyTo (to, statInfo.cbSize, NULL, NULL);
1146     to->Commit (0);
1147     to->Release ();
1148     from->Release ();
1149     log_debug ( "streamFromFile %s succeeded\n", file);
1150     return TRUE;
1151 }
1152
1153
1154 int
1155 MapiGPGMEImpl::streamOnFile (const char *file, LPATTACH att)
1156 {
1157     HRESULT hr;
1158     LPSTREAM from = NULL, to = NULL;
1159     STATSTG statInfo;
1160
1161     hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 
1162                             0, 0, (LPUNKNOWN*) &from);
1163     if (FAILED (hr))
1164         return FALSE;
1165
1166     hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
1167                            STGM_CREATE | STGM_READWRITE, (char*) file,
1168                            NULL, &to);
1169     if (!SUCCEEDED (hr)) {
1170         from->Release ();
1171         log_debug ( "streamOnFile %s failed with %s\n", file, 
1172                     hr == MAPI_E_NO_ACCESS? 
1173                     "no access" : hr == MAPI_E_NOT_FOUND? "not found" : "unknown");
1174         return FALSE;
1175     }
1176     from->Stat (&statInfo, STATFLAG_NONAME);
1177     from->CopyTo (to, statInfo.cbSize, NULL, NULL);
1178     to->Commit (0);
1179     to->Release ();
1180     from->Release ();
1181     log_debug ( "streamOnFile %s succeeded\n", file);
1182     return TRUE;
1183 }
1184
1185
1186 int
1187 MapiGPGMEImpl::getMessageFlags (GpgMsg * msg)
1188 {
1189     HRESULT hr;
1190     LPSPropValue propval = NULL;
1191     int flags = 0;
1192
1193     // FIXME
1194 //     hr = HrGetOneProp (msg, PR_MESSAGE_FLAGS, &propval);
1195     if (FAILED (hr))
1196         return 0;
1197     flags = propval->Value.l;
1198     MAPIFreeBuffer (propval);
1199     return flags;
1200 }
1201
1202
1203 int
1204 MapiGPGMEImpl::getMessageHasAttachments (GpgMsg * msg)
1205 {
1206     HRESULT hr;
1207     LPSPropValue propval = NULL;
1208     int nattach = 0;
1209
1210     // FIXME
1211 //     hr = HrGetOneProp (msg, PR_HASATTACH, &propval);
1212     if (FAILED (hr))
1213         return 0;
1214     nattach = propval->Value.b? 1 : 0;
1215     MAPIFreeBuffer (propval);
1216     return nattach;   
1217 }
1218
1219
1220
1221 bool
1222 MapiGPGMEImpl::setAttachMethod (LPATTACH obj, int mode)
1223 {
1224     SPropValue prop;
1225     HRESULT hr;
1226     prop.ulPropTag = PR_ATTACH_METHOD;
1227     prop.Value.ul = mode;
1228     hr = HrSetOneProp (obj, &prop);
1229     return FAILED (hr)? true : false;
1230 }
1231
1232
1233 int
1234 MapiGPGMEImpl::getAttachMethod (LPATTACH obj)
1235 {
1236     HRESULT hr;
1237     LPSPropValue propval = NULL;
1238     int method = 0;
1239
1240     hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_METHOD, &propval);
1241     if (FAILED (hr))
1242         return 0;
1243     method = propval->Value.ul;
1244     MAPIFreeBuffer (propval);
1245     return method;
1246 }
1247
1248 bool
1249 MapiGPGMEImpl::setAttachFilename (LPATTACH obj, const char *name, bool islong)
1250 {
1251     HRESULT hr;
1252     SPropValue prop;
1253     prop.ulPropTag = PR_ATTACH_LONG_FILENAME;
1254
1255     if (!islong)
1256         prop.ulPropTag = PR_ATTACH_FILENAME;
1257     prop.Value.lpszA = (char*) name;   
1258     hr = HrSetOneProp (obj, &prop);
1259     return FAILED (hr)? false: true;
1260 }
1261
1262
1263 char*
1264 MapiGPGMEImpl::getAttachPathname (LPATTACH obj)
1265 {
1266     LPSPropValue propval;
1267     HRESULT hr;
1268     char *path;
1269
1270     hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_LONG_PATHNAME, &propval);
1271     if (FAILED (hr)) {
1272         hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_PATHNAME, &propval);
1273         if (SUCCEEDED (hr)) {
1274             path = xstrdup (propval[0].Value.lpszA);
1275             MAPIFreeBuffer (propval);
1276         }
1277         else
1278             return NULL;
1279     }
1280     else {
1281         path = xstrdup (propval[0].Value.lpszA);
1282         MAPIFreeBuffer (propval);
1283     }
1284     return path;
1285 }
1286
1287
1288 char*
1289 MapiGPGMEImpl::getAttachFilename (LPATTACH obj)
1290 {
1291     LPSPropValue propval;
1292     HRESULT hr;
1293     char *name;
1294
1295     hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_LONG_FILENAME, &propval);
1296     if (FAILED(hr)) {
1297         hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_FILENAME, &propval);
1298         if (SUCCEEDED (hr)) {
1299             name = xstrdup (propval[0].Value.lpszA);
1300             MAPIFreeBuffer (propval);
1301         }
1302         else
1303             return NULL;
1304     }
1305     else {
1306         name = xstrdup (propval[0].Value.lpszA);
1307         MAPIFreeBuffer (propval);
1308     }
1309     return name;
1310 }
1311
1312
1313 bool
1314 MapiGPGMEImpl::checkAttachmentExtension (const char *ext)
1315 {
1316     if (ext == NULL)
1317         return false;
1318     if (*ext == '.')
1319         ext++;
1320     log_debug ( "checkAttachmentExtension: %s\n", ext);
1321     if (stricmp (ext, "gpg") == 0 ||
1322         stricmp (ext, "pgp") == 0 ||
1323         stricmp (ext, "asc") == 0)
1324         return true;
1325     return false;
1326 }
1327
1328
1329 const char*
1330 MapiGPGMEImpl::getAttachmentExtension (const char *fname)
1331 {
1332     static char ext[4];
1333     char *p;
1334
1335     p = strrchr (fname, '.');
1336     if (p != NULL) {
1337         /* XXX: what if the extension is < 3 chars */
1338         strncpy (ext, p, 4);
1339         if (checkAttachmentExtension (ext))
1340             return ext;
1341     }
1342     return EXT_MSG;
1343 }
1344
1345
1346 const char*
1347 MapiGPGMEImpl::getPGPExtension (int action)
1348 {
1349     if (ATT_SIGN (action))
1350         return EXT_SIG;
1351     return EXT_MSG;
1352 }
1353
1354
1355 bool 
1356 MapiGPGMEImpl::setXHeader (GpgMsg * msg, const char *name, const char *val)
1357 {  
1358 #ifndef __MINGW32__
1359     USES_CONVERSION;
1360 #endif
1361     LPMDB lpMdb = NULL;
1362     HRESULT hr = 0;
1363     LPSPropTagArray pProps = NULL;
1364     SPropValue pv;
1365     MAPINAMEID mnid[1]; 
1366     // {00020386-0000-0000-C000-000000000046}  ->  GUID For X-Headers   
1367     GUID guid = {0x00020386, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00,
1368                  0x00, 0x00, 0x00, 0x46} };
1369
1370     memset (&mnid[0], 0, sizeof (MAPINAMEID));
1371     mnid[0].lpguid = &guid;
1372     mnid[0].ulKind = MNID_STRING;
1373 //     mnid[0].Kind.lpwstrName = A2W (name);
1374 // FIXME
1375 //    hr = msg->GetIDsFromNames (1, (LPMAPINAMEID*)mnid, MAPI_CREATE, &pProps);
1376     if (FAILED (hr)) {
1377         log_debug ("set X-Header failed.\n");
1378         return false;
1379     }
1380     
1381     pv.ulPropTag = (pProps->aulPropTag[0] & 0xFFFF0000) | PT_STRING8;
1382     pv.Value.lpszA = (char *)val;
1383 //FIXME     hr = HrSetOneProp(msg, &pv);        
1384     if (!SUCCEEDED (hr)) {
1385         log_debug ("set X-Header failed.\n");
1386         return false;
1387     }
1388
1389     log_debug ("set X-Header succeeded.\n");
1390     return true;
1391 }
1392
1393
1394 char*
1395 MapiGPGMEImpl::getXHeader (GpgMsg *msg, const char *name)
1396 {
1397     /* XXX: PR_TRANSPORT_HEADERS is not available in my MSDN. */
1398     return NULL;
1399 }
1400
1401
1402 void
1403 MapiGPGMEImpl::freeAttachments (void)
1404 {
1405     if (attachTable != NULL) {
1406         attachTable->Release ();
1407         attachTable = NULL;
1408     }
1409     if (attachRows != NULL) {
1410         FreeProws (attachRows);
1411         attachRows = NULL;
1412     }
1413 }
1414
1415
1416
1417
1418 void
1419 MapiGPGMEImpl::releaseAttachment (LPATTACH att)
1420 {
1421     att->Release ();
1422 }
1423
1424
1425
1426 char*
1427 MapiGPGMEImpl::generateTempname (const char *name)
1428 {
1429     char temp[MAX_PATH+2];
1430     char *p;
1431
1432     GetTempPath (sizeof (temp)-1, temp);
1433     if (temp[strlen (temp)-1] != '\\')
1434         strcat (temp, "\\");
1435     p = (char *)xcalloc (1, strlen (temp) + strlen (name) + 16);
1436     sprintf (p, "%s%s", temp, name);
1437     return p;
1438 }
1439
1440
1441 bool
1442 MapiGPGMEImpl::signAttachment (const char *datfile)
1443 {
1444     char *sigfile;
1445     LPATTACH newatt;
1446     int pos=0, err=0;
1447
1448     log_debug ("MapiGPGME.%s:%d: enter\n", __func__, __LINE__);
1449
1450     sigfile = (char *)xcalloc (1,strlen (datfile)+5);
1451     strcpy (sigfile, datfile);
1452     strcat (sigfile, ".asc");
1453
1454     newatt = createAttachment (NULL/*FIXME*/, pos);
1455     setAttachMethod (newatt, ATTACH_BY_VALUE);
1456     setAttachFilename (newatt, sigfile, false);
1457
1458     err = op_sign_file (OP_SIG_DETACH, datfile, sigfile, nstorePasswd);
1459
1460     if (streamFromFile (sigfile, newatt)) {
1461         log_debug ("signAttachment: commit changes.\n");
1462         newatt->SaveChanges (FORCE_SAVE);
1463     }
1464     releaseAttachment (newatt);
1465     xfree (sigfile);
1466
1467     log_debug ("MapiGPGME.%s: leave (rc=%d)\n", __func__, err);
1468
1469     return (!err)? true : false;
1470 }
1471
1472 /* XXX: find a way to see if the attachment is already secured. This could be
1473         done by watching at the extension or checking the first lines. */
1474 int
1475 MapiGPGMEImpl::processAttachment (LPATTACH *attm, HWND hwnd,
1476                                   int pos, int action)
1477 {    
1478     LPATTACH att = *attm;
1479     int method = getAttachMethod (att);
1480     BOOL success = TRUE;
1481     HRESULT hr;
1482
1483     /* XXX: sign-only code is still not very intuitive. */
1484
1485     if (action == GPG_ATTACH_NONE)
1486         return FALSE;
1487     if (action == GPG_ATTACH_DECRYPT && !saveDecryptedAtt)
1488         return TRUE;
1489
1490     switch (method) {
1491     case ATTACH_EMBEDDED_MSG:
1492         LPMESSAGE emb;
1493
1494         /* we do not support to sign these kind of attachments. */
1495         if (action == GPG_ATTACH_SIGN)
1496             return TRUE;
1497         hr = att->OpenProperty (PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, 
1498                                 MAPI_MODIFY, (LPUNKNOWN*) &emb);
1499         if (FAILED (hr))
1500             return FALSE;
1501 //FIXME         setWindow (hwnd);
1502 //      setMessage (emb);
1503         if (doCmdAttach (action))
1504             success = FALSE;
1505         emb->SaveChanges (FORCE_SAVE);
1506         att->SaveChanges (FORCE_SAVE);
1507         emb->Release ();
1508         break;
1509
1510     case ATTACH_BY_VALUE:
1511         char *inname;
1512         char *outname;
1513         char *tmp;
1514
1515         tmp = getAttachFilename (att);
1516         inname =  generateTempname (tmp);
1517         log_debug ("enc inname: '%s'\n", inname);
1518         if (action != GPG_ATTACH_DECRYPT) {
1519             char *tmp2 = (char *)xcalloc (1, strlen (inname) 
1520                                              + strlen (ATT_PREFIX) + 4 + 1);
1521             sprintf (tmp2, "%s"ATT_PREFIX".%s", tmp, getPGPExtension (action));
1522             outname = generateTempname (tmp2);
1523             xfree (tmp2);
1524             log_debug ( "enc outname: '%s'\n", outname);
1525         }
1526         else {
1527             if (checkAttachmentExtension (strrchr (tmp, '.')) == false) {
1528                 log_debug ( "%s: no pgp extension found.\n", tmp);
1529                 xfree (tmp);
1530                 xfree (inname);
1531                 return TRUE;
1532             }
1533             char *tmp2 = (char*)xcalloc (1, strlen (tmp) + 4);
1534             strcpy (tmp2, tmp);
1535             tmp2[strlen (tmp2) - 4] = '\0';
1536             outname = generateTempname (tmp2);
1537             xfree (tmp2);
1538             log_debug ("dec outname: '%s'\n", outname);
1539         }
1540         success = FALSE;
1541         /* if we are in sign-only mode, just create a detached signature
1542            for each attachment but do not alter the attachment data itself. */
1543         if (action != GPG_ATTACH_SIGN && streamOnFile (inname, att)) {
1544             if (doCmdFile (action, inname, outname))
1545                 success = TRUE;
1546             else
1547                 log_debug ( "doCmdFile failed\n");
1548         }
1549         if ((action == GPG_ATTACH_ENCRYPT || action == GPG_ATTACH_SIGN) 
1550             && autoSignAtt)
1551             signAttachment (inname);
1552
1553         /*DeleteFile (inname);*/
1554         /* XXX: the file does not seemed to be closed. */
1555         xfree (inname);
1556         xfree (tmp);
1557         
1558         if (action != GPG_ATTACH_SIGN)
1559             deleteAttachment (NULL/*FIXME*/,pos);
1560
1561         if (action == GPG_ATTACH_ENCRYPT) {
1562             LPATTACH newatt;
1563             *attm = newatt = createAttachment (NULL/*FIXME*/,pos);
1564             setAttachMethod (newatt, ATTACH_BY_VALUE);
1565             setAttachFilename (newatt, outname, false);
1566
1567             if (streamFromFile (outname, newatt)) {
1568                 log_debug ( "commit changes.\n");           
1569                 newatt->SaveChanges (FORCE_SAVE);
1570             }
1571         }
1572         else if (success && action == GPG_ATTACH_DECRYPT) {
1573           //        success = saveDecryptedAttachment (NULL, outname);
1574             log_debug ("saveDecryptedAttachment ec=%d\n", success);
1575         }
1576         DeleteFile (outname);
1577         xfree (outname);
1578         releaseAttachment (att);
1579         break;
1580
1581     case ATTACH_BY_REF_ONLY:
1582         break;
1583
1584     case ATTACH_OLE:
1585         break;
1586
1587     }
1588
1589     return success;
1590 }
1591
1592
1593
1594
1595
1596 int
1597 MapiGPGMEImpl::encryptAttachments (HWND hwnd)
1598 {    
1599     unsigned int n;
1600
1601 //     n = if (!getAttachments ())
1602 //      return FALSE;
1603     n = 0 /*FIXMEcountAttachments ()*/;
1604     log_debug ("enc: mail has %d attachments\n", n);
1605     if (!n) {
1606         freeAttachments ();
1607         return TRUE;
1608     }
1609     for (int i=0; i < n; i++) {
1610       LPATTACH amsg = openAttachment (NULL/*FIXME*/,i);
1611         if (amsg == NULL)
1612             continue;
1613         processAttachment (&amsg, hwnd, i, GPG_ATTACH_ENCRYPT);
1614         releaseAttachment (amsg);       
1615     }
1616     freeAttachments ();
1617     return 0;
1618 }
1619
1620
1621
1622 void  
1623 MapiGPGMEImpl::showVersion (void)
1624 {
1625   /* Not yet available. */
1626 }
1627
1628
1629 int
1630 MapiGPGMEImpl::startKeyManager (void)
1631 {
1632     return start_key_manager ();
1633 }
1634
1635
1636 void
1637 MapiGPGMEImpl::startConfigDialog (HWND parent)
1638 {
1639     config_dialog_box (parent);
1640 }
1641
1642
1643 int
1644 MapiGPGMEImpl::readOptions (void)
1645 {
1646     char *val=NULL;
1647
1648     load_extension_value ("autoSignAttachments", &val);
1649     autoSignAtt = val == NULL || *val != '1' ? 0 : 1;
1650     xfree (val); val =NULL;
1651
1652     load_extension_value ("saveDecryptedAttachments", &val);
1653     saveDecryptedAtt = val == NULL || *val != '1'? 0 : 1;
1654     xfree (val); val =NULL;
1655
1656     load_extension_value ("encryptDefault", &val);
1657     doEncrypt = val == NULL || *val != '1'? 0 : 1;
1658     xfree (val); val=NULL;
1659
1660     load_extension_value ("signDefault", &val);
1661     doSign = val == NULL || *val != '1'? 0 : 1;
1662     xfree (val); val = NULL;
1663
1664     load_extension_value ("addDefaultKey", &val);
1665     encryptDefault = val == NULL || *val != '1' ? 0 : 1;
1666     xfree (val); val = NULL;
1667
1668     load_extension_value ("storePasswdTime", &val);
1669     nstorePasswd = val == NULL || *val == '0'? 0 : atol (val);
1670     xfree (val); val = NULL;
1671
1672     load_extension_value ("encodingFormat", &val);
1673     encFormat = val == NULL? GPG_FMT_CLASSIC  : atol (val);
1674     xfree (val); val = NULL;
1675
1676     load_extension_value ("logFile", &val);
1677     if (val == NULL ||*val == '"' || *val == 0) {
1678         setLogFile (NULL);
1679     }
1680     else {
1681         setLogFile (val);
1682         setEnableLogging (true);
1683     }
1684     xfree (val); val=NULL;
1685
1686     load_extension_value ("defaultKey", &val);
1687     if (val == NULL || *val == '"') {
1688         encryptDefault = 0;
1689         xfree (defaultKey);
1690         defaultKey = NULL;
1691     }
1692     else {
1693         setDefaultKey (val);
1694         encryptDefault = 1;
1695     }
1696
1697     xfree (val); val=NULL;
1698
1699     return 0;
1700 }
1701
1702
1703 void
1704 MapiGPGMEImpl::displayError (HWND root, const char *title)
1705 {       
1706     char buf[256];
1707     
1708     FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (), 
1709                    MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 
1710                    buf, sizeof (buf)-1, NULL);
1711     MessageBox (root, buf, title, MB_OK|MB_ICONERROR);
1712 }
1713
1714
1715 int
1716 MapiGPGMEImpl::writeOptions (void)
1717 {
1718     struct conf {
1719         const char *name;
1720         bool value;
1721     };
1722     struct conf opt[] = {
1723         {"encryptDefault", doEncrypt},
1724         {"signDefault", doSign},
1725         {"addDefaultKey", encryptDefault},
1726         {"saveDecryptedAttachments", saveDecryptedAtt},
1727         {"autoSignAttachments", autoSignAtt},
1728         {NULL, 0}
1729     };
1730     char buf[32];
1731
1732     for (int i=0; opt[i].name != NULL; i++) {
1733         int rc = store_extension_value (opt[i].name, opt[i].value? "1": "0");
1734         if (rc)
1735             displayError (NULL, "Save options in the registry");
1736         /* XXX: also show the name of the value */
1737     }
1738
1739     if (logfile != NULL)
1740         store_extension_value ("logFile", logfile);
1741     if (defaultKey != NULL)
1742         store_extension_value ("defaultKey", defaultKey);
1743     
1744     sprintf (buf, "%d", nstorePasswd);
1745     store_extension_value ("storePasswdTime", buf);
1746     
1747     sprintf (buf, "%d", encFormat);
1748     store_extension_value ("encodingFormat", buf);
1749
1750     return 0;
1751 }
1752
1753
1754 int 
1755 MapiGPGMEImpl::attachPublicKey (const char *keyid)
1756 {
1757     /* @untested@ */
1758     const char *patt[1];
1759     char *keyfile;
1760     int err, pos = 0;
1761     LPATTACH newatt;
1762
1763     keyfile = generateTempname (keyid);
1764     patt[0] = xstrdup (keyid);
1765     err = op_export_keys (patt, keyfile);
1766
1767     newatt = createAttachment (NULL/*FIXME*/,pos);
1768     setAttachMethod (newatt, ATTACH_BY_VALUE);
1769     setAttachFilename (newatt, keyfile, false);
1770     /* XXX: set proper RFC3156 MIME types. */
1771
1772     if (streamFromFile (keyfile, newatt)) {
1773         log_debug ("attachPublicKey: commit changes.\n");
1774         newatt->SaveChanges (FORCE_SAVE);
1775     }
1776     releaseAttachment (newatt);
1777     xfree (keyfile);
1778     xfree ((void *)patt[0]);
1779     return err;
1780 }