More polishing.
[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 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
17  * License along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <windows.h>
26
27 #include "mymapi.h"
28 #include "mymapitags.h"
29 #include "myexchext.h"
30 #include "common.h"
31 #include "mapihelp.h"
32 #include "mimeparser.h"
33 #include "mimemaker.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 /* Convert the clear signed message from INPUT into a PS?MIME signed
42    message and return it in a new allocated buffer.  OUTPUTLEN
43    received the valid length of that buffer; the buffer is guarnateed
44    to be Nul terminated.  */
45 static char *
46 pgp_mime_from_clearsigned (LPSTREAM input, size_t *outputlen)
47 {
48   HRESULT hr;
49   STATSTG statinfo;
50   ULONG nread;
51   char *body = NULL;
52   char *p, *p0, *dest, *mark;
53   char boundary[BOUNDARYSIZE+1];
54   char top_header[200 + 2*BOUNDARYSIZE]; 
55   char sig_header[100 + BOUNDARYSIZE]; 
56   char end_header[10 + BOUNDARYSIZE];
57   size_t reserved_space;
58   int state;
59
60   *outputlen = 0;
61
62   /* Note that our parser does not make use of the micalg parameter.  */
63   generate_boundary (boundary);
64   snprintf (top_header, sizeof top_header,
65             "MIME-Version: 1.0\r\n"
66             "Content-Type: multipart/signed; boundary=\"%s\";\r\n"
67             "              protocol=\"application/pgp-signature\"\r\n"
68             "\r\n"
69             "--%s\r\n", boundary, boundary);
70   snprintf (sig_header, sizeof sig_header,
71             "--%s\r\n"
72             "Content-Type: application/pgp-signature\r\n"
73             "\r\n", boundary);
74   snprintf (end_header, sizeof end_header,
75             "\r\n"
76             "--%s--\r\n", boundary);
77   reserved_space = (strlen (top_header) + strlen (sig_header) 
78                     + strlen (end_header)+ 100);
79
80   hr = input->Stat (&statinfo, STATFLAG_NONAME);
81   if (hr)
82     {
83       log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
84       return NULL;
85     }
86       
87   body = (char*)xmalloc (reserved_space
88                          + (size_t)statinfo.cbSize.QuadPart + 2);
89   hr = input->Read (body+reserved_space,
90                     (size_t)statinfo.cbSize.QuadPart, &nread);
91   if (hr)
92     {
93       log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
94       xfree (body);
95       return NULL;
96     }
97   body[reserved_space + nread] = 0;
98   body[reserved_space + nread+1] = 0;  /* Just in case this is
99                                           accidently an wchar_t. */
100   if (nread != statinfo.cbSize.QuadPart)
101     {
102       log_debug ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
103       xfree (body);
104       return NULL;
105     }
106
107   /* We do in place conversion. */
108   state = 0;
109   dest = NULL;
110   for (p=body+reserved_space; p && *p; p = (p=strchr (p+1, '\n'))? (p+1):NULL)
111     {
112       if (!state && !strncmp (p, "-----BEGIN PGP SIGNED MESSAGE-----", 34)
113           && trailing_ws_p (p+34) )
114         {
115           dest = stpcpy (body, top_header);
116           state = 1;
117         }
118       else if (state == 1)
119         {
120           /* Wait for an empty line.  */
121           if (trailing_ws_p (p))
122             state = 2;
123         }
124       else if (state == 2 && strncmp (p, "-----", 5))
125         {
126           /* Copy signed data. */
127           p0 = p;
128           if (*p == '-' && p[1] == ' ')
129             p +=2;  /* Remove escaping.  */
130           mark = NULL;
131           while (*p && *p != '\n')
132             {
133               if (*p == ' ' || *p == '\t' || *p == '\r')
134                 mark = p;
135               else
136                 mark = NULL;
137               *dest++ = *p++;
138             }
139           if (mark)
140             *mark =0; /* Remove trailing white space.  */
141           if (*p == '\n')
142             {
143               if (p[-1] == '\r')
144                 *dest++ = '\r';
145               *dest++ = '\n';
146             }
147           if (p > p0)
148             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
149         }
150       else if (state == 2)
151         {
152           /* Armor line encountered.  */
153           p0 = p;
154           if (strncmp (p, "-----BEGIN PGP SIGNATURE-----", 29)
155               || !trailing_ws_p (p+29) )
156             fprintf (stderr,"Invalid clear signed message\n");
157           state = 3;
158           dest = stpcpy (dest, sig_header);
159         
160           while (*p && *p != '\n')
161             *dest++ = *p++;
162           if (*p == '\n')
163             {
164               if (p[-1] == '\r')
165                 *dest++ = '\r';
166               *dest++ = '\n';
167             }
168           if (p > p0)
169             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
170         }
171       else if (state == 3 && strncmp (p, "-----", 5))
172         {
173           /* Copy signature.  */
174           p0 = p;
175           while (*p && *p != '\n')
176             *dest++ = *p++;
177           if (*p == '\n')
178             {
179               if (p[-1] == '\r')
180                 *dest++ = '\r';
181               *dest++ = '\n';
182             }
183           if (p > p0)
184             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
185         }
186       else if (state == 3)
187         {
188           /* Armor line encountered.  */
189           p0 = p;
190           if (strncmp (p, "-----END PGP SIGNATURE-----", 27)
191               || !trailing_ws_p (p+27) )
192             fprintf (stderr,"Invalid clear signed message (no end)\n");
193           while (*p && *p != '\n')
194             *dest++ = *p++;
195           if (*p == '\n')
196             {
197               if (p[-1] == '\r')
198                 *dest++ = '\r';
199               *dest++ = '\n';
200             }
201           dest = stpcpy (dest, end_header);
202           if (p > p0)
203             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
204           break; /* Ah well, we can stop here.  */
205         }
206     }
207   if (!dest)
208     {
209       xfree (body);
210       return NULL;
211     }
212   *dest = 0;
213   *outputlen = strlen (body);
214
215   return body;
216 }
217
218
219
220 /* Verify MESSAGE and update the attachments as required.  MSGTYPE
221    should be the type of the message so that the fucntion can decide
222    what to do.  With FORCE set the verification is done regardlessless
223    of a cached signature result. */
224 int
225 message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
226 {
227   mapi_attach_item_t *table = NULL;
228   int moss_idx = -1;
229   int i;
230   char *inbuf;
231   size_t inbuflen;
232   protocol_t protocol = PROTOCOL_UNKNOWN;
233   int err;
234
235   switch (msgtype)
236     {
237     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
238       break;
239     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
240       log_debug ("Opaque signed message are not yet supported!");
241       return 0;
242     case MSGTYPE_GPGOL_CLEAR_SIGNED:
243       break;
244     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
245     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
246     case MSGTYPE_GPGOL_PGP_MESSAGE:
247       return -1; /* Should not be called for such a message.  */
248     case MSGTYPE_UNKNOWN:
249     case MSGTYPE_GPGOL:
250       return 0; /* Nothing to do.  */
251     }
252   
253   /* If a verification is forced, we set the cached signature status
254      first to "?" to mark that no verification has yet happened. */
255   if (force)
256     mapi_set_sig_status (message, "?");
257   else if (mapi_test_sig_status (message))
258     return 0; /* Already checked that message.  */
259
260   if (msgtype == MSGTYPE_GPGOL_CLEAR_SIGNED)
261     {
262       /* PGP's clear signed messages are special: All is contained in
263          the body and thus there is no requirement for an
264          attachment.  */
265       LPSTREAM rawstream;
266       
267       rawstream = mapi_get_body_as_stream (message);
268       if (!rawstream)
269         return -1;
270       
271       inbuf = pgp_mime_from_clearsigned (rawstream, &inbuflen);
272       rawstream->Release ();
273       if (!inbuf)
274         return -1;
275       protocol = PROTOCOL_OPENPGP;
276     }
277   else
278     {
279       /* PGP/MIME or S/MIME stuff.  */
280       table = mapi_create_attach_table (message, 0);
281       if (!table)
282         return -1; /* No attachment - this should not happen.  */
283
284       for (i=0; !table[i].end_of_table; i++)
285         if (table[i].attach_type == ATTACHTYPE_MOSS)
286           {
287             moss_idx = i;
288             break;
289           }
290       if (moss_idx == -1 && !table[0].end_of_table && table[1].end_of_table)
291         {
292           /* No MOSS flag found in the table but there is only one
293              attachment.  Due to the message type we know that this is
294              the original MOSS message.  We mark this attachment as
295              hidden, so that it won't get displayed.  We further mark
296              it as our original MOSS attachment so that after parsing
297              we have a mean to find it again (see above).  */ 
298           moss_idx = 0;
299           mapi_mark_moss_attach (message, table+0);
300         }
301       
302       if (moss_idx == -1)
303         {
304           mapi_release_attach_table (table);
305           return -1; /* No original attachment - this should not happen.  */
306         }
307
308       inbuf = mapi_get_attach (message, table+0, &inbuflen);
309       if (!inbuf)
310         {
311           mapi_release_attach_table (table);
312           return -1; /* Problem getting the attachment.  */
313         }
314     }
315
316   err = mime_verify (protocol, inbuf, inbuflen, message, 0, 0);
317   log_debug ("mime_verify returned %d", err);
318   if (err)
319     {
320       char buf[200];
321       
322       snprintf (buf, sizeof buf, "Verify failed (%s)", gpg_strerror (err));
323       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
324     }
325   xfree (inbuf);
326                     
327   if (err)
328     {
329       char buf[200];
330       snprintf (buf, sizeof buf, "- %s", gpg_strerror (err));
331       mapi_set_sig_status (message, gpg_strerror (err));
332     }
333   else
334     mapi_set_sig_status (message, "! Good signature");
335
336   mapi_release_attach_table (table);
337   return 0;
338 }
339
340 /* Decrypt MESSAGE, check signature and update the attachments as
341    required.  MSGTYPE should be the type of the message so that the
342    function can decide what to do.  With FORCE set the verification is
343    done regardlessless of a cached signature result - hmmm, should we
344    such a thing for an encrypted message? */
345 int
346 message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
347 {
348   mapi_attach_item_t *table = NULL;
349   int part2_idx;
350   int tblidx;
351   int retval = -1;
352   LPSTREAM cipherstream;
353   gpg_error_t err;
354   int is_opaque = 0;
355   protocol_t protocol;
356
357   switch (msgtype)
358     {
359     case MSGTYPE_UNKNOWN:
360     case MSGTYPE_GPGOL:
361     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
362     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
363     case MSGTYPE_GPGOL_CLEAR_SIGNED:
364       return -1; /* Should not have been called for this.  */
365     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
366       break;
367     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
368       is_opaque = 1;
369       break;
370     case MSGTYPE_GPGOL_PGP_MESSAGE:
371       break;
372     }
373   
374   if (msgtype == MSGTYPE_GPGOL_PGP_MESSAGE)
375     {
376       /* PGP messages are special:  All is contained in the body and thus
377          there is no requirement for an attachment.  */
378       cipherstream = mapi_get_body_as_stream (message);
379       if (!cipherstream)
380         goto leave;
381       protocol = PROTOCOL_OPENPGP;
382     }
383   else
384     {
385   
386       /* PGP/MIME or S/MIME stuff.  */
387       table = mapi_create_attach_table (message, 0);
388       if (!table)
389         goto leave; /* No attachment - this should not happen.  */
390
391       if (is_opaque)
392         {
393           /* S/MIME opaque encrypted message: We expect one
394              attachment.  As we don't know ether we are called the
395              first time, we first try to find this attachment by
396              looking at all attachments.  Only if this fails we
397              identify it by its order.  */
398           part2_idx = -1;
399           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
400             if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
401               {
402                 if (part2_idx == -1 && table[tblidx].content_type 
403                     && (!strcmp (table[tblidx].content_type,
404                                  "application/pkcs7-mime")
405                         || !strcmp (table[tblidx].content_type,
406                                     "application/x-pkcs7-mime")))
407                   part2_idx = tblidx;
408               }
409           if (part2_idx == -1 && tblidx >= 1)
410             {
411               /* We have attachments but none are marked.  Thus we
412                  assume that this is the first time we see this
413                  message and we will set the mark now if we see
414                  appropriate content types. */
415               if (table[0].content_type               
416                   && (!strcmp (table[0].content_type, "application/pkcs7-mime")
417                       || !strcmp (table[0].content_type,
418                                   "application/x-pkcs7-mime")))
419                 part2_idx = 0;
420               if (part2_idx != -1)
421                 mapi_mark_moss_attach (message, table+part2_idx);
422             }
423           if (part2_idx == -1)
424             {
425               log_debug ("%s:%s: this is not an S/MIME encrypted message",
426                          SRCNAME, __func__);
427               goto leave;
428             }
429           protocol = PROTOCOL_SMIME;
430         }
431       else 
432         {
433           /* Multipart/encrypted message: We expect 2 attachments.
434              The first one with the version number and the second one
435              with the ciphertext.  As we don't know ether we are
436              called the first time, we first try to find these
437              attachments by looking at all attachments.  Only if this
438              fails we identify them by their order (i.e. the first 2
439              attachments) and mark them as part1 and part2.  */
440           int part1_idx;
441           
442           part1_idx = part2_idx = -1;
443           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
444             if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
445               {
446                 if (part1_idx == -1 && table[tblidx].content_type 
447                     && !strcmp (table[tblidx].content_type,
448                                 "application/pgp-encrypted"))
449                   part1_idx = tblidx;
450                 else if (part2_idx == -1 && table[tblidx].content_type 
451                          && !strcmp (table[tblidx].content_type,
452                                      "application/octet-stream"))
453                   part2_idx = tblidx;
454               }
455           if (part1_idx == -1 && part2_idx == -1 && tblidx >= 2)
456             {
457               /* At least 2 attachments but none are marked.  Thus we
458                  assume that this is the first time we see this
459                  message and we will set the mark now if we see
460                  appropriate content types. */
461               if (table[0].content_type               
462                   && !strcmp (table[0].content_type,
463                               "application/pgp-encrypted"))
464                 part1_idx = 0;
465               if (table[1].content_type             
466                   && !strcmp (table[1].content_type, 
467                               "application/octet-stream"))
468                 part2_idx = 1;
469               if (part1_idx != -1 && part2_idx != -1)
470                 {
471                   mapi_mark_moss_attach (message, table+part1_idx);
472                   mapi_mark_moss_attach (message, table+part2_idx);
473                 }
474             }
475           if (part1_idx == -1 || part2_idx == -1)
476             {
477               log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
478                          SRCNAME, __func__);
479               goto leave;
480             }
481           protocol = PROTOCOL_OPENPGP;
482         }
483       
484       cipherstream = mapi_get_attach_as_stream (message, table+part2_idx);
485       if (!cipherstream)
486         goto leave; /* Problem getting the attachment.  */
487     }
488
489   err = mime_decrypt (protocol, cipherstream, message, 0, 0);
490   log_debug ("mime_decrypt returned %d (%s)", err, gpg_strerror (err));
491   if (err)
492     {
493       char buf[200];
494       
495       snprintf (buf, sizeof buf, "Decryption failed (%s)", gpg_strerror (err));
496       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
497     }
498   cipherstream->Release ();
499   retval = 0;
500
501  leave:
502   mapi_release_attach_table (table);
503   return retval;
504 }
505
506
507 \f
508
509 /* Return an array of strings with the recipients of the message. On
510    success a malloced array is returned containing allocated strings
511    for each recipient.  The end of the array is marked by NULL.
512    Caller is responsible for releasing the array.  On failure NULL is
513    returned.  */
514 static char **
515 get_recipients (LPMESSAGE message)
516 {
517   static SizedSPropTagArray (1L, PropRecipientNum) = {1L, {PR_EMAIL_ADDRESS}};
518   HRESULT hr;
519   LPMAPITABLE lpRecipientTable = NULL;
520   LPSRowSet lpRecipientRows = NULL;
521   unsigned int rowidx;
522   LPSPropValue row;
523   char **rset;
524   int rsetidx;
525
526
527   if (!message)
528     return NULL;
529
530   hr = message->GetRecipientTable (0, &lpRecipientTable);
531   if (FAILED (hr)) 
532     {
533       log_debug_w32 (-1, "%s:%s: GetRecipientTable failed", SRCNAME, __func__);
534       return NULL;
535     }
536
537   hr = HrQueryAllRows (lpRecipientTable, (LPSPropTagArray) &PropRecipientNum,
538                        NULL, NULL, 0L, &lpRecipientRows);
539   if (FAILED (hr)) 
540     {
541       log_debug_w32 (-1, "%s:%s: HrQueryAllRows failed", SRCNAME, __func__);
542       if (lpRecipientTable)
543         lpRecipientTable->Release();
544       return NULL;
545     }
546
547   rset = (char**)xcalloc (lpRecipientRows->cRows+1, sizeof *rset);
548
549   for (rowidx=0, rsetidx=0; rowidx < lpRecipientRows->cRows; rowidx++)
550     {
551       if (!lpRecipientRows->aRow[rowidx].cValues)
552         continue;
553       row = lpRecipientRows->aRow[rowidx].lpProps;
554
555       switch (PROP_TYPE (row->ulPropTag))
556         {
557         case PT_UNICODE:
558           if ((rset[rsetidx] = wchar_to_utf8 (row->Value.lpszW)))
559             rsetidx++;
560           else
561             log_debug ("%s:%s: error converting recipient to utf8\n",
562                        SRCNAME, __func__);
563           break;
564       
565         case PT_STRING8: /* Assume ASCII. */
566           rset[rsetidx++] = xstrdup (row->Value.lpszA);
567           break;
568           
569         default:
570           log_debug ("%s:%s: proptag=0x%08lx not supported\n",
571                      SRCNAME, __func__, row->ulPropTag);
572           break;
573         }
574     }
575
576   if (lpRecipientTable)
577     lpRecipientTable->Release();
578   if (lpRecipientRows)
579     FreeProws(lpRecipientRows); 
580   
581   log_debug ("%s:%s: got %d recipients:\n", SRCNAME, __func__, rsetidx);
582   for (rsetidx=0; rset[rsetidx]; rsetidx++)
583     log_debug ("%s:%s: \t`%s'\n", SRCNAME, __func__, rset[rsetidx]);
584
585   return rset;
586 }
587
588
589 static void
590 release_recipient_array (char **recipients)
591 {
592   int idx;
593
594   if (recipients)
595     {
596       for (idx=0; recipients[idx]; idx++)
597         xfree (recipients[idx]);
598       xfree (recipients);
599     }
600 }
601
602
603
604 static int
605 sign_encrypt (LPMESSAGE message, HWND hwnd, int signflag)
606 {
607   gpg_error_t err;
608   char **recipients;
609
610   recipients = get_recipients (message);
611   if (!recipients || !recipients[0])
612     {
613       MessageBox (hwnd, _("No recipients to encrypt to are given"),
614                   "GpgOL", MB_ICONERROR|MB_OK);
615
616       err = gpg_error (GPG_ERR_GENERAL);
617     }
618   else
619     {
620       if (signflag)
621         err = mime_sign_encrypt (message, PROTOCOL_OPENPGP, recipients);
622       else
623         err = mime_encrypt (message, PROTOCOL_OPENPGP, recipients);
624       if (err)
625         {
626           char buf[200];
627           
628           snprintf (buf, sizeof buf,
629                     _("Encryption failed (%s)"), gpg_strerror (err));
630           MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
631         }
632     }
633   release_recipient_array (recipients);
634   return err;
635 }
636
637
638 /* Sign the MESSAGE.  */
639 int 
640 message_sign (LPMESSAGE message, HWND hwnd)
641 {
642   gpg_error_t err;
643
644   err = mime_sign (message, PROTOCOL_OPENPGP);
645   if (err)
646     {
647       char buf[200];
648       
649       snprintf (buf, sizeof buf,
650                 _("Signing failed (%s)"), gpg_strerror (err));
651       MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
652     }
653   return err;
654 }
655
656
657
658 /* Encrypt the MESSAGE.  */
659 int 
660 message_encrypt (LPMESSAGE message, HWND hwnd)
661 {
662   return sign_encrypt (message, hwnd, 0);
663 }
664
665
666 /* Sign+Encrypt the MESSAGE.  */
667 int 
668 message_sign_encrypt (LPMESSAGE message, HWND hwnd)
669 {
670   return sign_encrypt (message, hwnd, 1);
671 }
672
673
674