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