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