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