More chnages, in particular made text/plain with foo.asc
[gpgol.git] / src / gpgmsg.cpp
1 /* gpgmsg.cpp - Implementation ofthe GpgMsg class
2  *      Copyright (C) 2005 g10 Code GmbH
3  *
4  * This file is part of GPGol.
5  * 
6  * GPGol 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  * GPGol 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 <assert.h>
28 #include <string.h>
29
30 #include "mymapi.h"
31 #include "mymapitags.h"
32
33 #include "intern.h"
34 #include "gpgmsg.hh"
35 #include "util.h"
36 #include "msgcache.h"
37 #include "engine.h"
38 #include "display.h"
39
40 static const char oid_mimetag[] =
41   {0x2A, 0x86, 0x48, 0x86, 0xf7, 0x14, 0x03, 0x0a, 0x04};
42
43
44 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
45                                        __FILE__, __func__, __LINE__); \
46                         } while (0)
47
48 /* Constants to describe the PGP armor types. */
49 typedef enum 
50   {
51     ARMOR_NONE = 0,
52     ARMOR_MESSAGE,
53     ARMOR_SIGNATURE,
54     ARMOR_SIGNED,
55     ARMOR_FILE,     
56     ARMOR_PUBKEY,
57     ARMOR_SECKEY
58   }
59 armor_t;
60
61
62 struct attach_info
63 {
64   int end_of_table;  /* True if this is the last plus one entry of the
65                         table. */
66   int is_encrypted;  /* This is an encrypted attchment. */
67   int is_signed;     /* This is a signed attachment. */
68   unsigned int sig_pos; /* For signed attachments the index of the
69                            attachment with the detached signature. */
70   
71   int method;        /* MAPI attachmend method. */
72   char *filename;    /* Malloced filename of this attachment or NULL. */
73   char *content_type;/* Malloced string with the mime attrib or NULL.
74                         Parameters are stripped off thus a compare
75                         against "type/subtype" is sufficient. */
76   const char *content_type_parms; /* If not NULL the parameters of the
77                                      content_type. */
78   armor_t armor_type;   /* 0 or the type of the PGP armor. */
79 };
80 typedef struct attach_info *attach_info_t;
81
82
83 static int get_attach_method (LPATTACH obj);
84 static bool set_x_header (LPMESSAGE msg, const char *name, const char *val);
85
86
87
88 /*
89    The implementation class of MapiGPGME.  
90  */
91 class GpgMsgImpl : public GpgMsg
92 {
93 public:    
94   GpgMsgImpl () 
95   {
96     message = NULL;
97     exchange_cb = NULL;
98     body = NULL;
99     body_plain = NULL;
100     is_pgpmime = false;
101     silent = false;
102
103     attach.att_table = NULL;
104     attach.rows = NULL;
105   }
106
107   ~GpgMsgImpl ()
108   {
109     if (message)
110       message->Release ();
111     xfree (body);
112     xfree (body_plain);
113
114     if (attach.att_table)
115       {
116         attach.att_table->Release ();
117         attach.att_table = NULL;
118       }
119     if (attach.rows)
120       {
121         FreeProws (attach.rows);
122         attach.rows = NULL;
123       }
124   }
125
126   void destroy ()
127   {
128     delete this;
129   }
130
131   void operator delete (void *p) 
132   {
133     ::operator delete (p);
134   }
135
136   void setMapiMessage (LPMESSAGE msg)
137   {
138     if (message)
139       {
140         message->Release ();
141         message = NULL;
142       }
143     if (msg)
144       {
145         msg->AddRef ();
146         message = msg;
147       }
148   }
149
150   /* Set the callback for Exchange. */
151   void setExchangeCallback (void *cb)
152   {
153     exchange_cb = cb;
154   }
155   
156   void setSilent (bool value)
157   {
158     silent = value;
159   }
160
161   openpgp_t getMessageType (void);
162   bool hasAttachments (void);
163   const char *getOrigText (void);
164   const char *GpgMsgImpl::getDisplayText (void);
165   const char *getPlainText (void);
166
167   int decrypt (HWND hwnd);
168   int sign (HWND hwnd);
169   int encrypt (HWND hwnd)
170   {
171     return encrypt_and_sign (hwnd, false);
172   }
173   int signEncrypt (HWND hwnd)
174   {
175     return encrypt_and_sign (hwnd, true);
176   }
177   int attachPublicKey (const char *keyid);
178
179   char **getRecipients (void);
180   unsigned int getAttachments (void);
181   void verifyAttachment (HWND hwnd, attach_info_t table,
182                          unsigned int pos_data,
183                          unsigned int pos_sig);
184   void decryptAttachment (HWND hwnd, int pos, bool save_plaintext, int ttl,
185                           const char *filename);
186   void signAttachment (HWND hwnd, int pos, gpgme_key_t sign_key, int ttl);
187   int encryptAttachment (HWND hwnd, int pos, gpgme_key_t *keys,
188                          gpgme_key_t sign_key, int ttl);
189
190
191 private:
192   LPMESSAGE message;  /* Pointer to the message. */
193   void *exchange_cb;  /* Call back used with the display function. */
194   char *body;         /* utf-8 encoded body string or NULL. */
195   char *body_plain;   /* Plaintext version of BODY or NULL. */
196   bool is_pgpmime;    /* True if the message is a PGP/MIME encrypted one. */
197   bool silent;        /* Don't pop up message boxes.  Currently this
198                          is only used with decryption. g*/
199
200   /* This structure collects the information about attachments. */
201   struct 
202   {
203     LPMAPITABLE att_table;/* The loaded attachment table or NULL. */
204     LPSRowSet   rows;     /* The retrieved set of rows from the table. */
205   } attach;
206   
207   void loadBody (void);
208   bool isPgpmimeVersionPart (int pos);
209   attach_info_t gatherAttachmentInfo (void);
210   int encrypt_and_sign (HWND hwnd, bool sign);
211 };
212
213
214 /* Return a new instance and initialize with the MAPI message object
215    MSG. */
216 GpgMsg *
217 CreateGpgMsg (LPMESSAGE msg)
218 {
219   GpgMsg *m = new GpgMsgImpl ();
220   if (!m)
221     out_of_core ();
222   m->setMapiMessage (msg);
223   return m;
224 }
225
226
227 /* Release an array of GPGME keys. */
228 static void 
229 free_key_array (gpgme_key_t *keys)
230 {
231   if (keys)
232     {
233       for (int i = 0; keys[i]; i++) 
234         gpgme_key_release (keys[i]);
235       xfree (keys);
236     }
237 }
238
239 /* Release an array of strings with recipient names. */
240 static void
241 free_recipient_array (char **recipients)
242 {
243   int i;
244
245   if (recipients)
246     {
247       for (i=0; recipients[i]; i++) 
248         xfree (recipients[i]);  
249       xfree (recipients);
250     }
251 }
252
253 /* Release a table with attachments infos. */
254 static void
255 release_attach_info (attach_info_t table)
256 {
257   int i;
258
259   if (!table)
260     return;
261   for (i=0; !table[i].end_of_table; i++)
262     {
263       xfree (table[i].filename);
264       xfree (table[i].content_type);
265     }
266   xfree (table);
267 }
268
269
270 /* Return the number of recipients in the array RECIPIENTS. */
271 static int 
272 count_recipients (char **recipients)
273 {
274   int i;
275   
276   for (i=0; recipients[i] != NULL; i++)
277     ;
278   return i;
279 }
280
281
282 /* Return a string suitable for displaying in a message box.  The
283    function takes FORMAT and replaces the string "@LIST@" with the
284    names of the attachmets. Depending on the set bits in WHAT only
285    certain attachments are inserted. 
286
287    Defined bits in MODE are:
288       0 = Any attachment
289       1 = signed attachments
290       2 = encrypted attachments
291
292    Caller must free the returned value.  Routine is guaranteed to
293    return a string.
294 */
295 static char *
296 text_from_attach_info (attach_info_t table, const char *format,
297                        unsigned int what)
298 {
299   int pos;
300   size_t length;
301   char *buffer, *p;
302   const char *marker;
303
304   marker = strstr (format, "@LIST@");
305   if (!marker)
306     return xstrdup (format);
307
308 #define CONDITION  (table[pos].filename \
309                     && ( (what&1) \
310                          || ((what & 2) && table[pos].is_signed) \
311                          || ((what & 4) && table[pos].is_encrypted)))
312
313   for (length=0, pos=0; !table[pos].end_of_table; pos++)
314     if (CONDITION)
315       length += 2 + strlen (table[pos].filename) + 1;
316
317   length += strlen (format);
318   buffer = p = (char*)xmalloc (length+1);
319
320   strncpy (p, format, marker - format);
321   p += marker - format;
322
323   for (pos=0; !table[pos].end_of_table; pos++)
324     if (CONDITION)
325       {
326         if (table[pos].is_signed)
327           p = stpcpy (p, "S ");
328         else if (table[pos].is_encrypted)
329           p = stpcpy (p, "E ");
330         else
331           p = stpcpy (p, "* ");
332         p = stpcpy (p, table[pos].filename);
333         p = stpcpy (p, "\n");
334       }
335   strcpy (p, marker+6);
336 #undef CONDITION
337
338   return buffer;
339 }
340
341
342 \f
343 /* Load the body and make it available as an UTF8 string in the
344    instance variable BODY.  */
345 void
346 GpgMsgImpl::loadBody (void)
347 {
348   HRESULT hr;
349   LPSPropValue lpspvFEID = NULL;
350   LPSTREAM stream;
351 //   SPropValue prop;
352   STATSTG statInfo;
353   ULONG nread;
354
355   if (body || !message)
356     return;
357
358   hr = HrGetOneProp ((LPMAPIPROP)message, PR_BODY, &lpspvFEID);
359   if (SUCCEEDED (hr))
360     { /* Message is small enough to be retrieved this way. */
361       switch ( PROP_TYPE (lpspvFEID->ulPropTag) )
362         {
363         case PT_UNICODE:
364           body = wchar_to_utf8 (lpspvFEID->Value.lpszW);
365           if (!body)
366             log_debug ("%s: error converting to utf8\n", __func__);
367           break;
368           
369         case PT_STRING8:
370           body = xstrdup (lpspvFEID->Value.lpszA);
371           break;
372           
373         default:
374           log_debug ("%s: proptag=0x%08lx not supported\n",
375                      __func__, lpspvFEID->ulPropTag);
376           break;
377         }
378       MAPIFreeBuffer (lpspvFEID);
379     }
380   else /* Message is large; Use a stream to read it. */
381     {
382       hr = message->OpenProperty (PR_BODY, &IID_IStream,
383                                   0, 0, (LPUNKNOWN*)&stream);
384       if ( hr != S_OK )
385         {
386           log_debug ("%s:%s: OpenProperty failed: hr=%#lx",
387                      __FILE__, __func__, hr);
388           return;
389         }
390       
391       hr = stream->Stat (&statInfo, STATFLAG_NONAME);
392       if ( hr != S_OK )
393         {
394           log_debug ("%s:%s: Stat failed: hr=%#lx", __FILE__, __func__, hr);
395           stream->Release ();
396           return;
397         }
398       
399       /* Fixme: We might want to read only the first 1k to decide
400          whether this is actually an OpenPGP message and only then
401          continue reading.  This requires some changes in this
402          module. */
403       body = (char*)xmalloc ((size_t)statInfo.cbSize.QuadPart + 2);
404       hr = stream->Read (body, (size_t)statInfo.cbSize.QuadPart, &nread);
405       if ( hr != S_OK )
406         {
407           log_debug ("%s:%s: Read failed: hr=%#lx", __FILE__, __func__, hr);
408           xfree (body);
409           body = NULL;
410           stream->Release ();
411           return;
412         }
413       body[nread] = 0;
414       body[nread+1] = 0;
415       if (nread != statInfo.cbSize.QuadPart)
416         {
417           log_debug ("%s:%s: not enough bytes returned\n", __FILE__, __func__);
418           xfree (body);
419           body = NULL;
420           stream->Release ();
421           return;
422         }
423       stream->Release ();
424       
425       /* FIXME: We need to optimize this. */
426       {
427         char *tmp;
428         tmp = wchar_to_utf8 ((wchar_t*)body);
429         if (!tmp)
430           log_debug ("%s: error converting to utf8\n", __func__);
431         else
432           {
433             xfree (body);
434             body = tmp;
435           }
436       }
437     }
438
439   if (body)
440     log_debug ("%s:%s: loaded body `%s' at %p\n",
441                __FILE__, __func__, body, body);
442   
443
444 //   prop.ulPropTag = PR_ACCESS;
445 //   prop.Value.l = MAPI_ACCESS_MODIFY;
446 //   hr = HrSetOneProp (message, &prop);
447 //   if (FAILED (hr))
448 //     log_debug ("%s:%s: updating message access to 0x%08lx failed: hr=%#lx",
449 //                    __FILE__, __func__, prop.Value.l, hr);
450 }
451
452
453 /* Return the subject of the message or NULL if it does not
454    exists.  Caller must free. */
455 #if 0
456 static char *
457 get_subject (LPMESSAGE obj)
458 {
459   HRESULT hr;
460   LPSPropValue propval = NULL;
461   char *name;
462
463   hr = HrGetOneProp ((LPMAPIPROP)obj, PR_SUBJECT, &propval);
464   if (FAILED (hr))
465     {
466       log_error ("%s:%s: error getting the subject: hr=%#lx",
467                  __FILE__, __func__, hr);
468       return NULL; 
469     }
470   switch ( PROP_TYPE (propval->ulPropTag) )
471     {
472     case PT_UNICODE:
473       name = wchar_to_utf8 (propval->Value.lpszW);
474       if (!name)
475         log_debug ("%s:%s: error converting to utf8\n", __FILE__, __func__);
476       break;
477       
478     case PT_STRING8:
479       name = xstrdup (propval->Value.lpszA);
480       break;
481       
482     default:
483       log_debug ("%s:%s: proptag=%#lx not supported\n",
484                  __FILE__, __func__, propval->ulPropTag);
485       name = NULL;
486       break;
487     }
488   MAPIFreeBuffer (propval);
489   return name;
490 }
491 #endif
492
493 /* Set the subject of the message OBJ to STRING. Returns 0 on
494    success. */
495 #if 0
496 static int
497 set_subject (LPMESSAGE obj, const char *string)
498 {
499   HRESULT hr;
500   SPropValue prop;
501   const char *s;
502   
503   /* Decide whether we ned to use the Unicode version. */
504   for (s=string; *s && !(*s & 0x80); s++)
505     ;
506   if (*s)
507     {
508       prop.ulPropTag = PR_SUBJECT_W;
509       prop.Value.lpszW = utf8_to_wchar (string);
510       hr = HrSetOneProp (obj, &prop);
511       xfree (prop.Value.lpszW);
512     }
513   else /* Only plain ASCII. */
514     {
515       prop.ulPropTag = PR_SUBJECT_A;
516       prop.Value.lpszA = (CHAR*)string;
517       hr = HrSetOneProp (obj, &prop);
518     }
519   if (hr != S_OK)
520     {
521       log_debug ("%s:%s: HrSetOneProp failed: hr=%#lx\n",
522                  __FILE__, __func__, hr); 
523       return gpg_error (GPG_ERR_GENERAL);
524     }
525   return 0;
526 }
527 #endif
528
529
530 /* Return the type of a message. */
531 openpgp_t
532 GpgMsgImpl::getMessageType (void)
533 {
534   const char *s;
535   
536   loadBody ();
537   
538   if (!body || !(s = strstr (body, "BEGIN PGP ")))
539     return OPENPGP_NONE;
540
541   /* (The extra strstr() above is just a simple optimization.) */
542   if (strstr (body, "BEGIN PGP MESSAGE"))
543     return OPENPGP_MSG;
544   else if (strstr (body, "BEGIN PGP SIGNED MESSAGE"))
545     return OPENPGP_CLEARSIG;
546   else if (strstr (body, "BEGIN PGP SIGNATURE"))
547     return OPENPGP_SIG;
548   else if (strstr (body, "BEGIN PGP PUBLIC KEY"))
549     return OPENPGP_PUBKEY;
550   else if (strstr (body, "BEGIN PGP PRIVATE KEY"))
551     return OPENPGP_SECKEY;
552   else
553     return OPENPGP_NONE;
554 }
555
556
557 /* Return the body text as received or composed.  This is guaranteed
558    to never return NULL.  */
559 const char *
560 GpgMsgImpl::getOrigText ()
561 {
562   loadBody ();
563   
564   return body? body : "";
565 }
566
567
568 /* Return the text of the message to be used for the display.  The
569    message objects has intrinsic knowledge about the correct text.  */
570 const char *
571 GpgMsgImpl::getDisplayText (void)
572 {
573   loadBody ();
574
575   if (body_plain)
576     return body_plain;
577   else if (body)
578     return body;
579   else
580     return "";
581 }
582
583
584
585 /* Return an array of strings with the recipients of the message. On
586    success a malloced array is returned containing allocated strings
587    for each recipient.  The end of the array is marked by NULL.
588    Caller is responsible for releasing the array.  On failure NULL is
589    returned.  */
590 char ** 
591 GpgMsgImpl::getRecipients ()
592 {
593   static SizedSPropTagArray (1L, PropRecipientNum) = {1L, {PR_EMAIL_ADDRESS}};
594   HRESULT hr;
595   LPMAPITABLE lpRecipientTable = NULL;
596   LPSRowSet lpRecipientRows = NULL;
597   char **rset;
598   int i, j;
599
600   if (!message)
601     return NULL;
602
603   hr = message->GetRecipientTable (0, &lpRecipientTable);
604   if (FAILED (hr)) 
605     {
606       log_debug_w32 (-1, "%s:%s: GetRecipientTable failed",
607                      __FILE__, __func__);
608       return NULL;
609     }
610
611   hr = HrQueryAllRows (lpRecipientTable, (LPSPropTagArray) &PropRecipientNum,
612                        NULL, NULL, 0L, &lpRecipientRows);
613   if (FAILED (hr)) 
614     {
615       log_debug_w32 (-1, "%s:%s: GHrQueryAllRows failed", __FILE__, __func__);
616       if (lpRecipientTable)
617         lpRecipientTable->Release();
618       return NULL;
619     }
620
621   rset = (char**)xcalloc (lpRecipientRows->cRows+1, sizeof *rset);
622
623   for (i = j = 0; (unsigned int)i < lpRecipientRows->cRows; i++)
624     {
625       LPSPropValue row;
626
627       if (!lpRecipientRows->aRow[j].cValues)
628         continue;
629       row = lpRecipientRows->aRow[j].lpProps;
630
631       switch ( PROP_TYPE (row->ulPropTag) )
632         {
633         case PT_UNICODE:
634           rset[j] = wchar_to_utf8 (row->Value.lpszW);
635           if (rset[j])
636             j++;
637           else
638             log_debug ("%s:%s: error converting recipient to utf8\n",
639                        __FILE__, __func__);
640           break;
641       
642         case PT_STRING8: /* Assume Ascii. */
643           rset[j++] = xstrdup (row->Value.lpszA);
644           break;
645           
646         default:
647           log_debug ("%s:%s: proptag=0x%08lx not supported\n",
648                      __FILE__, __func__, row->ulPropTag);
649           break;
650         }
651     }
652   rset[j] = NULL;
653
654   if (lpRecipientTable)
655     lpRecipientTable->Release();
656   if (lpRecipientRows)
657     FreeProws(lpRecipientRows); 
658   
659   log_debug ("%s:%s: got %d recipients:\n",
660              __FILE__, __func__, j);
661   for (i=0; rset[i]; i++)
662     log_debug ("%s:%s: \t`%s'\n", __FILE__, __func__, rset[i]);
663
664   return rset;
665 }
666
667
668
669
670 /* Decrypt the message MSG and update the window.  HWND identifies the
671    current window. */
672 int 
673 GpgMsgImpl::decrypt (HWND hwnd)
674 {
675   log_debug ("%s:%s: enter\n", __FILE__, __func__);
676   openpgp_t mtype;
677   char *plaintext = NULL;
678   attach_info_t table = NULL;
679   int err;
680   unsigned int pos;
681   unsigned int n_attach = 0;
682   unsigned int n_encrypted = 0;
683   unsigned int n_signed = 0;
684   HRESULT hr;
685   int pgpmime_succeeded = 0;
686
687   mtype = getMessageType ();
688
689   /* Check whether this possibly encrypted message has encrypted
690      attachments.  We check right now because we need to get into the
691      decryption code even if the body is not encrypted but attachments
692      are available. */
693   table = gatherAttachmentInfo ();
694   if (table)
695     {
696       for (pos=0; !table[pos].end_of_table; pos++)
697         if (table[pos].is_encrypted)
698           n_encrypted++;
699         else if (table[pos].is_signed)
700           n_signed++;
701       n_attach = pos;
702     }
703   log_debug ("%s:%s: message has %u attachments with "
704              "%u signed and %d encrypted\n",
705              __FILE__, __func__, n_attach, n_signed, n_encrypted);
706   if (mtype == OPENPGP_NONE && !n_encrypted && !n_signed) 
707     {
708       /* Because we usually work around the OL object model, it can't
709          notice that we changed the windows's text behind its back (by
710          means of update_display and the SetWindowText API).  Thus it
711          happens sometimes that the ciphertext is still displayed
712          although the MAPI calls in loadBody returned the plaintext
713          (becuase we once used set_message_body).  The effect is that
714          when cliching the decrypt button, we won't have any
715          ciphertext to decrypt and thus get to here.  We try solving
716          this by updating the window if we also have a cached entry.
717
718          Another solution would be to always update the windows's text
719          using a cached plaintext (in OnRead). I have some fear that
720          this might lead to unexpected behaviour in certain cases, so
721          we better only do it on demand and only if the old reply hack
722          has been enabled. */
723       void *refhandle;
724       const char *s;
725
726       if (!opt.compat.old_reply_hack
727           && (s = msgcache_get_from_mapi (message, &refhandle)))
728         {
729           xfree (body_plain);
730           body_plain = xstrdup (s);
731           update_display (hwnd, this, exchange_cb);
732           msgcache_unref (refhandle);
733           log_debug ("%s:%s: leave (already decrypted)\n", __FILE__, __func__);
734         }
735       else
736         {
737           MessageBox (hwnd, "No valid OpenPGP data found.",
738                       "GPG Decryption", MB_ICONWARNING|MB_OK);
739           log_debug ("%s:%s: leave (no OpenPGP data)\n", __FILE__, __func__);
740         }
741       
742       release_attach_info (table);
743       return 0;
744     }
745
746   if (is_pgpmime)
747     {
748       LPATTACH att;
749       int method;
750       LPSTREAM from;
751       
752       hr = message->OpenAttach (1, NULL, MAPI_BEST_ACCESS, &att);       
753       if (FAILED (hr))
754         {
755           log_error ("%s:%s: can't open PGP/MIME attachment 2: hr=%#lx",
756                      __FILE__, __func__, hr);
757           MessageBox (hwnd, "Problem decrypting PGP/MIME message",
758                       "GPG Decryption", MB_ICONERROR|MB_OK);
759           log_debug ("%s:%s: leave (PGP/MIME problem)\n", __FILE__, __func__);
760           release_attach_info (table);
761           return gpg_error (GPG_ERR_GENERAL);
762         }
763
764       method = get_attach_method (att);
765       if (method != ATTACH_BY_VALUE)
766         {
767           log_error ("%s:%s: unsupported method %d for PGP/MIME attachment 2",
768                      __FILE__, __func__, method);
769           MessageBox (hwnd, "Problem decrypting PGP/MIME message",
770                       "GPG Decryption", MB_ICONERROR|MB_OK);
771           log_debug ("%s:%s: leave (bad PGP/MIME method)\n",__FILE__,__func__);
772           att->Release ();
773           release_attach_info (table);
774           return gpg_error (GPG_ERR_GENERAL);
775         }
776
777       hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 
778                               0, 0, (LPUNKNOWN*) &from);
779       if (FAILED (hr))
780         {
781           log_error ("%s:%s: can't open data of attachment 2: hr=%#lx",
782                      __FILE__, __func__, hr);
783           MessageBox (hwnd, "Problem decrypting PGP/MIME message",
784                       "GPG Decryption", MB_ICONERROR|MB_OK);
785           log_debug ("%s:%s: leave (OpenProperty failed)\n",__FILE__,__func__);
786           att->Release ();
787           release_attach_info (table);
788           return gpg_error (GPG_ERR_GENERAL);
789         }
790
791       err = op_decrypt_stream_to_buffer (from, &plaintext,
792                                          opt.passwd_ttl, NULL);
793       from->Release ();
794       att->Release ();
795       if (!err)
796         pgpmime_succeeded = 1;
797     }
798   else if (mtype == OPENPGP_CLEARSIG)
799     err = op_verify (getOrigText (), NULL, NULL);
800   else if (*getOrigText())
801     err = op_decrypt (getOrigText (), &plaintext, opt.passwd_ttl, NULL);
802   else
803     err = gpg_error (GPG_ERR_NO_DATA);
804   if (err)
805     {
806       if (!is_pgpmime && n_attach && gpg_err_code (err) == GPG_ERR_NO_DATA)
807         ;
808       else if (mtype == OPENPGP_CLEARSIG)
809         MessageBox (hwnd, op_strerror (err),
810                     "GPG verification failed", MB_ICONERROR|MB_OK);
811       else
812         MessageBox (hwnd, op_strerror (err),
813                     "GPG decryption failed", MB_ICONERROR|MB_OK);
814     }
815   else if (plaintext && *plaintext)
816     {   
817       int is_html = is_html_body (plaintext);
818
819       log_debug ("decrypt isHtml=%d\n", is_html);
820
821       /* Do we really need to set the body?  update_display below
822          should be sufficient.  The problem witgh this is that we have
823          changes in the MAPI and OL will later ask whether to save
824          them.  The original reason for this kludge was to get the
825          plaintext into the reply (by setting the property without
826          calling SaveChanges) - with OL2003 it didn't worked reliable
827          and thus we implemented the trick with the msgcache. For now
828          we will disable it but add a compatibility flag to re-enable
829          it. */
830       if (opt.compat.old_reply_hack)
831         set_message_body (message, plaintext);
832
833       xfree (body_plain);
834       body_plain = plaintext;
835       plaintext = NULL;
836       msgcache_put (body_plain, 0, message);
837
838       /* XXX: find a way to handle text/html message in a better way! */
839       /* I have disabled the kludge to see what happens to a html
840          message. */
841       if (!silent && /*is_html ||*/ update_display (hwnd, this, exchange_cb)) 
842         {
843           const char s[] = 
844             "The message text cannot be displayed.\n"
845             "You have to save the decrypted message to view it.\n"
846             "Then you need to re-open the message.\n\n"
847             "Do you want to save the decrypted message?";
848           int what;
849           
850           what = MessageBox (hwnd, s, "GPG Decryption",
851                              MB_YESNO|MB_ICONWARNING);
852           if (what == IDYES) 
853             {
854               log_debug ("decrypt: saving plaintext message.\n");
855               hr = message->SaveChanges (KEEP_OPEN_READWRITE|FORCE_SAVE);
856               if (FAILED (hr))
857                 log_debug ("%s:%s: SaveChanges failed: hr=%#lx",
858                            __FILE__, __func__, hr);
859             }
860         }
861     }
862
863
864   /* If we have signed attachments.  Ask whether the signatures should
865      be verified; we do this is case of large attachments where
866      verification might take long. */
867   if (!silent && n_signed && !pgpmime_succeeded)
868     {
869       const char s[] = 
870         "Signed attachments found.\n\n"
871         "@LIST@\n"
872         "Do you want to verify the signatures?";
873       int what;
874       char *text;
875
876       text = text_from_attach_info (table, s, 2);
877       
878       what = MessageBox (hwnd, text, "Attachment Verification",
879                          MB_YESNO|MB_ICONINFORMATION);
880       xfree (text);
881       if (what == IDYES) 
882         {
883           for (pos=0; !table[pos].end_of_table; pos++)
884             if (table[pos].is_signed)
885               {
886                 assert (table[pos].sig_pos < n_attach);
887                 verifyAttachment (hwnd, table, pos, table[pos].sig_pos);
888               }
889         }
890     }
891
892   if (!silent && n_encrypted && !pgpmime_succeeded)
893     {
894       const char s[] = 
895         "Encrypted attachments found.\n\n"
896         "@LIST@\n"
897         "Do you want to decrypt and save them?";
898       int what;
899       char *text;
900
901       text = text_from_attach_info (table, s, 4);
902       what = MessageBox (hwnd, text, "Attachment Decryption",
903                          MB_YESNO|MB_ICONINFORMATION);
904       xfree (text);
905       if (what == IDYES) 
906         {
907           for (pos=0; !table[pos].end_of_table; pos++)
908             if (table[pos].is_encrypted)
909               decryptAttachment (hwnd, pos, true, opt.passwd_ttl,
910                                  table[pos].filename);
911         }
912     }
913
914   release_attach_info (table);
915   log_debug ("%s:%s: leave (rc=%d)\n", __FILE__, __func__, err);
916   return err;
917 }
918
919
920
921
922 \f
923 /* Sign the current message. Returns 0 on success. */
924 int
925 GpgMsgImpl::sign (HWND hwnd)
926 {
927   const char *plaintext;
928   char *signedtext = NULL;
929   int err = 0;
930   gpgme_key_t sign_key = NULL;
931
932   log_debug ("%s:%s: enter message=%p\n", __FILE__, __func__, message);
933   
934   /* We don't sign an empty body - a signature on a zero length string
935      is pretty much useless. */
936   if (!*(plaintext = getOrigText ()) && !hasAttachments ()) 
937     {
938       log_debug ("%s:%s: leave (empty)", __FILE__, __func__);
939       return 0; 
940     }
941
942   /* Pop up a dialog box to ask for the signer of the message. */
943   if (signer_dialog_box (&sign_key, NULL) == -1)
944     {
945       log_debug ("%s.%s: leave (dialog failed)\n", __FILE__, __func__);
946       return gpg_error (GPG_ERR_CANCELED);  
947     }
948
949   if (*plaintext)
950     {
951       err = op_sign (plaintext, &signedtext, 
952                      OP_SIG_CLEAR, sign_key, opt.passwd_ttl);
953       if (err)
954         {
955           MessageBox (hwnd, op_strerror (err),
956                       "GPG Sign", MB_ICONERROR|MB_OK);
957           goto leave;
958         }
959     }
960
961   if (opt.auto_sign_attach && hasAttachments ())
962     {
963       unsigned int n;
964       
965       n = getAttachments ();
966       log_debug ("%s:%s: message has %u attachments\n", __FILE__, __func__, n);
967       for (unsigned int i=0; i < n; i++) 
968         signAttachment (hwnd, i, sign_key, opt.passwd_ttl);
969       /* FIXME: we should throw an error if signing of any attachment
970          failed. */
971     }
972
973   set_x_header (message, "GPGol-Version", PACKAGE_VERSION);
974
975   /* Now that we successfully processed the attachments, we can save
976      the changes to the body.  For unknown reasons we need to set it
977      to empty first. */
978   if (*plaintext)
979     {
980       err = set_message_body (message, "");
981       if (!err)
982         err = set_message_body (message, signedtext);
983       if (err)
984         goto leave;
985     }
986
987
988  leave:
989   xfree (signedtext);
990   gpgme_key_release (sign_key);
991   log_debug ("%s:%s: leave (err=%s)\n", __FILE__, __func__, op_strerror (err));
992   return err;
993 }
994
995
996 \f
997 /* Encrypt and optionally sign (if SIGN_FLAG is true) the entire message
998    including all attachments. Returns 0 on success. */
999 int 
1000 GpgMsgImpl::encrypt_and_sign (HWND hwnd, bool sign_flag)
1001 {
1002   log_debug ("%s:%s: enter\n", __FILE__, __func__);
1003   gpgme_key_t *keys = NULL;
1004   gpgme_key_t sign_key = NULL;
1005   bool is_html;
1006   const char *plaintext;
1007   char *ciphertext = NULL;
1008   char **recipients = NULL;
1009   char **unknown = NULL;
1010   int err = 0;
1011   size_t all = 0;
1012   int n_keys;
1013     
1014   
1015   if (!*(plaintext = getOrigText ()) && !hasAttachments ()) 
1016     {
1017       log_debug ("%s:%s: leave (empty)", __FILE__, __func__);
1018       return 0; 
1019     }
1020
1021   /* Pop up a dialog box to ask for the signer of the message. */
1022   if (sign_flag)
1023     {
1024       if (signer_dialog_box (&sign_key, NULL) == -1)
1025         {
1026           log_debug ("%s.%s: leave (dialog failed)\n", __FILE__, __func__);
1027           return gpg_error (GPG_ERR_CANCELED);  
1028         }
1029     }
1030
1031   /* Gather the keys for the recipients. */
1032   recipients = getRecipients ();
1033   n_keys = op_lookup_keys (recipients, &keys, &unknown, &all);
1034
1035   log_debug ("%s:%s: found %d recipients, need %d, unknown=%p\n",
1036              __FILE__, __func__, n_keys, all, unknown);
1037   
1038   if (n_keys != count_recipients (recipients))
1039     {
1040       int opts = 0;
1041       gpgme_key_t *keys2 = NULL;
1042
1043       /* FIXME: The implementation is not correct: op_lookup_keys
1044          returns the number of missing keys but we compare against the
1045          total number of keys; thus the box would pop up even when all
1046          have been found. */
1047       log_debug ("%s:%s: calling recipient_dialog_box2", __FILE__, __func__);
1048       recipient_dialog_box2 (keys, unknown, all, &keys2, &opts);
1049       xfree (keys);
1050       keys = keys2;
1051       if (opts & OPT_FLAG_CANCEL) 
1052         {
1053           err = gpg_error (GPG_ERR_CANCELED);
1054           goto leave;
1055         }
1056     }
1057
1058   if (sign_key)
1059     log_debug ("%s:%s: signer: 0x%s %s\n",  __FILE__, __func__,
1060                keyid_from_key (sign_key), userid_from_key (sign_key));
1061   else
1062     log_debug ("%s:%s: no signer\n", __FILE__, __func__);
1063   if (keys)
1064     {
1065       for (int i=0; keys[i] != NULL; i++)
1066         log_debug ("%s.%s: recp.%d 0x%s %s\n", __FILE__, __func__,
1067                    i, keyid_from_key (keys[i]), userid_from_key (keys[i]));
1068     }
1069
1070   if (*plaintext)
1071     {
1072       is_html = is_html_body (plaintext);
1073
1074       err = op_encrypt (plaintext, &ciphertext, keys, NULL, 0);
1075       if (err)
1076         {
1077           MessageBox (hwnd, op_strerror (err),
1078                       "GPG Encryption", MB_ICONERROR|MB_OK);
1079           goto leave;
1080         }
1081
1082       if (is_html) 
1083         {
1084           char *tmp = add_html_line_endings (ciphertext);
1085           xfree (ciphertext);
1086           ciphertext = tmp;
1087         }
1088
1089 //       {
1090 //         SPropValue prop;
1091
1092 //         prop.ulPropTag=PR_MESSAGE_CLASS_A;
1093 //         prop.Value.lpszA="IPM.Note.OPENPGP";
1094 //         hr = HrSetOneProp (message, &prop);
1095 //         if (hr != S_OK)
1096 //           {
1097 //             log_error ("%s:%s: can't set message class: hr=%#lx\n",
1098 //                        __FILE__, __func__, hr); 
1099 //           }
1100
1101 //         prop.ulPropTag=PR_CONTENT_TYPE_A;
1102 //         prop.Value.lpszA="application/encrypted;foo=bar;type=mytype";
1103 //         hr = HrSetOneProp (message, &prop);
1104 //         if (hr != S_OK)
1105 //           {
1106 //             log_error ("%s:%s: can't set content type: hr=%#lx\n",
1107 //                        __FILE__, __func__, hr); 
1108 //           }
1109 //       }
1110
1111     }
1112
1113   if (hasAttachments ())
1114     {
1115       unsigned int n;
1116       
1117       n = getAttachments ();
1118       log_debug ("%s:%s: message has %u attachments\n", __FILE__, __func__, n);
1119       for (unsigned int i=0; !err && i < n; i++) 
1120         err = encryptAttachment (hwnd, i, keys, NULL, 0);
1121       if (err)
1122         {
1123           MessageBox (hwnd, op_strerror (err),
1124                       "GPG Attachment Encryption", MB_ICONERROR|MB_OK);
1125           goto leave;
1126         }
1127     }
1128
1129   set_x_header (message, "GPGol-Version", PACKAGE_VERSION);
1130
1131   /* Now that we successfully processed the attachments, we can save
1132      the changes to the body.  For unknown reasons we need to set it
1133      to empty first. */
1134   if (*plaintext)
1135     {
1136       err = set_message_body (message, "");
1137       if (!err)
1138         err = set_message_body (message, ciphertext);
1139       if (err)
1140         goto leave;
1141     }
1142
1143
1144  leave:
1145   /* FIXME: What to do with already encrypted attachments if some of
1146      the encrypted (or other operations) failed? */
1147
1148   for (int i=0; i < n_keys; i++)
1149     xfree (unknown[i]);
1150   if (n_keys)
1151     xfree (unknown);
1152
1153   free_key_array (keys);
1154   free_recipient_array (recipients);
1155   xfree (ciphertext);
1156   log_debug ("%s:%s: leave (err=%s)\n", __FILE__, __func__, op_strerror (err));
1157   return err;
1158 }
1159
1160
1161
1162 \f
1163 /* Attach a public key to a message. */
1164 int 
1165 GpgMsgImpl::attachPublicKey (const char *keyid)
1166 {
1167     /* @untested@ */
1168 #if 0
1169     const char *patt[1];
1170     char *keyfile;
1171     int err, pos = 0;
1172     LPATTACH newatt;
1173
1174     keyfile = generateTempname (keyid);
1175     patt[0] = xstrdup (keyid);
1176     err = op_export_keys (patt, keyfile);
1177
1178     newatt = createAttachment (NULL/*FIXME*/,pos);
1179     setAttachMethod (newatt, ATTACH_BY_VALUE);
1180     setAttachFilename (newatt, keyfile, false);
1181     /* XXX: set proper RFC3156 MIME types. */
1182
1183     if (streamFromFile (keyfile, newatt)) {
1184         log_debug ("attachPublicKey: commit changes.\n");
1185         newatt->SaveChanges (FORCE_SAVE);
1186     }
1187     releaseAttachment (newatt);
1188     xfree (keyfile);
1189     xfree ((void *)patt[0]);
1190     return err;
1191 #endif
1192     return -1;
1193 }
1194
1195
1196
1197
1198 \f
1199 /* Returns whether the message has any attachments. */
1200 bool
1201 GpgMsgImpl::hasAttachments (void)
1202 {
1203   return !!getAttachments ();
1204 }
1205
1206
1207 /* Reads the attachment information and returns the number of
1208    attachments. */
1209 unsigned int
1210 GpgMsgImpl::getAttachments (void)
1211 {
1212   SizedSPropTagArray (1L, propAttNum) = {
1213     1L, {PR_ATTACH_NUM}
1214   };
1215   HRESULT hr;    
1216   LPMAPITABLE table;
1217   LPSRowSet   rows;
1218
1219   if (!message)
1220     return 0;
1221
1222   if (!attach.att_table)
1223     {
1224       hr = message->GetAttachmentTable (0, &table);
1225       if (FAILED (hr))
1226         {
1227           log_debug ("%s:%s: GetAttachmentTable failed: hr=%#lx",
1228                      __FILE__, __func__, hr);
1229           return 0;
1230         }
1231       
1232       hr = HrQueryAllRows (table, (LPSPropTagArray)&propAttNum,
1233                            NULL, NULL, 0, &rows);
1234       if (FAILED (hr))
1235         {
1236           log_debug ("%s:%s: HrQueryAllRows failed: hr=%#lx",
1237                      __FILE__, __func__, hr);
1238           table->Release ();
1239           return 0;
1240         }
1241       attach.att_table = table;
1242       attach.rows = rows;
1243     }
1244
1245   return attach.rows->cRows > 0? attach.rows->cRows : 0;
1246 }
1247
1248
1249
1250 /* Return the attachment method for attachment OBJ. In case of error we
1251    return 0 which happens to be not defined. */
1252 static int
1253 get_attach_method (LPATTACH obj)
1254 {
1255   HRESULT hr;
1256   LPSPropValue propval = NULL;
1257   int method ;
1258
1259   hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_METHOD, &propval);
1260   if (FAILED (hr))
1261     {
1262       log_error ("%s:%s: error getting attachment method: hr=%#lx",
1263                  __FILE__, __func__, hr);
1264       return 0; 
1265     }
1266   /* We don't bother checking whether we really get a PT_LONG ulong
1267      back; if not the system is seriously damaged and we can't do
1268      further harm by returning a possible random value. */
1269   method = propval->Value.l;
1270   MAPIFreeBuffer (propval);
1271   return method;
1272 }
1273
1274
1275 /* Return the content-type of the attachment OBJ or NULL if it dow not
1276    exists.  Caller must free. */
1277 static char *
1278 get_attach_mime_tag (LPATTACH obj)
1279 {
1280   HRESULT hr;
1281   LPSPropValue propval = NULL;
1282   char *name;
1283
1284   hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_MIME_TAG_A, &propval);
1285   if (FAILED (hr))
1286     {
1287       log_error ("%s:%s: error getting attachment's MIME tag: hr=%#lx",
1288                  __FILE__, __func__, hr);
1289       return NULL; 
1290     }
1291   switch ( PROP_TYPE (propval->ulPropTag) )
1292     {
1293     case PT_UNICODE:
1294       name = wchar_to_utf8 (propval->Value.lpszW);
1295       if (!name)
1296         log_debug ("%s:%s: error converting to utf8\n", __FILE__, __func__);
1297       break;
1298       
1299     case PT_STRING8:
1300       name = xstrdup (propval->Value.lpszA);
1301       break;
1302       
1303     default:
1304       log_debug ("%s:%s: proptag=%#lx not supported\n",
1305                  __FILE__, __func__, propval->ulPropTag);
1306       name = NULL;
1307       break;
1308     }
1309   MAPIFreeBuffer (propval);
1310   return name;
1311 }
1312
1313
1314 /* Return the data property of an attachments or NULL in case of an
1315    error.  Caller must free.  Note, that this routine should only be
1316    used for short data objects like detached signatures. */
1317 static char *
1318 get_short_attach_data (LPATTACH obj)
1319 {
1320   HRESULT hr;
1321   LPSPropValue propval = NULL;
1322   char *data;
1323
1324   hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_DATA_BIN, &propval);
1325   if (FAILED (hr))
1326     {
1327       log_error ("%s:%s: error getting attachment's data: hr=%#lx",
1328                  __FILE__, __func__, hr);
1329       return NULL; 
1330     }
1331   switch ( PROP_TYPE (propval->ulPropTag) )
1332     {
1333     case PT_BINARY:
1334       /* This is a binary obnject but we know that it must be plain
1335          ASCII due to the armoed format.  */
1336       data = (char*)xmalloc (propval->Value.bin.cb + 1);
1337       memcpy (data, propval->Value.bin.lpb, propval->Value.bin.cb);
1338       data[propval->Value.bin.cb] = 0;
1339       break;
1340       
1341     default:
1342       log_debug ("%s:%s: proptag=%#lx not supported\n",
1343                  __FILE__, __func__, propval->ulPropTag);
1344       data = NULL;
1345       break;
1346     }
1347   MAPIFreeBuffer (propval);
1348   return data;
1349 }
1350
1351
1352 /* Check whether the attachment at position POS in the attachment
1353    table is the first part of a PGP/MIME message.  This routine should
1354    only be called if it has already been checked that the content-type
1355    of the attachment is application/pgp-encrypted. */
1356 bool
1357 GpgMsgImpl::isPgpmimeVersionPart (int pos)
1358 {
1359   HRESULT hr;
1360   LPATTACH att;
1361   LPSPropValue propval = NULL;
1362   bool result = false;
1363
1364   hr = message->OpenAttach (pos, NULL, MAPI_BEST_ACCESS, &att); 
1365   if (FAILED(hr))
1366     return false;
1367
1368   hr = HrGetOneProp ((LPMAPIPROP)att, PR_ATTACH_SIZE, &propval);
1369   if (FAILED (hr))
1370     {
1371       att->Release ();
1372       return false;
1373     }
1374   if ( PROP_TYPE (propval->ulPropTag) != PT_LONG
1375       || propval->Value.l < 10 || propval->Value.l > 1000 )
1376     {
1377       MAPIFreeBuffer (propval);
1378       att->Release ();
1379       return false;
1380     }
1381   MAPIFreeBuffer (propval);
1382
1383   hr = HrGetOneProp ((LPMAPIPROP)att, PR_ATTACH_DATA_BIN, &propval);
1384   if (SUCCEEDED (hr))
1385     {
1386       if (PROP_TYPE (propval->ulPropTag) == PT_BINARY)
1387         {
1388           if (propval->Value.bin.cb > 10 && propval->Value.bin.cb < 15 
1389               && !memcmp (propval->Value.bin.lpb, "Version: 1", 10)
1390               && ( propval->Value.bin.lpb[10] == '\r'
1391                    || propval->Value.bin.lpb[10] == '\n'))
1392             result = true;
1393         }
1394       MAPIFreeBuffer (propval);
1395     }
1396   att->Release ();
1397   return result;
1398 }
1399
1400
1401
1402 /* Set an arbitary header in the message MSG with NAME to the value
1403    VAL. */
1404 static bool 
1405 set_x_header (LPMESSAGE msg, const char *name, const char *val)
1406 {  
1407   HRESULT hr;
1408   LPSPropTagArray pProps = NULL;
1409   SPropValue pv;
1410   MAPINAMEID mnid, *pmnid;      
1411   /* {00020386-0000-0000-C000-000000000046}  ->  GUID For X-Headers */
1412   GUID guid = {0x00020386, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00,
1413                                             0x00, 0x00, 0x00, 0x46} };
1414
1415   if (!msg)
1416     return false;
1417
1418   memset (&mnid, 0, sizeof mnid);
1419   mnid.lpguid = &guid;
1420   mnid.ulKind = MNID_STRING;
1421   mnid.Kind.lpwstrName = utf8_to_wchar (name);
1422   pmnid = &mnid;
1423   hr = msg->GetIDsFromNames (1, &pmnid, MAPI_CREATE, &pProps);
1424   xfree (mnid.Kind.lpwstrName);
1425   if (FAILED (hr)) 
1426     {
1427       log_error ("%s:%s: can't get mapping for header `%s': hr=%#lx\n",
1428                  __FILE__, __func__, name, hr); 
1429       return false;
1430     }
1431     
1432   pv.ulPropTag = (pProps->aulPropTag[0] & 0xFFFF0000) | PT_STRING8;
1433   pv.Value.lpszA = (char *)val;
1434   hr = HrSetOneProp(msg, &pv);  
1435   if (hr != S_OK)
1436     {
1437       log_error ("%s:%s: can't set header `%s': hr=%#lx\n",
1438                  __FILE__, __func__, name, hr); 
1439       return false;
1440     }
1441   return true;
1442 }
1443
1444
1445
1446 /* Return the filename from the attachment as a malloced string.  The
1447    encoding we return will be utf8, however the MAPI docs declare that
1448    MAPI does only handle plain ANSI and thus we don't really care
1449    later on.  In fact we would need to convert the filename back to
1450    wchar and use the Unicode versions of the file API.  Returns NULL
1451    on error or if no filename is available. */
1452 static char *
1453 get_attach_filename (LPATTACH obj)
1454 {
1455   HRESULT hr;
1456   LPSPropValue propval;
1457   char *name = NULL;
1458
1459   hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_LONG_FILENAME, &propval);
1460   if (FAILED(hr)) 
1461     hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_FILENAME, &propval);
1462   if (FAILED(hr))
1463     {
1464       log_debug ("%s:%s: no filename property found", __FILE__, __func__);
1465       return NULL;
1466     }
1467
1468   switch ( PROP_TYPE (propval->ulPropTag) )
1469     {
1470     case PT_UNICODE:
1471       name = wchar_to_utf8 (propval->Value.lpszW);
1472       if (!name)
1473         log_debug ("%s:%s: error converting to utf8\n", __FILE__, __func__);
1474       break;
1475       
1476     case PT_STRING8:
1477       name = xstrdup (propval->Value.lpszA);
1478       break;
1479       
1480     default:
1481       log_debug ("%s:%s: proptag=%#lx not supported\n",
1482                  __FILE__, __func__, propval->ulPropTag);
1483       name = NULL;
1484       break;
1485     }
1486   MAPIFreeBuffer (propval);
1487   return name;
1488 }
1489
1490
1491
1492
1493 /* Return a filename to be used for saving an attachment. Returns an
1494    malloced string on success. HWND is the current Window and SRCNAME
1495    the filename to be used as suggestion.  On error; i.e. cancel NULL
1496    is returned. */
1497 static char *
1498 get_save_filename (HWND root, const char *srcname)
1499                                      
1500 {
1501   char filter[] = "All Files (*.*)\0*.*\0\0";
1502   char fname[MAX_PATH+1];
1503   OPENFILENAME ofn;
1504
1505   memset (fname, 0, sizeof (fname));
1506   strncpy (fname, srcname, MAX_PATH-1);
1507   fname[MAX_PATH] = 0;  
1508   
1509
1510   memset (&ofn, 0, sizeof (ofn));
1511   ofn.lStructSize = sizeof (ofn);
1512   ofn.hwndOwner = root;
1513   ofn.lpstrFile = fname;
1514   ofn.nMaxFile = MAX_PATH;
1515   ofn.lpstrFileTitle = NULL;
1516   ofn.nMaxFileTitle = 0;
1517   ofn.Flags |= OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
1518   ofn.lpstrTitle = "GPG - Save decrypted attachment";
1519   ofn.lpstrFilter = filter;
1520
1521   if (GetSaveFileName (&ofn))
1522     return xstrdup (fname);
1523   return NULL;
1524 }
1525
1526 \f
1527 /* Read the attachment ATT and try to detect whether this is a PGP
1528    Armored message.  METHOD is the attach method of ATT.  Returns 0 if
1529    it is not a PGP attachment. */
1530 static armor_t
1531 get_pgp_armor_type (LPATTACH att, int method)
1532 {
1533   HRESULT hr;
1534   LPSTREAM stream;
1535   char buffer [128];
1536   ULONG nread;
1537   const char *s;
1538
1539   if (method != ATTACH_BY_VALUE)
1540     return ARMOR_NONE;
1541   
1542   hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 
1543                           0, 0, (LPUNKNOWN*) &stream);
1544   if (FAILED (hr))
1545     {
1546       log_debug ("%s:%s: can't attachment data: hr=%#lx",
1547                  __FILE__, __func__,  hr);
1548       return ARMOR_NONE;
1549     }
1550
1551   hr = stream->Read (buffer, sizeof buffer -1, &nread);
1552   if ( hr != S_OK )
1553     {
1554       log_debug ("%s:%s: Read failed: hr=%#lx", __FILE__, __func__, hr);
1555       stream->Release ();
1556       return ARMOR_NONE;
1557     }
1558   buffer[nread] = 0;
1559   stream->Release ();
1560
1561   s = strstr (buffer, "-----BEGIN PGP ");
1562   if (!s)
1563     return ARMOR_NONE;
1564   s += 15;
1565   if (!strncmp (s, "MESSAGE-----", 12))
1566     return ARMOR_MESSAGE;
1567   else if (!strncmp (s, "SIGNATURE-----", 14))
1568     return ARMOR_SIGNATURE;
1569   else if (!strncmp (s, "SIGNED MESSAGE-----", 19))
1570     return ARMOR_SIGNED;
1571   else if (!strncmp (s, "ARMORED FILE-----", 17))
1572     return ARMOR_FILE;
1573   else if (!strncmp (s, "PUBLIC KEY BLOCK-----", 21))
1574     return ARMOR_PUBKEY;
1575   else if (!strncmp (s, "PRIVATE KEY BLOCK-----", 22))
1576     return ARMOR_SECKEY;
1577   else if (!strncmp (s, "SECRET KEY BLOCK-----", 21))
1578     return ARMOR_SECKEY;
1579   else
1580     return ARMOR_NONE;
1581 }
1582
1583
1584 /* Gather information about attachments and return a new object with
1585    these information.  Caller must release the returned information.
1586    The routine will return NULL in case of an error or if no
1587    attachments are available. */
1588 attach_info_t
1589 GpgMsgImpl::gatherAttachmentInfo (void)
1590 {    
1591   HRESULT hr;
1592   attach_info_t table;
1593   unsigned int pos, n_attach;
1594   const char *s;
1595
1596   is_pgpmime = false;
1597   n_attach = getAttachments ();
1598   log_debug ("%s:%s: message has %u attachments\n",
1599              __FILE__, __func__, n_attach);
1600   if (!n_attach)
1601       return NULL;
1602
1603   table = (attach_info_t)xcalloc (n_attach+1, sizeof *table);
1604   for (pos=0; pos < n_attach; pos++) 
1605     {
1606       LPATTACH att;
1607
1608       hr = message->OpenAttach (pos, NULL, MAPI_BEST_ACCESS, &att);     
1609       if (FAILED (hr))
1610         {
1611           log_error ("%s:%s: can't open attachment %d: hr=%#lx",
1612                      __FILE__, __func__, pos, hr);
1613           continue;
1614         }
1615
1616       table[pos].method = get_attach_method (att);
1617       table[pos].filename = get_attach_filename (att);
1618       table[pos].content_type = get_attach_mime_tag (att);
1619       if (table[pos].content_type)
1620         {
1621           char *p = strchr (table[pos].content_type, ';');
1622           if (p)
1623             {
1624               *p++ = 0;
1625               trim_trailing_spaces (table[pos].content_type);
1626               while (strchr (" \t\r\n", *p))
1627                 p++;
1628               trim_trailing_spaces (p);
1629               table[pos].content_type_parms = p;
1630             }
1631           if (!stricmp (table[pos].content_type, "text/plain")
1632               && table[pos].filename 
1633               && (s = strrchr (table[pos].filename, '.'))
1634               && !stricmp (s, ".asc"))
1635             table[pos].armor_type = get_pgp_armor_type (att,table[pos].method);
1636         }
1637       att->Release ();
1638     }
1639   table[pos].end_of_table = 1;
1640
1641   /* Figure out whether there are encrypted attachments. */
1642   for (pos=0; !table[pos].end_of_table; pos++)
1643     {
1644       if (table[pos].armor_type == ARMOR_MESSAGE)
1645         table[pos].is_encrypted = 1;
1646       else if (table[pos].filename && (s = strrchr (table[pos].filename, '.'))
1647                &&  (!stricmp (s, ".pgp") || !stricmp (s, ".gpg")))
1648         table[pos].is_encrypted = 1;
1649       else if (table[pos].content_type  
1650                && ( !stricmp (table[pos].content_type,
1651                               "application/pgp-encrypted")
1652                    || (!stricmp (table[pos].content_type,
1653                                  "multipart/encrypted")
1654                        && table[pos].content_type_parms
1655                        && strstr (table[pos].content_type_parms,
1656                                   "application/pgp-encrypted"))
1657                    || (!stricmp (table[pos].content_type,
1658                                  "application/pgp")
1659                        && table[pos].content_type_parms
1660                        && strstr (table[pos].content_type_parms,
1661                                   "x-action=encrypt"))))
1662         table[pos].is_encrypted = 1;
1663     }
1664      
1665   /* Figure out what attachments are signed. */
1666   for (pos=0; !table[pos].end_of_table; pos++)
1667     {
1668       if (table[pos].filename && (s = strrchr (table[pos].filename, '.'))
1669           &&  !stricmp (s, ".asc")
1670           && table[pos].content_type  
1671           && !stricmp (table[pos].content_type, "application/pgp-signature"))
1672         {
1673           size_t len = (s - table[pos].filename);
1674
1675           /* We mark the actual file, assuming that the .asc is a
1676              detached signature.  To correlate the data file and the
1677              signature we keep track of the POS. */
1678           for (unsigned int i=0; !table[i].end_of_table; i++)
1679             if (i != pos && table[i].filename 
1680                 && strlen (table[i].filename) == len
1681                 && !strncmp (table[i].filename, table[pos].filename, len))
1682               {
1683                 table[i].is_signed = 1;
1684                 table[i].sig_pos = pos;
1685               }
1686         }
1687       else if (table[pos].content_type  
1688                && (!stricmp (table[pos].content_type, "application/pgp")
1689                    && table[pos].content_type_parms
1690                    && strstr (table[pos].content_type_parms,"x-action=sign")))
1691         table[pos].is_signed = 1;
1692     }
1693
1694   log_debug ("%s:%s: attachment info:\n", __FILE__, __func__);
1695   for (pos=0; !table[pos].end_of_table; pos++)
1696     {
1697       log_debug ("\t%d %d %d %u %d `%s' `%s' `%s'\n",
1698                  pos, table[pos].is_encrypted,
1699                  table[pos].is_signed, table[pos].sig_pos,
1700                  table[pos].armor_type,
1701                  table[pos].filename, table[pos].content_type,
1702                  table[pos].content_type_parms);
1703     }
1704
1705   /* Simple check whether this is PGP/MIME encrypted.  At least with
1706      OL2003 the conent-type of the body is also correctly set but we
1707      don't make use of this as it is not clear whether this is true
1708      for othyer storage providers. */
1709   if (!opt.compat.no_pgpmime
1710       && pos == 2 && table[0].content_type && table[1].content_type
1711       && !stricmp (table[0].content_type, "application/pgp-encrypted")
1712       && !stricmp (table[1].content_type, "application/octet-stream")
1713       && isPgpmimeVersionPart (0))
1714     {
1715       log_debug ("\tThis is a PGP/MIME encrypted message - table adjusted");
1716       table[0].is_encrypted = 0;
1717       table[1].is_encrypted = 1;
1718       is_pgpmime = true;
1719     }
1720
1721   return table;
1722 }
1723
1724
1725
1726 \f
1727 /* Verify the ATTachment at attachments and table position POS_DATA
1728    agains the signature at position POS_SIG.  Display the status for
1729    each signature. */
1730 void
1731 GpgMsgImpl::verifyAttachment (HWND hwnd, attach_info_t table,
1732                               unsigned int pos_data,
1733                               unsigned int pos_sig)
1734
1735 {    
1736   HRESULT hr;
1737   LPATTACH att;
1738   int err;
1739   char *sig_data;
1740
1741   log_debug ("%s:%s: verifying attachment %d/%d",
1742              __FILE__, __func__, pos_data, pos_sig);
1743
1744   assert (table);
1745   assert (message);
1746
1747   /* First we copy the actual signature into a memory buffer.  Such a
1748      signature is expected to be samll enough to be readable directly
1749      (i.e.less that 16k as suggested by the MS MAPI docs). */
1750   hr = message->OpenAttach (pos_sig, NULL, MAPI_BEST_ACCESS, &att);     
1751   if (FAILED (hr))
1752     {
1753       log_error ("%s:%s: can't open attachment %d (sig): hr=%#lx",
1754                  __FILE__, __func__, pos_sig, hr);
1755       return;
1756     }
1757
1758   if ( table[pos_sig].method == ATTACH_BY_VALUE )
1759     sig_data = get_short_attach_data (att);
1760   else
1761     {
1762       log_error ("%s:%s: attachment %d (sig): method %d not supported",
1763                  __FILE__, __func__, pos_sig, table[pos_sig].method);
1764       att->Release ();
1765       return;
1766     }
1767   att->Release ();
1768   if (!sig_data)
1769     return; /* Problem getting signature; error has already been
1770                logged. */
1771
1772   /* Now get on with the actual signed data. */
1773   hr = message->OpenAttach (pos_data, NULL, MAPI_BEST_ACCESS, &att);    
1774   if (FAILED (hr))
1775     {
1776       log_error ("%s:%s: can't open attachment %d (data): hr=%#lx",
1777                  __FILE__, __func__, pos_data, hr);
1778       xfree (sig_data);
1779       return;
1780     }
1781
1782   if ( table[pos_data].method == ATTACH_BY_VALUE )
1783     {
1784       LPSTREAM stream;
1785
1786       hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 
1787                               0, 0, (LPUNKNOWN*) &stream);
1788       if (FAILED (hr))
1789         {
1790           log_error ("%s:%s: can't open data of attachment %d: hr=%#lx",
1791                      __FILE__, __func__, pos_data, hr);
1792           goto leave;
1793         }
1794       err = op_verify_detached_sig (stream, sig_data,
1795                                     table[pos_data].filename);
1796       if (err)
1797         {
1798           log_debug ("%s:%s: verify detached signature failed: %s",
1799                      __FILE__, __func__, op_strerror (err)); 
1800           MessageBox (hwnd, op_strerror (err),
1801                       "GPG Attachment Verification", MB_ICONERROR|MB_OK);
1802         }
1803       stream->Release ();
1804     }
1805   else
1806     {
1807       log_error ("%s:%s: attachment %d (data): method %d not supported",
1808                  __FILE__, __func__, pos_data, table[pos_data].method);
1809     }
1810
1811  leave:
1812   /* Close this attachment. */
1813   xfree (sig_data);
1814   att->Release ();
1815 }
1816
1817
1818 /* Decrypt the attachment with the internal number POS.
1819    SAVE_PLAINTEXT must be true to save the attachemnt; displaying a
1820    attachment is not yet supported.  If FILENAME is not NULL it will
1821    be displayed along with status outputs. */
1822 void
1823 GpgMsgImpl::decryptAttachment (HWND hwnd, int pos, bool save_plaintext,
1824                                int ttl, const char *filename)
1825 {    
1826   HRESULT hr;
1827   LPATTACH att;
1828   int method, err;
1829
1830   log_debug ("%s:%s: processing attachment %d", __FILE__, __func__, pos);
1831
1832   /* Make sure that we can access the attachment table. */
1833   if (!message || !getAttachments ())
1834     {
1835       log_debug ("%s:%s: no attachemnts at all", __FILE__, __func__);
1836       return;
1837     }
1838
1839   if (!save_plaintext)
1840     {
1841       log_error ("%s:%s: save_plaintext not requested", __FILE__, __func__);
1842       return;
1843     }
1844
1845   hr = message->OpenAttach (pos, NULL, MAPI_BEST_ACCESS, &att); 
1846   if (FAILED (hr))
1847     {
1848       log_debug ("%s:%s: can't open attachment %d: hr=%#lx",
1849                  __FILE__, __func__, pos, hr);
1850       return;
1851     }
1852
1853   method = get_attach_method (att);
1854   if ( method == ATTACH_EMBEDDED_MSG)
1855     {
1856       /* This is an embedded message.  The orginal G-DATA plugin
1857          decrypted the message and then updated the attachemnt;
1858          i.e. stored the plaintext.  This seemed to ensure that the
1859          attachemnt message was properly displayed.  I am not sure
1860          what we should do - it might be necessary to have a callback
1861          to allow displaying the attachment.  Needs further
1862          experiments. */
1863       LPMESSAGE emb;
1864       
1865       hr = att->OpenProperty (PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, 
1866                               MAPI_MODIFY, (LPUNKNOWN*)&emb);
1867       if (FAILED (hr))
1868         {
1869           log_error ("%s:%s: can't open data obj of attachment %d: hr=%#lx",
1870                      __FILE__, __func__, pos, hr);
1871           goto leave;
1872         }
1873
1874       //FIXME  Not sure what to do here.  Did it ever work?
1875       //        setWindow (hwnd);
1876       //        setMessage (emb);
1877       //if (doCmdAttach (action))
1878       //  success = FALSE;
1879       //XXX;
1880       //emb->SaveChanges (FORCE_SAVE);
1881       //att->SaveChanges (FORCE_SAVE);
1882       emb->Release ();
1883     }
1884   else if (method == ATTACH_BY_VALUE)
1885     {
1886       char *s;
1887       char *outname;
1888       char *suggested_name;
1889       LPSTREAM from, to;
1890
1891       suggested_name = get_attach_filename (att);
1892       if (suggested_name)
1893         log_debug ("%s:%s: attachment %d, filename `%s'", 
1894                   __FILE__, __func__, pos, suggested_name);
1895       /* Strip of know extensions or use a default name. */
1896       if (!suggested_name)
1897         {
1898           xfree (suggested_name);
1899           suggested_name = (char*)xmalloc (50);
1900           snprintf (suggested_name, 49, "unnamed-%d.dat", pos);
1901         }
1902       else if ((s = strrchr (suggested_name, '.'))
1903                && (!stricmp (s, ".pgp") 
1904                    || !stricmp (s, ".gpg") 
1905                    || !stricmp (s, ".asc")) )
1906         {
1907           *s = 0;
1908         }
1909       outname = get_save_filename (hwnd, suggested_name);
1910       xfree (suggested_name);
1911
1912       hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 
1913                               0, 0, (LPUNKNOWN*) &from);
1914       if (FAILED (hr))
1915         {
1916           log_error ("%s:%s: can't open data of attachment %d: hr=%#lx",
1917                      __FILE__, __func__, pos, hr);
1918           xfree (outname);
1919           goto leave;
1920         }
1921
1922       hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
1923                              (STGM_CREATE | STGM_READWRITE),
1924                              outname, NULL, &to); 
1925       if (FAILED (hr)) 
1926         {
1927           log_error ("%s:%s: can't create stream for `%s': hr=%#lx\n",
1928                      __FILE__, __func__, outname, hr); 
1929           from->Release ();
1930           xfree (outname);
1931           goto leave;
1932         }
1933       
1934       err = op_decrypt_stream (from, to, ttl, filename);
1935       if (err)
1936         {
1937           log_debug ("%s:%s: decrypt stream failed: %s",
1938                      __FILE__, __func__, op_strerror (err)); 
1939           to->Revert ();
1940           to->Release ();
1941           from->Release ();
1942           MessageBox (hwnd, op_strerror (err),
1943                       "GPG Attachment Decryption", MB_ICONERROR|MB_OK);
1944           /* FIXME: We might need to delete outname now.  However a
1945              sensible implementation of the stream object should have
1946              done it trhough the Revert call. */
1947           xfree (outname);
1948           goto leave;
1949         }
1950         
1951       to->Commit (0);
1952       to->Release ();
1953       from->Release ();
1954
1955       /*  Hmmm: Why are we deleting the attachment now????? 
1956           Disabled until clarified.   FIXME */
1957       //if (message->DeleteAttach (pos, 0, NULL, 0) == S_OK)
1958       //   show error;
1959
1960       xfree (outname);
1961     }
1962   else
1963     {
1964       log_error ("%s:%s: attachment %d: method %d not supported",
1965                  __FILE__, __func__, pos, method);
1966     }
1967
1968  leave:
1969   /* Close this attachment. */
1970   att->Release ();
1971 }
1972
1973
1974 /* Sign the attachment with the internal number POS.  TTL is caching
1975    time for a required passphrase. */
1976 void
1977 GpgMsgImpl::signAttachment (HWND hwnd, int pos, gpgme_key_t sign_key, int ttl)
1978 {    
1979   HRESULT hr;
1980   LPATTACH att;
1981   int method, err;
1982   LPSTREAM from = NULL;
1983   LPSTREAM to = NULL;
1984   char *signame = NULL;
1985   LPATTACH newatt = NULL;
1986
1987   /* Make sure that we can access the attachment table. */
1988   if (!message || !getAttachments ())
1989     {
1990       log_debug ("%s:%s: no attachemnts at all", __FILE__, __func__);
1991       return;
1992     }
1993
1994   hr = message->OpenAttach (pos, NULL, MAPI_BEST_ACCESS, &att); 
1995   if (FAILED (hr))
1996     {
1997       log_debug ("%s:%s: can't open attachment %d: hr=%#lx",
1998                  __FILE__, __func__, pos, hr);
1999       return;
2000     }
2001
2002   /* Construct a filename for the new attachment. */
2003   {
2004     char *tmpname = get_attach_filename (att);
2005     if (!tmpname)
2006       {
2007         signame = (char*)xmalloc (70);
2008         snprintf (signame, 70, "gpg-signature-%d.asc", pos);
2009       }
2010     else
2011       {
2012         signame = (char*)xmalloc (strlen (tmpname) + 4 + 1);
2013         strcpy (stpcpy (signame, tmpname), ".asc");
2014         xfree (tmpname);
2015       }
2016   }
2017
2018   method = get_attach_method (att);
2019   if (method == ATTACH_EMBEDDED_MSG)
2020     {
2021       log_debug ("%s:%s: signing embedded attachments is not supported",
2022                  __FILE__, __func__);
2023     }
2024   else if (method == ATTACH_BY_VALUE)
2025     {
2026       ULONG newpos;
2027       SPropValue prop;
2028
2029       hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 
2030                               0, 0, (LPUNKNOWN*)&from);
2031       if (FAILED (hr))
2032         {
2033           log_error ("%s:%s: can't open data of attachment %d: hr=%#lx",
2034                      __FILE__, __func__, pos, hr);
2035           goto leave;
2036         }
2037
2038       hr = message->CreateAttach (NULL, 0, &newpos, &newatt);
2039       if (hr != S_OK)
2040         {
2041           log_error ("%s:%s: can't create attachment: hr=%#lx\n",
2042                      __FILE__, __func__, hr); 
2043           goto leave;
2044         }
2045
2046       prop.ulPropTag = PR_ATTACH_METHOD;
2047       prop.Value.ul = ATTACH_BY_VALUE;
2048       hr = HrSetOneProp (newatt, &prop);
2049       if (hr != S_OK)
2050         {
2051           log_error ("%s:%s: can't set attach method: hr=%#lx\n",
2052                      __FILE__, __func__, hr); 
2053           goto leave;
2054         }
2055       
2056       prop.ulPropTag = PR_ATTACH_LONG_FILENAME_A;
2057       prop.Value.lpszA = signame;   
2058       hr = HrSetOneProp (newatt, &prop);
2059       if (hr != S_OK)
2060         {
2061           log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
2062                      __FILE__, __func__, hr); 
2063           goto leave;
2064         }
2065       log_debug ("%s:%s: setting filename of attachment %d/%ld to `%s'",
2066                  __FILE__, __func__, pos, newpos, signame);
2067
2068       prop.ulPropTag = PR_ATTACH_EXTENSION_A;
2069       prop.Value.lpszA = ".pgpsig";   
2070       hr = HrSetOneProp (newatt, &prop);
2071       if (hr != S_OK)
2072         {
2073           log_error ("%s:%s: can't set attach extension: hr=%#lx\n",
2074                      __FILE__, __func__, hr); 
2075           goto leave;
2076         }
2077
2078       prop.ulPropTag = PR_ATTACH_TAG;
2079       prop.Value.bin.cb  = sizeof oid_mimetag;
2080       prop.Value.bin.lpb = (LPBYTE)oid_mimetag;
2081       hr = HrSetOneProp (newatt, &prop);
2082       if (hr != S_OK)
2083         {
2084           log_error ("%s:%s: can't set attach tag: hr=%#lx\n",
2085                      __FILE__, __func__, hr); 
2086           goto leave;
2087         }
2088
2089       prop.ulPropTag = PR_ATTACH_MIME_TAG_A;
2090       prop.Value.lpszA = "application/pgp-signature";
2091       hr = HrSetOneProp (newatt, &prop);
2092       if (hr != S_OK)
2093         {
2094           log_error ("%s:%s: can't set attach mime tag: hr=%#lx\n",
2095                      __FILE__, __func__, hr); 
2096           goto leave;
2097         }
2098
2099       hr = newatt->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 0,
2100                                  MAPI_CREATE | MAPI_MODIFY, (LPUNKNOWN*)&to);
2101       if (FAILED (hr)) 
2102         {
2103           log_error ("%s:%s: can't create output stream: hr=%#lx\n",
2104                      __FILE__, __func__, hr); 
2105           goto leave;
2106         }
2107       
2108       err = op_sign_stream (from, to, OP_SIG_DETACH, sign_key, ttl);
2109       if (err)
2110         {
2111           log_debug ("%s:%s: sign stream failed: %s",
2112                      __FILE__, __func__, op_strerror (err)); 
2113           to->Revert ();
2114           MessageBox (hwnd, op_strerror (err),
2115                       "GPG Attachment Signing", MB_ICONERROR|MB_OK);
2116           goto leave;
2117         }
2118       from->Release ();
2119       from = NULL;
2120       to->Commit (0);
2121       to->Release ();
2122       to = NULL;
2123
2124       hr = newatt->SaveChanges (0);
2125       if (hr != S_OK)
2126         {
2127           log_error ("%s:%s: SaveChanges failed: hr=%#lx\n",
2128                      __FILE__, __func__, hr); 
2129           goto leave;
2130         }
2131
2132     }
2133   else
2134     {
2135       log_error ("%s:%s: attachment %d: method %d not supported",
2136                  __FILE__, __func__, pos, method);
2137     }
2138
2139  leave:
2140   if (from)
2141     from->Release ();
2142   if (to)
2143     to->Release ();
2144   xfree (signame);
2145   if (newatt)
2146     newatt->Release ();
2147
2148   att->Release ();
2149 }
2150
2151 /* Encrypt the attachment with the internal number POS.  KEYS is a
2152    NULL terminates array with recipients to whom the message should be
2153    encrypted.  If SIGN_KEY is not NULL the attachment will also get
2154    signed. TTL is the passphrase caching time and only used if
2155    SIGN_KEY is not NULL. Returns 0 on success. */
2156 int
2157 GpgMsgImpl::encryptAttachment (HWND hwnd, int pos, gpgme_key_t *keys,
2158                                gpgme_key_t sign_key, int ttl)
2159 {    
2160   HRESULT hr;
2161   LPATTACH att;
2162   int method, err;
2163   LPSTREAM from = NULL;
2164   LPSTREAM to = NULL;
2165   char *filename = NULL;
2166   LPATTACH newatt = NULL;
2167
2168   /* Make sure that we can access the attachment table. */
2169   if (!message || !getAttachments ())
2170     {
2171       log_debug ("%s:%s: no attachemnts at all", __FILE__, __func__);
2172       return 0;
2173     }
2174
2175   hr = message->OpenAttach (pos, NULL, MAPI_BEST_ACCESS, &att); 
2176   if (FAILED (hr))
2177     {
2178       log_debug ("%s:%s: can't open attachment %d: hr=%#lx",
2179                  __FILE__, __func__, pos, hr);
2180       err = gpg_error (GPG_ERR_GENERAL);
2181       return err;
2182     }
2183
2184   /* Construct a filename for the new attachment. */
2185   {
2186     char *tmpname = get_attach_filename (att);
2187     if (!tmpname)
2188       {
2189         filename = (char*)xmalloc (70);
2190         snprintf (filename, 70, "gpg-encrypted-%d.pgp", pos);
2191       }
2192     else
2193       {
2194         filename = (char*)xmalloc (strlen (tmpname) + 4 + 1);
2195         strcpy (stpcpy (filename, tmpname), ".pgp");
2196         xfree (tmpname);
2197       }
2198   }
2199
2200   method = get_attach_method (att);
2201   if (method == ATTACH_EMBEDDED_MSG)
2202     {
2203       log_debug ("%s:%s: encrypting embedded attachments is not supported",
2204                  __FILE__, __func__);
2205       err = gpg_error (GPG_ERR_NOT_SUPPORTED);
2206     }
2207   else if (method == ATTACH_BY_VALUE)
2208     {
2209       ULONG newpos;
2210       SPropValue prop;
2211
2212       hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 
2213                               0, 0, (LPUNKNOWN*)&from);
2214       if (FAILED (hr))
2215         {
2216           log_error ("%s:%s: can't open data of attachment %d: hr=%#lx",
2217                      __FILE__, __func__, pos, hr);
2218           err = gpg_error (GPG_ERR_GENERAL);
2219           goto leave;
2220         }
2221
2222       hr = message->CreateAttach (NULL, 0, &newpos, &newatt);
2223       if (hr != S_OK)
2224         {
2225           log_error ("%s:%s: can't create attachment: hr=%#lx\n",
2226                      __FILE__, __func__, hr); 
2227           err = gpg_error (GPG_ERR_GENERAL);
2228           goto leave;
2229         }
2230
2231       prop.ulPropTag = PR_ATTACH_METHOD;
2232       prop.Value.ul = ATTACH_BY_VALUE;
2233       hr = HrSetOneProp (newatt, &prop);
2234       if (hr != S_OK)
2235         {
2236           log_error ("%s:%s: can't set attach method: hr=%#lx\n",
2237                      __FILE__, __func__, hr); 
2238           err = gpg_error (GPG_ERR_GENERAL);
2239           goto leave;
2240         }
2241       
2242       prop.ulPropTag = PR_ATTACH_LONG_FILENAME_A;
2243       prop.Value.lpszA = filename;   
2244       hr = HrSetOneProp (newatt, &prop);
2245       if (hr != S_OK)
2246         {
2247           log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
2248                      __FILE__, __func__, hr); 
2249           err = gpg_error (GPG_ERR_GENERAL);
2250           goto leave;
2251         }
2252       log_debug ("%s:%s: setting filename of attachment %d/%ld to `%s'",
2253                  __FILE__, __func__, pos, newpos, filename);
2254
2255       prop.ulPropTag = PR_ATTACH_EXTENSION_A;
2256       prop.Value.lpszA = ".pgpenc";   
2257       hr = HrSetOneProp (newatt, &prop);
2258       if (hr != S_OK)
2259         {
2260           log_error ("%s:%s: can't set attach extension: hr=%#lx\n",
2261                      __FILE__, __func__, hr); 
2262           err = gpg_error (GPG_ERR_GENERAL);
2263           goto leave;
2264         }
2265
2266       prop.ulPropTag = PR_ATTACH_TAG;
2267       prop.Value.bin.cb  = sizeof oid_mimetag;
2268       prop.Value.bin.lpb = (LPBYTE)oid_mimetag;
2269       hr = HrSetOneProp (newatt, &prop);
2270       if (hr != S_OK)
2271         {
2272           log_error ("%s:%s: can't set attach tag: hr=%#lx\n",
2273                      __FILE__, __func__, hr); 
2274           err = gpg_error (GPG_ERR_GENERAL);
2275           goto leave;
2276         }
2277
2278       prop.ulPropTag = PR_ATTACH_MIME_TAG_A;
2279       prop.Value.lpszA = "application/pgp-encrypted";
2280       hr = HrSetOneProp (newatt, &prop);
2281       if (hr != S_OK)
2282         {
2283           log_error ("%s:%s: can't set attach mime tag: hr=%#lx\n",
2284                      __FILE__, __func__, hr); 
2285           err = gpg_error (GPG_ERR_GENERAL);
2286           goto leave;
2287         }
2288
2289       hr = newatt->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 0,
2290                                  MAPI_CREATE | MAPI_MODIFY, (LPUNKNOWN*)&to);
2291       if (FAILED (hr)) 
2292         {
2293           log_error ("%s:%s: can't create output stream: hr=%#lx\n",
2294                      __FILE__, __func__, hr); 
2295           err = gpg_error (GPG_ERR_GENERAL);
2296           goto leave;
2297         }
2298       
2299       err = op_encrypt_stream (from, to, keys, sign_key, ttl);
2300       if (err)
2301         {
2302           log_debug ("%s:%s: encrypt stream failed: %s",
2303                      __FILE__, __func__, op_strerror (err)); 
2304           to->Revert ();
2305           MessageBox (hwnd, op_strerror (err),
2306                       "GPG Attachment Encryption", MB_ICONERROR|MB_OK);
2307           goto leave;
2308         }
2309       from->Release ();
2310       from = NULL;
2311       to->Commit (0);
2312       to->Release ();
2313       to = NULL;
2314
2315       hr = newatt->SaveChanges (0);
2316       if (hr != S_OK)
2317         {
2318           log_error ("%s:%s: SaveChanges failed: hr=%#lx\n",
2319                      __FILE__, __func__, hr); 
2320           err = gpg_error (GPG_ERR_GENERAL);
2321           goto leave;
2322         }
2323
2324       hr = message->DeleteAttach (pos, 0, NULL, 0);
2325       if (hr != S_OK)
2326         {
2327           log_error ("%s:%s: DeleteAtatch failed: hr=%#lx\n",
2328                      __FILE__, __func__, hr); 
2329           err = gpg_error (GPG_ERR_GENERAL);
2330           goto leave;
2331         }
2332
2333     }
2334   else
2335     {
2336       log_error ("%s:%s: attachment %d: method %d not supported",
2337                  __FILE__, __func__, pos, method);
2338       err = gpg_error (GPG_ERR_NOT_SUPPORTED);
2339     }
2340
2341  leave:
2342   if (from)
2343     from->Release ();
2344   if (to)
2345     to->Release ();
2346   xfree (filename);
2347   if (newatt)
2348     newatt->Release ();
2349
2350   att->Release ();
2351   return err;
2352 }