Fixed PGP inline encrypted message decryption.
[gpgol.git] / src / message.cpp
1 /* message.cpp - Functions for message handling
2  *      Copyright (C) 2006, 2007, 2008 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.1 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 License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <windows.h>
24
25 #include "mymapi.h"
26 #include "mymapitags.h"
27 #include "myexchext.h"
28 #include "common.h"
29 #include "mapihelp.h"
30 #include "mimeparser.h"
31 #include "mimemaker.h"
32 #include "display.h"
33 #include "message.h"
34
35 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
36                                      SRCNAME, __func__, __LINE__); \
37                         } while (0)
38
39
40 static void 
41 ul_release (LPVOID punk, const char *func)
42 {
43   ULONG res;
44   
45   if (!punk)
46     return;
47   res = UlRelease (punk);
48   log_debug ("%s:%s: UlRelease(%p) had %lu references\n", 
49              SRCNAME, func, punk, res);
50 }
51
52
53 /* A helper function used by OnRead and OnOpen to dispatch the
54    message.  Returns true if the message has been processed.  */
55 bool
56 message_incoming_handler (LPMESSAGE message, HWND hwnd)
57 {
58   bool retval = false;
59   msgtype_t msgtype;
60   int pass = 0;
61
62  retry:
63   pass++;
64   msgtype = mapi_get_message_type (message);
65   switch (msgtype)
66     {
67     case MSGTYPE_UNKNOWN: 
68       /* If this message has never passed our change message class
69          code it won't have an unknown msgtype _and_ no sig status
70          flag.  Thus we look at the message class now and change it if
71          required.  It won't get displayed correctly right away but a
72          latter decrypt command or when viewed a second time all has
73          been set.  Note that we should have similar code for some
74          message classes in GpgolUserEvents:OnSelectionChange; but
75          tehre are a couiple of problems.  */
76       if (pass == 1 && !mapi_has_sig_status (message))
77         {
78           log_debug ("%s:%s: message class not yet checked - doing now\n",
79                      SRCNAME, __func__);
80           if (mapi_change_message_class (message, 0))
81             goto retry;
82         }
83       break;
84     case MSGTYPE_SMIME:
85       if (pass == 1 && opt.enable_smime)
86         {
87           log_debug ("%s:%s: message class not checked with smime enabled "
88                      "- doing now\n", SRCNAME, __func__);
89           if (mapi_change_message_class (message, 0))
90             goto retry;
91         }
92       break;
93     case MSGTYPE_GPGOL:
94       log_debug ("%s:%s: ignoring unknown message of original SMIME class\n",
95                  SRCNAME, __func__);
96       break;
97     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
98       log_debug ("%s:%s: processing multipart signed message\n", 
99                  SRCNAME, __func__);
100       retval = true;
101       message_verify (message, msgtype, 0, hwnd);
102       break;
103     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
104       log_debug ("%s:%s: processing multipart encrypted message\n",
105                  SRCNAME, __func__);
106       retval = true;
107       message_decrypt (message, msgtype, 0, hwnd);
108       break;
109     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
110       log_debug ("%s:%s: processing opaque signed message\n", 
111                  SRCNAME, __func__);
112       retval = true;
113       message_verify (message, msgtype, 0, hwnd);
114       break;
115     case MSGTYPE_GPGOL_CLEAR_SIGNED:
116       log_debug ("%s:%s: processing clear signed pgp message\n", 
117                  SRCNAME, __func__);
118       retval = true;
119       message_verify (message, msgtype, 0, hwnd);
120       break;
121     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
122       log_debug ("%s:%s: processing opaque encrypted message\n",
123                  SRCNAME, __func__);
124       retval = true;
125       message_decrypt (message, msgtype, 0, hwnd);
126       break;
127     case MSGTYPE_GPGOL_PGP_MESSAGE:
128       log_debug ("%s:%s: processing pgp message\n", SRCNAME, __func__);
129       retval = true;
130       message_decrypt (message, msgtype, 0, hwnd);
131       break;
132     }
133
134   return retval;
135 }
136
137
138 /* Common Code used by OnReadComplete and OnOpenComplete to display a
139    modified message.   Returns true if the message was encrypted.  */
140 bool
141 message_display_handler (LPEXCHEXTCALLBACK eecb, HWND hwnd)
142 {
143   int err;
144   HRESULT hr;
145   LPMESSAGE message = NULL;
146   LPMDB mdb = NULL;
147   int ishtml, wasprotected = false;
148   char *body;
149   
150   hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
151   if (SUCCEEDED (hr))
152     {
153       /* (old: If the message was protected we don't allow a fallback to the
154          OOM display methods.)  Now: As it is difficult to find the
155          actual window we now use the OOM display always.  */
156       err = mapi_get_gpgol_body_attachment (message, &body, NULL, 
157                                             &ishtml, &wasprotected);
158       if (!err && body)
159         update_display (hwnd, /*wasprotected? NULL:*/ eecb, ishtml, body);
160       else
161         update_display (hwnd, NULL, 0, 
162                         _("[Crypto operation failed - "
163                           "can't show the body of the message]"));
164       xfree (body);
165   
166       /*  put_outlook_property (eecb, "EncryptedStatus", "MyStatus"); */
167     }
168   else
169     log_debug_w32 (hr, "%s:%s: error getting message", SRCNAME, __func__);
170
171   ul_release (message, __func__);
172   ul_release (mdb, __func__);
173
174   return !!wasprotected;
175 }
176
177
178 /* If the current message is an encrypted one remove the body
179    properties which might have come up due to OL internal
180    syncronization and a failing olDiscard feature.  */
181 void
182 message_wipe_body_cruft (LPEXCHEXTCALLBACK eecb)
183 {
184   
185   HRESULT hr;
186   LPMESSAGE message = NULL;
187   LPMDB mdb = NULL;
188       
189   log_debug ("%s:%s: enter", SRCNAME, __func__);
190   hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
191   if (SUCCEEDED (hr))
192     {
193       switch (mapi_get_message_type (message))
194         {
195         case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
196         case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
197           {
198             if (mapi_has_last_decrypted (message))
199               {
200                 SPropTagArray proparray;
201                 int anyokay = 0;
202             
203                 proparray.cValues = 1;
204                 proparray.aulPropTag[0] = PR_BODY;
205                 hr = message->DeleteProps (&proparray, NULL);
206                 if (hr)
207                   log_debug_w32 (hr, "%s:%s: deleting PR_BODY failed",
208                                  SRCNAME, __func__);
209                 else
210                   anyokay++;
211             
212                 proparray.cValues = 1;
213                 proparray.aulPropTag[0] = PR_BODY_HTML;
214                 message->DeleteProps (&proparray, NULL);
215                 if (hr)
216                   log_debug_w32 (hr, "%s:%s: deleting PR_BODY_HTML failed", 
217                                  SRCNAME, __func__);
218                 else
219                   anyokay++;
220
221                 if (anyokay)
222                   {
223                     hr = message->SaveChanges (KEEP_OPEN_READWRITE);
224                     if (hr)
225                       log_error_w32 (hr, "%s:%s: SaveChanges failed",
226                                      SRCNAME, __func__); 
227                     else
228                       log_debug ("%s:%s: SaveChanges succeded; "
229                                  "body cruft removed",
230                                  SRCNAME, __func__); 
231                   }
232               }  
233             else
234               log_debug_w32 (hr, "%s:%s: "
235                              "error getting message decryption status", 
236                              SRCNAME, __func__);
237           }
238           break;
239
240         case MSGTYPE_GPGOL_PGP_MESSAGE:
241           /* We can't delete the body of a message if it is an inline
242              PGP encrypted message because the body holds the
243              ciphertext.  */
244           break;
245
246         default: 
247           break;
248         }
249       
250       ul_release (message, __func__);
251       ul_release (mdb, __func__);
252     }
253 }
254
255
256
257 /* Display some information about MESSAGE.  */
258 void
259 message_show_info (LPMESSAGE message, HWND hwnd)
260 {
261   char *msgcls = mapi_get_message_class (message);
262   char *sigstat = mapi_get_sig_status (message);
263   char *mimeinfo = mapi_get_mime_info (message);
264   size_t buflen;
265   char *buffer;
266
267   buflen = strlen (msgcls) + strlen (sigstat) + strlen (mimeinfo) + 200;
268   buffer = (char*)xmalloc (buflen+1);
269   snprintf (buffer, buflen, 
270             _("Message class: %s\n"
271               "Sig Status   : %s\n"
272               "Structure of the message:\n"
273               "%s"), 
274             msgcls,
275             sigstat,
276             mimeinfo);
277   
278   MessageBox (hwnd, buffer, _("GpgOL - Message Information"),
279               MB_ICONINFORMATION|MB_OK);
280   xfree (buffer);
281   xfree (mimeinfo);
282   xfree (sigstat);
283   xfree (msgcls);
284 }
285
286
287
288 \f
289 /* Convert the clear signed message from INPUT into a PS?MIME signed
290    message and return it in a new allocated buffer.  OUTPUTLEN
291    received the valid length of that buffer; the buffer is guarnateed
292    to be Nul terminated.  */
293 static char *
294 pgp_mime_from_clearsigned (LPSTREAM input, size_t *outputlen)
295 {
296   HRESULT hr;
297   STATSTG statinfo;
298   ULONG nread;
299   char *body = NULL;
300   char *p, *p0, *dest, *mark;
301   char boundary[BOUNDARYSIZE+1];
302   char top_header[200 + 2*BOUNDARYSIZE]; 
303   char sig_header[100 + BOUNDARYSIZE]; 
304   char end_header[10 + BOUNDARYSIZE];
305   size_t reserved_space;
306   int state;
307
308   *outputlen = 0;
309
310   /* Note that our parser does not make use of the micalg parameter.  */
311   generate_boundary (boundary);
312   snprintf (top_header, sizeof top_header,
313             "MIME-Version: 1.0\r\n"
314             "Content-Type: multipart/signed; boundary=\"%s\";\r\n"
315             "              protocol=\"application/pgp-signature\"\r\n"
316             "\r\n"
317             "--%s\r\n", boundary, boundary);
318   snprintf (sig_header, sizeof sig_header,
319             "--%s\r\n"
320             "Content-Type: application/pgp-signature\r\n"
321             "\r\n", boundary);
322   snprintf (end_header, sizeof end_header,
323             "\r\n"
324             "--%s--\r\n", boundary);
325   reserved_space = (strlen (top_header) + strlen (sig_header) 
326                     + strlen (end_header)+ 100);
327
328   hr = input->Stat (&statinfo, STATFLAG_NONAME);
329   if (hr)
330     {
331       log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
332       return NULL;
333     }
334       
335   body = (char*)xmalloc (reserved_space
336                          + (size_t)statinfo.cbSize.QuadPart + 2);
337   hr = input->Read (body+reserved_space,
338                     (size_t)statinfo.cbSize.QuadPart, &nread);
339   if (hr)
340     {
341       log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
342       xfree (body);
343       return NULL;
344     }
345   body[reserved_space + nread] = 0;
346   body[reserved_space + nread+1] = 0;  /* Just in case this is
347                                           accidently an wchar_t. */
348   if (nread != statinfo.cbSize.QuadPart)
349     {
350       log_debug ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
351       xfree (body);
352       return NULL;
353     }
354
355   /* We do in place conversion. */
356   state = 0;
357   dest = NULL;
358   for (p=body+reserved_space; p && *p; p = (p=strchr (p+1, '\n'))? (p+1):NULL)
359     {
360       if (!state && !strncmp (p, "-----BEGIN PGP SIGNED MESSAGE-----", 34)
361           && trailing_ws_p (p+34) )
362         {
363           dest = stpcpy (body, top_header);
364           state = 1;
365         }
366       else if (state == 1)
367         {
368           /* Wait for an empty line.  */
369           if (trailing_ws_p (p))
370             state = 2;
371         }
372       else if (state == 2 && strncmp (p, "-----", 5))
373         {
374           /* Copy signed data. */
375           p0 = p;
376           if (*p == '-' && p[1] == ' ')
377             p +=2;  /* Remove escaping.  */
378           mark = NULL;
379           while (*p && *p != '\n')
380             {
381               if (*p == ' ' || *p == '\t' || *p == '\r')
382                 mark = p;
383               else
384                 mark = NULL;
385               *dest++ = *p++;
386             }
387           if (mark)
388             *mark =0; /* Remove trailing white space.  */
389           if (*p == '\n')
390             {
391               if (p[-1] == '\r')
392                 *dest++ = '\r';
393               *dest++ = '\n';
394             }
395           if (p > p0)
396             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
397         }
398       else if (state == 2)
399         {
400           /* Armor line encountered.  */
401           p0 = p;
402           if (strncmp (p, "-----BEGIN PGP SIGNATURE-----", 29)
403               || !trailing_ws_p (p+29) )
404             fprintf (stderr,"Invalid clear signed message\n");
405           state = 3;
406           dest = stpcpy (dest, sig_header);
407         
408           while (*p && *p != '\n')
409             *dest++ = *p++;
410           if (*p == '\n')
411             {
412               if (p[-1] == '\r')
413                 *dest++ = '\r';
414               *dest++ = '\n';
415             }
416           if (p > p0)
417             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
418         }
419       else if (state == 3 && strncmp (p, "-----", 5))
420         {
421           /* Copy signature.  */
422           p0 = p;
423           while (*p && *p != '\n')
424             *dest++ = *p++;
425           if (*p == '\n')
426             {
427               if (p[-1] == '\r')
428                 *dest++ = '\r';
429               *dest++ = '\n';
430             }
431           if (p > p0)
432             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
433         }
434       else if (state == 3)
435         {
436           /* Armor line encountered.  */
437           p0 = p;
438           if (strncmp (p, "-----END PGP SIGNATURE-----", 27)
439               || !trailing_ws_p (p+27) )
440             fprintf (stderr,"Invalid clear signed message (no end)\n");
441           while (*p && *p != '\n')
442             *dest++ = *p++;
443           if (*p == '\n')
444             {
445               if (p[-1] == '\r')
446                 *dest++ = '\r';
447               *dest++ = '\n';
448             }
449           dest = stpcpy (dest, end_header);
450           if (p > p0)
451             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
452           break; /* Ah well, we can stop here.  */
453         }
454     }
455   if (!dest)
456     {
457       xfree (body);
458       return NULL;
459     }
460   *dest = 0;
461   *outputlen = strlen (body);
462
463   return body;
464 }
465
466
467
468 /* Verify MESSAGE and update the attachments as required.  MSGTYPE
469    should be the type of the message so that the fucntion can decide
470    what to do.  With FORCE set the verification is done regardlessless
471    of a cached signature result. */
472 int
473 message_verify (LPMESSAGE message, msgtype_t msgtype, int force, HWND hwnd)
474 {
475   HRESULT hr;
476   mapi_attach_item_t *table = NULL;
477   int moss_idx = -1;
478   int i;
479   char *inbuf;
480   size_t inbuflen;
481   protocol_t protocol = PROTOCOL_UNKNOWN;
482   int err;
483
484   switch (msgtype)
485     {
486     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
487       break;
488     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
489       log_debug ("Opaque signed message are not yet supported!");
490       return 0;
491     case MSGTYPE_GPGOL_CLEAR_SIGNED:
492       break;
493     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
494     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
495     case MSGTYPE_GPGOL_PGP_MESSAGE:
496       log_debug ("%s:%s: message of type %d not expected",
497                  SRCNAME, __func__, msgtype);
498       return -1; /* Should not be called for such a message.  */
499     case MSGTYPE_UNKNOWN:
500     case MSGTYPE_SMIME:
501     case MSGTYPE_GPGOL:
502       log_debug ("%s:%s: message of type %d ignored",
503                  SRCNAME, __func__, msgtype);
504       return 0; /* Nothing to do.  */
505     }
506   
507   /* If a verification is forced, we set the cached signature status
508      first to "?" to mark that no verification has yet happened.  If a
509      verification status has been set and the body attachment is
510      available we don't do a verification again.  The need to check
511      for the body attachment is to avoid problems if that attachment
512      has accidently be deleted. */
513   if (force)
514     mapi_set_sig_status (message, "?");
515   else if (mapi_test_sig_status (message) 
516            && !mapi_get_gpgol_body_attachment (message, NULL,NULL,NULL,NULL))
517     return 0; /* Already checked that message.  */
518
519   if (msgtype == MSGTYPE_GPGOL_CLEAR_SIGNED)
520     {
521       /* PGP's clear signed messages are special: All is contained in
522          the body and thus there is no requirement for an
523          attachment.  */
524       LPSTREAM rawstream;
525       
526       rawstream = mapi_get_body_as_stream (message);
527       if (!rawstream)
528         return -1;
529       
530       inbuf = pgp_mime_from_clearsigned (rawstream, &inbuflen);
531       rawstream->Release ();
532       if (!inbuf)
533         return -1;
534       protocol = PROTOCOL_OPENPGP;
535     }
536   else
537     {
538       /* PGP/MIME or S/MIME stuff.  */
539       table = mapi_create_attach_table (message, 0);
540       if (!table)
541         return -1; /* No attachment - this should not happen.  */
542
543       for (i=0; !table[i].end_of_table; i++)
544         if (table[i].attach_type == ATTACHTYPE_MOSS)
545           {
546             moss_idx = i;
547             break;
548           }
549       if (moss_idx == -1 && !table[0].end_of_table && table[1].end_of_table)
550         {
551           /* No MOSS flag found in the table but there is only one
552              attachment.  Due to the message type we know that this is
553              the original MOSS message.  We mark this attachment as
554              hidden, so that it won't get displayed.  We further mark
555              it as our original MOSS attachment so that after parsing
556              we have a mean to find it again (see above).  */ 
557           moss_idx = 0;
558           mapi_mark_moss_attach (message, table+0);
559         }
560       
561       if (moss_idx == -1)
562         {
563           mapi_release_attach_table (table);
564           return -1; /* No original attachment - this should not happen.  */
565         }
566
567       inbuf = mapi_get_attach (message, table+0, &inbuflen);
568       if (!inbuf)
569         {
570           mapi_release_attach_table (table);
571           return -1; /* Problem getting the attachment.  */
572         }
573     }
574
575   err = mime_verify (protocol, inbuf, inbuflen, message, hwnd, 0);
576   log_debug ("mime_verify returned %d", err);
577   if (err)
578     {
579       char buf[200];
580       
581       snprintf (buf, sizeof buf, "Verify failed (%s)", gpg_strerror (err));
582       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
583     }
584   xfree (inbuf);
585                     
586   if (err)
587     {
588       char buf[200];
589       snprintf (buf, sizeof buf, "- %s", gpg_strerror (err));
590       mapi_set_sig_status (message, gpg_strerror (err));
591     }
592   else
593     mapi_set_sig_status (message, "! Good signature");
594
595   hr = message->SaveChanges (KEEP_OPEN_READWRITE);
596   if (hr)
597     log_error_w32 (hr, "%s:%s: SaveChanges failed",
598                    SRCNAME, __func__); 
599
600
601   mapi_release_attach_table (table);
602   return 0;
603 }
604
605 /* Decrypt MESSAGE, check signature and update the attachments as
606    required.  MSGTYPE should be the type of the message so that the
607    function can decide what to do.  With FORCE set the decryption is
608    done regardless whether it has already been done.  */
609 int
610 message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force, HWND hwnd)
611 {
612   mapi_attach_item_t *table = NULL;
613   int part2_idx;
614   int tblidx;
615   int retval = -1;
616   LPSTREAM cipherstream;
617   gpg_error_t err;
618   int is_opaque = 0;
619   protocol_t protocol;
620   LPATTACH saved_attach = NULL;
621   int need_saved_attach = 0;
622   int need_rfc822_parser = 0;
623
624   switch (msgtype)
625     {
626     case MSGTYPE_UNKNOWN:
627     case MSGTYPE_SMIME:
628     case MSGTYPE_GPGOL:
629     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
630     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
631     case MSGTYPE_GPGOL_CLEAR_SIGNED:
632       return -1; /* Should not have been called for this.  */
633     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
634       break;
635     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
636       is_opaque = 1;
637       break;
638     case MSGTYPE_GPGOL_PGP_MESSAGE:
639       break;
640     }
641   
642   if (!force && mapi_test_last_decrypted (message))
643     return 0; /* Already decrypted this message once during this
644                  session.  No need to do it again. */
645
646   if (msgtype == MSGTYPE_GPGOL_PGP_MESSAGE)
647     {
648       /* PGP messages are special:  All is contained in the body and thus
649          there is no requirement for an attachment.  */
650       cipherstream = mapi_get_body_as_stream (message);
651       if (!cipherstream)
652         goto leave;
653       protocol = PROTOCOL_OPENPGP;
654     }
655   else
656     {
657       /* PGP/MIME or S/MIME stuff.  */
658       table = mapi_create_attach_table (message, 0);
659       if (!table)
660         goto leave; /* No attachment - this should not happen.  */
661
662       if (is_opaque)
663         {
664           /* S/MIME opaque encrypted message: We expect one
665              attachment.  As we don't know wether we are called the
666              first time, we first try to find this attachment by
667              looking at all attachments.  Only if this fails we
668              identify it by its order.  */
669           part2_idx = -1;
670           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
671             if (table[tblidx].attach_type == ATTACHTYPE_MOSSTEMPL)
672               {
673                 /* This attachment has been generated by us in the
674                    course of sending a new message.  The content will
675                    be multipart/signed because we used this to trick
676                    out OL.  We stop here and use this part for further
677                    processing.  */
678                 part2_idx = tblidx;
679                 need_rfc822_parser = 1;
680                 break;
681               }
682             else if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
683               {
684                 if (part2_idx == -1 && table[tblidx].content_type 
685                     && (!strcmp (table[tblidx].content_type,
686                                  "application/pkcs7-mime")
687                         || !strcmp (table[tblidx].content_type,
688                                     "application/x-pkcs7-mime")))
689                   part2_idx = tblidx;
690               }
691           if (part2_idx == -1 && tblidx >= 1)
692             {
693               /* We have attachments but none are marked.  Thus we
694                  assume that this is the first time we see this
695                  message and we will set the mark now if we see
696                  appropriate content types. */
697               if (table[0].content_type               
698                   && (!strcmp (table[0].content_type, "application/pkcs7-mime")
699                       || !strcmp (table[0].content_type,
700                                   "application/x-pkcs7-mime")))
701                 part2_idx = 0;
702               if (part2_idx != -1)
703                 mapi_mark_moss_attach (message, table+part2_idx);
704             }
705           if (part2_idx == -1)
706             {
707               log_debug ("%s:%s: this is not an S/MIME encrypted message",
708                          SRCNAME, __func__);
709               goto leave;
710             }
711           protocol = PROTOCOL_SMIME;
712         }
713       else 
714         {
715           /* Multipart/encrypted message: We expect 2 attachments.
716              The first one with the version number and the second one
717              with the ciphertext.  As we don't know wether we are
718              called the first time, we first try to find these
719              attachments by looking at all attachments.  Only if this
720              fails we identify them by their order (i.e. the first 2
721              attachments) and mark them as part1 and part2.  */
722           int part1_idx;
723           
724           part1_idx = part2_idx = -1;
725           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
726             if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
727               {
728                 if (part1_idx == -1 && table[tblidx].content_type 
729                     && !strcmp (table[tblidx].content_type,
730                                 "application/pgp-encrypted"))
731                   part1_idx = tblidx;
732                 else if (part2_idx == -1 && table[tblidx].content_type 
733                          && !strcmp (table[tblidx].content_type,
734                                      "application/octet-stream"))
735                   part2_idx = tblidx;
736               }
737           if (part1_idx == -1 && part2_idx == -1 && tblidx >= 2)
738             {
739               /* At least 2 attachments but none are marked.  Thus we
740                  assume that this is the first time we see this
741                  message and we will set the mark now if we see
742                  appropriate content types. */
743               if (table[0].content_type               
744                   && !strcmp (table[0].content_type,
745                               "application/pgp-encrypted"))
746                 part1_idx = 0;
747               if (table[1].content_type             
748                   && !strcmp (table[1].content_type, 
749                               "application/octet-stream"))
750                 part2_idx = 1;
751               if (part1_idx != -1 && part2_idx != -1)
752                 {
753                   mapi_mark_moss_attach (message, table+part1_idx);
754                   mapi_mark_moss_attach (message, table+part2_idx);
755                 }
756             }
757
758
759           if (part1_idx == -1 || part2_idx == -1 
760               && !table[0].end_of_table && table[1].end_of_table
761               && table[0].attach_type == ATTACHTYPE_MOSS
762               && table[0].filename 
763               && !strcmp (table[0].filename, MIMEATTACHFILENAME))
764             {
765               /* This is likely a PGP/MIME created by us.  Due to the
766                  way we created that message, the MAPI derived content
767                  type is wrong and there is only one attachtment
768                  (gpgolXXX.dat).  We simply assume that it is PGP/MIME
769                  encrypted and pass it on to the mime parser.  We also
770                  keep the attachment open so that we can later set it
771                  to hidden if not yet done.  I can't remember whether
772                  it is possible to set the hidden attribute when
773                  creating the message - probably not.  Thus we take
774                  care of it here. */
775               log_debug ("%s:%s: "
776                          "assuming self-created PGP/MIME encrypted message",
777                          SRCNAME, __func__);
778               part2_idx = 0;
779               need_saved_attach = 1;
780             }
781           else if (part1_idx == -1 || part2_idx == -1)
782             {
783               log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
784                          SRCNAME, __func__);
785               goto leave;
786             }
787           protocol = PROTOCOL_OPENPGP;
788         }
789       
790       cipherstream = mapi_get_attach_as_stream (message, table+part2_idx,
791                                                 need_saved_attach? 
792                                                 &saved_attach : NULL );
793       if (!cipherstream)
794         goto leave; /* Problem getting the attachment.  */
795     }
796
797   err = mime_decrypt (protocol, cipherstream, message, 
798                       need_rfc822_parser, hwnd, 0);
799   log_debug ("mime_decrypt returned %d (%s)", err, gpg_strerror (err));
800   if (err)
801     {
802       char buf[200];
803       
804       snprintf (buf, sizeof buf, "Decryption failed (%s)", gpg_strerror (err));
805       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
806     }
807   else
808     {
809       if (saved_attach)
810         mapi_set_attach_hidden (saved_attach);
811     }
812   cipherstream->Release ();
813   retval = 0;
814
815
816  leave:
817   if (saved_attach)
818     saved_attach->Release ();
819   mapi_release_attach_table (table);
820   return retval;
821 }
822
823
824 \f
825
826 /* Return an array of strings with the recipients of the message. On
827    success a malloced array is returned containing allocated strings
828    for each recipient.  The end of the array is marked by NULL.
829    Caller is responsible for releasing the array.  On failure NULL is
830    returned.  */
831 static char **
832 get_recipients (LPMESSAGE message)
833 {
834   static SizedSPropTagArray (1L, PropRecipientNum) = {1L, {PR_EMAIL_ADDRESS}};
835   HRESULT hr;
836   LPMAPITABLE lpRecipientTable = NULL;
837   LPSRowSet lpRecipientRows = NULL;
838   unsigned int rowidx;
839   LPSPropValue row;
840   char **rset;
841   int rsetidx;
842
843
844   if (!message)
845     return NULL;
846
847   hr = message->GetRecipientTable (0, &lpRecipientTable);
848   if (FAILED (hr)) 
849     {
850       log_debug_w32 (-1, "%s:%s: GetRecipientTable failed", SRCNAME, __func__);
851       return NULL;
852     }
853
854   hr = HrQueryAllRows (lpRecipientTable, (LPSPropTagArray) &PropRecipientNum,
855                        NULL, NULL, 0L, &lpRecipientRows);
856   if (FAILED (hr)) 
857     {
858       log_debug_w32 (-1, "%s:%s: HrQueryAllRows failed", SRCNAME, __func__);
859       if (lpRecipientTable)
860         lpRecipientTable->Release();
861       return NULL;
862     }
863
864   rset = (char**)xcalloc (lpRecipientRows->cRows+1, sizeof *rset);
865
866   for (rowidx=0, rsetidx=0; rowidx < lpRecipientRows->cRows; rowidx++)
867     {
868       if (!lpRecipientRows->aRow[rowidx].cValues)
869         continue;
870       row = lpRecipientRows->aRow[rowidx].lpProps;
871
872       switch (PROP_TYPE (row->ulPropTag))
873         {
874         case PT_UNICODE:
875           if ((rset[rsetidx] = wchar_to_utf8 (row->Value.lpszW)))
876             rsetidx++;
877           else
878             log_debug ("%s:%s: error converting recipient to utf8\n",
879                        SRCNAME, __func__);
880           break;
881       
882         case PT_STRING8: /* Assume ASCII. */
883           rset[rsetidx++] = xstrdup (row->Value.lpszA);
884           break;
885           
886         default:
887           log_debug ("%s:%s: proptag=0x%08lx not supported\n",
888                      SRCNAME, __func__, row->ulPropTag);
889           break;
890         }
891     }
892
893   if (lpRecipientTable)
894     lpRecipientTable->Release();
895   if (lpRecipientRows)
896     FreeProws(lpRecipientRows); 
897   
898   log_debug ("%s:%s: got %d recipients:\n", SRCNAME, __func__, rsetidx);
899   for (rsetidx=0; rset[rsetidx]; rsetidx++)
900     log_debug ("%s:%s: \t`%s'\n", SRCNAME, __func__, rset[rsetidx]);
901
902   return rset;
903 }
904
905
906 static void
907 release_recipient_array (char **recipients)
908 {
909   int idx;
910
911   if (recipients)
912     {
913       for (idx=0; recipients[idx]; idx++)
914         xfree (recipients[idx]);
915       xfree (recipients);
916     }
917 }
918
919
920
921 static int
922 sign_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd, int signflag)
923 {
924   gpg_error_t err;
925   char **recipients;
926
927   recipients = get_recipients (message);
928   if (!recipients || !recipients[0])
929     {
930       MessageBox (hwnd, _("No recipients to encrypt to are given"),
931                   "GpgOL", MB_ICONERROR|MB_OK);
932
933       err = gpg_error (GPG_ERR_GENERAL);
934     }
935   else
936     {
937       if (signflag)
938         err = mime_sign_encrypt (message, hwnd, protocol, recipients);
939       else
940         err = mime_encrypt (message, hwnd, protocol, recipients);
941       if (err)
942         {
943           char buf[200];
944           
945           snprintf (buf, sizeof buf,
946                     _("Encryption failed (%s)"), gpg_strerror (err));
947           MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
948         }
949     }
950   release_recipient_array (recipients);
951   return err;
952 }
953
954
955 /* Sign the MESSAGE.  */
956 int 
957 message_sign (LPMESSAGE message, protocol_t protocol, HWND hwnd)
958 {
959   gpg_error_t err;
960
961   err = mime_sign (message, hwnd, protocol);
962   if (err)
963     {
964       char buf[200];
965       
966       snprintf (buf, sizeof buf,
967                 _("Signing failed (%s)"), gpg_strerror (err));
968       MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
969     }
970   return err;
971 }
972
973
974
975 /* Encrypt the MESSAGE.  */
976 int 
977 message_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd)
978 {
979   return sign_encrypt (message, protocol, hwnd, 0);
980 }
981
982
983 /* Sign+Encrypt the MESSAGE.  */
984 int 
985 message_sign_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd)
986 {
987   return sign_encrypt (message, protocol, hwnd, 1);
988 }
989
990
991