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