Preparing for a new release.
[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       if (mapi_has_last_decrypted (message))
194         {
195           SPropTagArray proparray;
196           int anyokay = 0;
197           
198           proparray.cValues = 1;
199           proparray.aulPropTag[0] = PR_BODY;
200           hr = message->DeleteProps (&proparray, NULL);
201           if (hr)
202             log_debug_w32 (hr, "%s:%s: deleting PR_BODY failed",
203                            SRCNAME, __func__);
204           else
205             anyokay++;
206           
207           proparray.cValues = 1;
208           proparray.aulPropTag[0] = PR_BODY_HTML;
209           message->DeleteProps (&proparray, NULL);
210           if (hr)
211             log_debug_w32 (hr, "%s:%s: deleting PR_BODY_HTML failed", 
212                            SRCNAME, __func__);
213           else
214             anyokay++;
215
216           if (anyokay)
217             {
218               hr = message->SaveChanges (KEEP_OPEN_READWRITE);
219               if (hr)
220                 log_error_w32 (hr, "%s:%s: SaveChanges failed",
221                                SRCNAME, __func__); 
222               else
223                 log_debug ("%s:%s: SaveChanges succeded; body cruft removed",
224                            SRCNAME, __func__); 
225             }
226         }  
227       else
228         log_debug_w32 (hr, "%s:%s: error getting message", 
229                        SRCNAME, __func__);
230      
231       ul_release (message, __func__);
232       ul_release (mdb, __func__);
233     }
234 }
235
236
237
238 /* Display some information about MESSAGE.  */
239 void
240 message_show_info (LPMESSAGE message, HWND hwnd)
241 {
242   char *msgcls = mapi_get_message_class (message);
243   char *sigstat = mapi_get_sig_status (message);
244   char *mimeinfo = mapi_get_mime_info (message);
245   size_t buflen;
246   char *buffer;
247
248   buflen = strlen (msgcls) + strlen (sigstat) + strlen (mimeinfo) + 200;
249   buffer = (char*)xmalloc (buflen+1);
250   snprintf (buffer, buflen, 
251             _("Message class: %s\n"
252               "Sig Status   : %s\n"
253               "Structure of the message:\n"
254               "%s"), 
255             msgcls,
256             sigstat,
257             mimeinfo);
258   
259   MessageBox (hwnd, buffer, _("GpgOL - Message Information"),
260               MB_ICONINFORMATION|MB_OK);
261   xfree (buffer);
262   xfree (mimeinfo);
263   xfree (sigstat);
264   xfree (msgcls);
265 }
266
267
268
269 \f
270 /* Convert the clear signed message from INPUT into a PS?MIME signed
271    message and return it in a new allocated buffer.  OUTPUTLEN
272    received the valid length of that buffer; the buffer is guarnateed
273    to be Nul terminated.  */
274 static char *
275 pgp_mime_from_clearsigned (LPSTREAM input, size_t *outputlen)
276 {
277   HRESULT hr;
278   STATSTG statinfo;
279   ULONG nread;
280   char *body = NULL;
281   char *p, *p0, *dest, *mark;
282   char boundary[BOUNDARYSIZE+1];
283   char top_header[200 + 2*BOUNDARYSIZE]; 
284   char sig_header[100 + BOUNDARYSIZE]; 
285   char end_header[10 + BOUNDARYSIZE];
286   size_t reserved_space;
287   int state;
288
289   *outputlen = 0;
290
291   /* Note that our parser does not make use of the micalg parameter.  */
292   generate_boundary (boundary);
293   snprintf (top_header, sizeof top_header,
294             "MIME-Version: 1.0\r\n"
295             "Content-Type: multipart/signed; boundary=\"%s\";\r\n"
296             "              protocol=\"application/pgp-signature\"\r\n"
297             "\r\n"
298             "--%s\r\n", boundary, boundary);
299   snprintf (sig_header, sizeof sig_header,
300             "--%s\r\n"
301             "Content-Type: application/pgp-signature\r\n"
302             "\r\n", boundary);
303   snprintf (end_header, sizeof end_header,
304             "\r\n"
305             "--%s--\r\n", boundary);
306   reserved_space = (strlen (top_header) + strlen (sig_header) 
307                     + strlen (end_header)+ 100);
308
309   hr = input->Stat (&statinfo, STATFLAG_NONAME);
310   if (hr)
311     {
312       log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
313       return NULL;
314     }
315       
316   body = (char*)xmalloc (reserved_space
317                          + (size_t)statinfo.cbSize.QuadPart + 2);
318   hr = input->Read (body+reserved_space,
319                     (size_t)statinfo.cbSize.QuadPart, &nread);
320   if (hr)
321     {
322       log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
323       xfree (body);
324       return NULL;
325     }
326   body[reserved_space + nread] = 0;
327   body[reserved_space + nread+1] = 0;  /* Just in case this is
328                                           accidently an wchar_t. */
329   if (nread != statinfo.cbSize.QuadPart)
330     {
331       log_debug ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
332       xfree (body);
333       return NULL;
334     }
335
336   /* We do in place conversion. */
337   state = 0;
338   dest = NULL;
339   for (p=body+reserved_space; p && *p; p = (p=strchr (p+1, '\n'))? (p+1):NULL)
340     {
341       if (!state && !strncmp (p, "-----BEGIN PGP SIGNED MESSAGE-----", 34)
342           && trailing_ws_p (p+34) )
343         {
344           dest = stpcpy (body, top_header);
345           state = 1;
346         }
347       else if (state == 1)
348         {
349           /* Wait for an empty line.  */
350           if (trailing_ws_p (p))
351             state = 2;
352         }
353       else if (state == 2 && strncmp (p, "-----", 5))
354         {
355           /* Copy signed data. */
356           p0 = p;
357           if (*p == '-' && p[1] == ' ')
358             p +=2;  /* Remove escaping.  */
359           mark = NULL;
360           while (*p && *p != '\n')
361             {
362               if (*p == ' ' || *p == '\t' || *p == '\r')
363                 mark = p;
364               else
365                 mark = NULL;
366               *dest++ = *p++;
367             }
368           if (mark)
369             *mark =0; /* Remove trailing white space.  */
370           if (*p == '\n')
371             {
372               if (p[-1] == '\r')
373                 *dest++ = '\r';
374               *dest++ = '\n';
375             }
376           if (p > p0)
377             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
378         }
379       else if (state == 2)
380         {
381           /* Armor line encountered.  */
382           p0 = p;
383           if (strncmp (p, "-----BEGIN PGP SIGNATURE-----", 29)
384               || !trailing_ws_p (p+29) )
385             fprintf (stderr,"Invalid clear signed message\n");
386           state = 3;
387           dest = stpcpy (dest, sig_header);
388         
389           while (*p && *p != '\n')
390             *dest++ = *p++;
391           if (*p == '\n')
392             {
393               if (p[-1] == '\r')
394                 *dest++ = '\r';
395               *dest++ = '\n';
396             }
397           if (p > p0)
398             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
399         }
400       else if (state == 3 && strncmp (p, "-----", 5))
401         {
402           /* Copy signature.  */
403           p0 = p;
404           while (*p && *p != '\n')
405             *dest++ = *p++;
406           if (*p == '\n')
407             {
408               if (p[-1] == '\r')
409                 *dest++ = '\r';
410               *dest++ = '\n';
411             }
412           if (p > p0)
413             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
414         }
415       else if (state == 3)
416         {
417           /* Armor line encountered.  */
418           p0 = p;
419           if (strncmp (p, "-----END PGP SIGNATURE-----", 27)
420               || !trailing_ws_p (p+27) )
421             fprintf (stderr,"Invalid clear signed message (no end)\n");
422           while (*p && *p != '\n')
423             *dest++ = *p++;
424           if (*p == '\n')
425             {
426               if (p[-1] == '\r')
427                 *dest++ = '\r';
428               *dest++ = '\n';
429             }
430           dest = stpcpy (dest, end_header);
431           if (p > p0)
432             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
433           break; /* Ah well, we can stop here.  */
434         }
435     }
436   if (!dest)
437     {
438       xfree (body);
439       return NULL;
440     }
441   *dest = 0;
442   *outputlen = strlen (body);
443
444   return body;
445 }
446
447
448
449 /* Verify MESSAGE and update the attachments as required.  MSGTYPE
450    should be the type of the message so that the fucntion can decide
451    what to do.  With FORCE set the verification is done regardlessless
452    of a cached signature result. */
453 int
454 message_verify (LPMESSAGE message, msgtype_t msgtype, int force, HWND hwnd)
455 {
456   HRESULT hr;
457   mapi_attach_item_t *table = NULL;
458   int moss_idx = -1;
459   int i;
460   char *inbuf;
461   size_t inbuflen;
462   protocol_t protocol = PROTOCOL_UNKNOWN;
463   int err;
464
465   switch (msgtype)
466     {
467     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
468       break;
469     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
470       log_debug ("Opaque signed message are not yet supported!");
471       return 0;
472     case MSGTYPE_GPGOL_CLEAR_SIGNED:
473       break;
474     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
475     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
476     case MSGTYPE_GPGOL_PGP_MESSAGE:
477       log_debug ("%s:%s: message of type %d not expected",
478                  SRCNAME, __func__, msgtype);
479       return -1; /* Should not be called for such a message.  */
480     case MSGTYPE_UNKNOWN:
481     case MSGTYPE_SMIME:
482     case MSGTYPE_GPGOL:
483       log_debug ("%s:%s: message of type %d ignored",
484                  SRCNAME, __func__, msgtype);
485       return 0; /* Nothing to do.  */
486     }
487   
488   /* If a verification is forced, we set the cached signature status
489      first to "?" to mark that no verification has yet happened.  If a
490      verification status has been set and the body attachment is
491      available we don't do a verification again.  The need to check
492      for the body attachment is to avoid problems if that attachment
493      has accidently be deleted. */
494   if (force)
495     mapi_set_sig_status (message, "?");
496   else if (mapi_test_sig_status (message) 
497            && !mapi_get_gpgol_body_attachment (message, NULL,NULL,NULL,NULL))
498     return 0; /* Already checked that message.  */
499
500   if (msgtype == MSGTYPE_GPGOL_CLEAR_SIGNED)
501     {
502       /* PGP's clear signed messages are special: All is contained in
503          the body and thus there is no requirement for an
504          attachment.  */
505       LPSTREAM rawstream;
506       
507       rawstream = mapi_get_body_as_stream (message);
508       if (!rawstream)
509         return -1;
510       
511       inbuf = pgp_mime_from_clearsigned (rawstream, &inbuflen);
512       rawstream->Release ();
513       if (!inbuf)
514         return -1;
515       protocol = PROTOCOL_OPENPGP;
516     }
517   else
518     {
519       /* PGP/MIME or S/MIME stuff.  */
520       table = mapi_create_attach_table (message, 0);
521       if (!table)
522         return -1; /* No attachment - this should not happen.  */
523
524       for (i=0; !table[i].end_of_table; i++)
525         if (table[i].attach_type == ATTACHTYPE_MOSS)
526           {
527             moss_idx = i;
528             break;
529           }
530       if (moss_idx == -1 && !table[0].end_of_table && table[1].end_of_table)
531         {
532           /* No MOSS flag found in the table but there is only one
533              attachment.  Due to the message type we know that this is
534              the original MOSS message.  We mark this attachment as
535              hidden, so that it won't get displayed.  We further mark
536              it as our original MOSS attachment so that after parsing
537              we have a mean to find it again (see above).  */ 
538           moss_idx = 0;
539           mapi_mark_moss_attach (message, table+0);
540         }
541       
542       if (moss_idx == -1)
543         {
544           mapi_release_attach_table (table);
545           return -1; /* No original attachment - this should not happen.  */
546         }
547
548       inbuf = mapi_get_attach (message, table+0, &inbuflen);
549       if (!inbuf)
550         {
551           mapi_release_attach_table (table);
552           return -1; /* Problem getting the attachment.  */
553         }
554     }
555
556   err = mime_verify (protocol, inbuf, inbuflen, message, hwnd, 0);
557   log_debug ("mime_verify returned %d", err);
558   if (err)
559     {
560       char buf[200];
561       
562       snprintf (buf, sizeof buf, "Verify failed (%s)", gpg_strerror (err));
563       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
564     }
565   xfree (inbuf);
566                     
567   if (err)
568     {
569       char buf[200];
570       snprintf (buf, sizeof buf, "- %s", gpg_strerror (err));
571       mapi_set_sig_status (message, gpg_strerror (err));
572     }
573   else
574     mapi_set_sig_status (message, "! Good signature");
575
576   hr = message->SaveChanges (KEEP_OPEN_READWRITE);
577   if (hr)
578     log_error_w32 (hr, "%s:%s: SaveChanges failed",
579                    SRCNAME, __func__); 
580
581
582   mapi_release_attach_table (table);
583   return 0;
584 }
585
586 /* Decrypt MESSAGE, check signature and update the attachments as
587    required.  MSGTYPE should be the type of the message so that the
588    function can decide what to do.  With FORCE set the decryption is
589    done regardless whether it has already been done.  */
590 int
591 message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force, HWND hwnd)
592 {
593   mapi_attach_item_t *table = NULL;
594   int part2_idx;
595   int tblidx;
596   int retval = -1;
597   LPSTREAM cipherstream;
598   gpg_error_t err;
599   int is_opaque = 0;
600   protocol_t protocol;
601   LPATTACH saved_attach = NULL;
602   int need_saved_attach = 0;
603   int need_rfc822_parser = 0;
604
605   switch (msgtype)
606     {
607     case MSGTYPE_UNKNOWN:
608     case MSGTYPE_SMIME:
609     case MSGTYPE_GPGOL:
610     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
611     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
612     case MSGTYPE_GPGOL_CLEAR_SIGNED:
613       return -1; /* Should not have been called for this.  */
614     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
615       break;
616     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
617       is_opaque = 1;
618       break;
619     case MSGTYPE_GPGOL_PGP_MESSAGE:
620       break;
621     }
622   
623   if (!force && mapi_test_last_decrypted (message))
624     return 0; /* Already decrypted this message once during this
625                  session.  No need to do it again. */
626
627   if (msgtype == MSGTYPE_GPGOL_PGP_MESSAGE)
628     {
629       /* PGP messages are special:  All is contained in the body and thus
630          there is no requirement for an attachment.  */
631       cipherstream = mapi_get_body_as_stream (message);
632       if (!cipherstream)
633         goto leave;
634       protocol = PROTOCOL_OPENPGP;
635     }
636   else
637     {
638       /* PGP/MIME or S/MIME stuff.  */
639       table = mapi_create_attach_table (message, 0);
640       if (!table)
641         goto leave; /* No attachment - this should not happen.  */
642
643       if (is_opaque)
644         {
645           /* S/MIME opaque encrypted message: We expect one
646              attachment.  As we don't know wether we are called the
647              first time, we first try to find this attachment by
648              looking at all attachments.  Only if this fails we
649              identify it by its order.  */
650           part2_idx = -1;
651           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
652             if (table[tblidx].attach_type == ATTACHTYPE_MOSSTEMPL)
653               {
654                 /* This attachment has been generated by us in the
655                    course of sending a new message.  The content will
656                    be multipart/signed because we used this to trick
657                    out OL.  We stop here and use this part for further
658                    processing.  */
659                 part2_idx = tblidx;
660                 need_rfc822_parser = 1;
661                 break;
662               }
663             else if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
664               {
665                 if (part2_idx == -1 && table[tblidx].content_type 
666                     && (!strcmp (table[tblidx].content_type,
667                                  "application/pkcs7-mime")
668                         || !strcmp (table[tblidx].content_type,
669                                     "application/x-pkcs7-mime")))
670                   part2_idx = tblidx;
671               }
672           if (part2_idx == -1 && tblidx >= 1)
673             {
674               /* We have attachments but none are marked.  Thus we
675                  assume that this is the first time we see this
676                  message and we will set the mark now if we see
677                  appropriate content types. */
678               if (table[0].content_type               
679                   && (!strcmp (table[0].content_type, "application/pkcs7-mime")
680                       || !strcmp (table[0].content_type,
681                                   "application/x-pkcs7-mime")))
682                 part2_idx = 0;
683               if (part2_idx != -1)
684                 mapi_mark_moss_attach (message, table+part2_idx);
685             }
686           if (part2_idx == -1)
687             {
688               log_debug ("%s:%s: this is not an S/MIME encrypted message",
689                          SRCNAME, __func__);
690               goto leave;
691             }
692           protocol = PROTOCOL_SMIME;
693         }
694       else 
695         {
696           /* Multipart/encrypted message: We expect 2 attachments.
697              The first one with the version number and the second one
698              with the ciphertext.  As we don't know wether we are
699              called the first time, we first try to find these
700              attachments by looking at all attachments.  Only if this
701              fails we identify them by their order (i.e. the first 2
702              attachments) and mark them as part1 and part2.  */
703           int part1_idx;
704           
705           part1_idx = part2_idx = -1;
706           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
707             if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
708               {
709                 if (part1_idx == -1 && table[tblidx].content_type 
710                     && !strcmp (table[tblidx].content_type,
711                                 "application/pgp-encrypted"))
712                   part1_idx = tblidx;
713                 else if (part2_idx == -1 && table[tblidx].content_type 
714                          && !strcmp (table[tblidx].content_type,
715                                      "application/octet-stream"))
716                   part2_idx = tblidx;
717               }
718           if (part1_idx == -1 && part2_idx == -1 && tblidx >= 2)
719             {
720               /* At least 2 attachments but none are marked.  Thus we
721                  assume that this is the first time we see this
722                  message and we will set the mark now if we see
723                  appropriate content types. */
724               if (table[0].content_type               
725                   && !strcmp (table[0].content_type,
726                               "application/pgp-encrypted"))
727                 part1_idx = 0;
728               if (table[1].content_type             
729                   && !strcmp (table[1].content_type, 
730                               "application/octet-stream"))
731                 part2_idx = 1;
732               if (part1_idx != -1 && part2_idx != -1)
733                 {
734                   mapi_mark_moss_attach (message, table+part1_idx);
735                   mapi_mark_moss_attach (message, table+part2_idx);
736                 }
737             }
738
739
740           if (part1_idx == -1 || part2_idx == -1 
741               && !table[0].end_of_table && table[1].end_of_table
742               && table[0].attach_type == ATTACHTYPE_MOSS
743               && table[0].filename 
744               && !strcmp (table[0].filename, MIMEATTACHFILENAME))
745             {
746               /* This is likely a PGP/MIME created by us.  Due to the
747                  way we created that message, the MAPI derived content
748                  type is wrong and there is only one attachtment
749                  (gpgolXXX.dat).  We simply assume that it is PGP/MIME
750                  encrypted and pass it on to the mime parser.  We also
751                  keep the attachment open so that we can later set it
752                  to hidden if not yet done.  I can't remember whether
753                  it is possible to set the hidden attribute when
754                  creating the message - probably not.  Thus we take
755                  care of it here. */
756               log_debug ("%s:%s: "
757                          "assuming self-created PGP/MIME encrypted message",
758                          SRCNAME, __func__);
759               part2_idx = 0;
760               need_saved_attach = 1;
761             }
762           else if (part1_idx == -1 || part2_idx == -1)
763             {
764               log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
765                          SRCNAME, __func__);
766               goto leave;
767             }
768           protocol = PROTOCOL_OPENPGP;
769         }
770       
771       cipherstream = mapi_get_attach_as_stream (message, table+part2_idx,
772                                                 need_saved_attach? 
773                                                 &saved_attach : NULL );
774       if (!cipherstream)
775         goto leave; /* Problem getting the attachment.  */
776     }
777
778   err = mime_decrypt (protocol, cipherstream, message, 
779                       need_rfc822_parser, hwnd, 0);
780   log_debug ("mime_decrypt returned %d (%s)", err, gpg_strerror (err));
781   if (err)
782     {
783       char buf[200];
784       
785       snprintf (buf, sizeof buf, "Decryption failed (%s)", gpg_strerror (err));
786       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
787     }
788   else
789     {
790       if (saved_attach)
791         mapi_set_attach_hidden (saved_attach);
792     }
793   cipherstream->Release ();
794   retval = 0;
795
796
797  leave:
798   if (saved_attach)
799     saved_attach->Release ();
800   mapi_release_attach_table (table);
801   return retval;
802 }
803
804
805 \f
806
807 /* Return an array of strings with the recipients of the message. On
808    success a malloced array is returned containing allocated strings
809    for each recipient.  The end of the array is marked by NULL.
810    Caller is responsible for releasing the array.  On failure NULL is
811    returned.  */
812 static char **
813 get_recipients (LPMESSAGE message)
814 {
815   static SizedSPropTagArray (1L, PropRecipientNum) = {1L, {PR_EMAIL_ADDRESS}};
816   HRESULT hr;
817   LPMAPITABLE lpRecipientTable = NULL;
818   LPSRowSet lpRecipientRows = NULL;
819   unsigned int rowidx;
820   LPSPropValue row;
821   char **rset;
822   int rsetidx;
823
824
825   if (!message)
826     return NULL;
827
828   hr = message->GetRecipientTable (0, &lpRecipientTable);
829   if (FAILED (hr)) 
830     {
831       log_debug_w32 (-1, "%s:%s: GetRecipientTable failed", SRCNAME, __func__);
832       return NULL;
833     }
834
835   hr = HrQueryAllRows (lpRecipientTable, (LPSPropTagArray) &PropRecipientNum,
836                        NULL, NULL, 0L, &lpRecipientRows);
837   if (FAILED (hr)) 
838     {
839       log_debug_w32 (-1, "%s:%s: HrQueryAllRows failed", SRCNAME, __func__);
840       if (lpRecipientTable)
841         lpRecipientTable->Release();
842       return NULL;
843     }
844
845   rset = (char**)xcalloc (lpRecipientRows->cRows+1, sizeof *rset);
846
847   for (rowidx=0, rsetidx=0; rowidx < lpRecipientRows->cRows; rowidx++)
848     {
849       if (!lpRecipientRows->aRow[rowidx].cValues)
850         continue;
851       row = lpRecipientRows->aRow[rowidx].lpProps;
852
853       switch (PROP_TYPE (row->ulPropTag))
854         {
855         case PT_UNICODE:
856           if ((rset[rsetidx] = wchar_to_utf8 (row->Value.lpszW)))
857             rsetidx++;
858           else
859             log_debug ("%s:%s: error converting recipient to utf8\n",
860                        SRCNAME, __func__);
861           break;
862       
863         case PT_STRING8: /* Assume ASCII. */
864           rset[rsetidx++] = xstrdup (row->Value.lpszA);
865           break;
866           
867         default:
868           log_debug ("%s:%s: proptag=0x%08lx not supported\n",
869                      SRCNAME, __func__, row->ulPropTag);
870           break;
871         }
872     }
873
874   if (lpRecipientTable)
875     lpRecipientTable->Release();
876   if (lpRecipientRows)
877     FreeProws(lpRecipientRows); 
878   
879   log_debug ("%s:%s: got %d recipients:\n", SRCNAME, __func__, rsetidx);
880   for (rsetidx=0; rset[rsetidx]; rsetidx++)
881     log_debug ("%s:%s: \t`%s'\n", SRCNAME, __func__, rset[rsetidx]);
882
883   return rset;
884 }
885
886
887 static void
888 release_recipient_array (char **recipients)
889 {
890   int idx;
891
892   if (recipients)
893     {
894       for (idx=0; recipients[idx]; idx++)
895         xfree (recipients[idx]);
896       xfree (recipients);
897     }
898 }
899
900
901
902 static int
903 sign_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd, int signflag)
904 {
905   gpg_error_t err;
906   char **recipients;
907
908   recipients = get_recipients (message);
909   if (!recipients || !recipients[0])
910     {
911       MessageBox (hwnd, _("No recipients to encrypt to are given"),
912                   "GpgOL", MB_ICONERROR|MB_OK);
913
914       err = gpg_error (GPG_ERR_GENERAL);
915     }
916   else
917     {
918       if (signflag)
919         err = mime_sign_encrypt (message, hwnd, protocol, recipients);
920       else
921         err = mime_encrypt (message, hwnd, protocol, recipients);
922       if (err)
923         {
924           char buf[200];
925           
926           snprintf (buf, sizeof buf,
927                     _("Encryption failed (%s)"), gpg_strerror (err));
928           MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
929         }
930     }
931   release_recipient_array (recipients);
932   return err;
933 }
934
935
936 /* Sign the MESSAGE.  */
937 int 
938 message_sign (LPMESSAGE message, protocol_t protocol, HWND hwnd)
939 {
940   gpg_error_t err;
941
942   err = mime_sign (message, hwnd, protocol);
943   if (err)
944     {
945       char buf[200];
946       
947       snprintf (buf, sizeof buf,
948                 _("Signing failed (%s)"), gpg_strerror (err));
949       MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
950     }
951   return err;
952 }
953
954
955
956 /* Encrypt the MESSAGE.  */
957 int 
958 message_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd)
959 {
960   return sign_encrypt (message, protocol, hwnd, 0);
961 }
962
963
964 /* Sign+Encrypt the MESSAGE.  */
965 int 
966 message_sign_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd)
967 {
968   return sign_encrypt (message, protocol, hwnd, 1);
969 }
970
971
972