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