A lot of cleanups. removed unused all gpgme stuff.
[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_has_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     mapi_set_sig_status (message, gpg_strerror (err));
329   else
330     mapi_set_sig_status (message, "Signature was good");
331
332   mapi_release_attach_table (table);
333   return 0;
334 }
335
336 /* Decrypt MESSAGE, check signature and update the attachments as
337    required.  MSGTYPE should be the type of the message so that the
338    function can decide what to do.  With FORCE set the verification is
339    done regardlessless of a cached signature result - hmmm, should we
340    such a thing for an encrypted message? */
341 int
342 message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
343 {
344   mapi_attach_item_t *table = NULL;
345   int part2_idx;
346   int tblidx;
347   int retval = -1;
348   LPSTREAM cipherstream;
349   gpg_error_t err;
350   int is_opaque = 0;
351   protocol_t protocol;
352
353   switch (msgtype)
354     {
355     case MSGTYPE_UNKNOWN:
356     case MSGTYPE_GPGOL:
357     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
358     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
359     case MSGTYPE_GPGOL_CLEAR_SIGNED:
360       return -1; /* Should not have been called for this.  */
361     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
362       break;
363     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
364       is_opaque = 1;
365       break;
366     case MSGTYPE_GPGOL_PGP_MESSAGE:
367       break;
368     }
369   
370   if (msgtype == MSGTYPE_GPGOL_PGP_MESSAGE)
371     {
372       /* PGP messages are special:  All is contained in the body and thus
373          there is no requirement for an attachment.  */
374       cipherstream = mapi_get_body_as_stream (message);
375       if (!cipherstream)
376         goto leave;
377       protocol = PROTOCOL_OPENPGP;
378     }
379   else
380     {
381   
382       /* PGP/MIME or S/MIME stuff.  */
383       table = mapi_create_attach_table (message, 0);
384       if (!table)
385         goto leave; /* No attachment - this should not happen.  */
386
387       if (is_opaque)
388         {
389           /* S/MIME opaque encrypted message: We expect one
390              attachment.  As we don't know ether we are called the
391              first time, we first try to find this attachment by
392              looking at all attachments.  Only if this fails we
393              identify it by its order.  */
394           part2_idx = -1;
395           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
396             if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
397               {
398                 if (part2_idx == -1 && table[tblidx].content_type 
399                     && (!strcmp (table[tblidx].content_type,
400                                  "application/pkcs7-mime")
401                         || !strcmp (table[tblidx].content_type,
402                                     "application/x-pkcs7-mime")))
403                   part2_idx = tblidx;
404               }
405           if (part2_idx == -1 && tblidx >= 1)
406             {
407               /* We have attachments but none are marked.  Thus we
408                  assume that this is the first time we see this
409                  message and we will set the mark now if we see
410                  appropriate content types. */
411               if (table[0].content_type               
412                   && (!strcmp (table[0].content_type, "application/pkcs7-mime")
413                       || !strcmp (table[0].content_type,
414                                   "application/x-pkcs7-mime")))
415                 part2_idx = 0;
416               if (part2_idx != -1)
417                 mapi_mark_moss_attach (message, table+part2_idx);
418             }
419           if (part2_idx == -1)
420             {
421               log_debug ("%s:%s: this is not an S/MIME encrypted message",
422                          SRCNAME, __func__);
423               goto leave;
424             }
425           protocol = PROTOCOL_SMIME;
426         }
427       else 
428         {
429           /* Multipart/encrypted message: We expect 2 attachments.
430              The first one with the version number and the second one
431              with the ciphertext.  As we don't know ether we are
432              called the first time, we first try to find these
433              attachments by looking at all attachments.  Only if this
434              fails we identify them by their order (i.e. the first 2
435              attachments) and mark them as part1 and part2.  */
436           int part1_idx;
437           
438           part1_idx = part2_idx = -1;
439           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
440             if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
441               {
442                 if (part1_idx == -1 && table[tblidx].content_type 
443                     && !strcmp (table[tblidx].content_type,
444                                 "application/pgp-encrypted"))
445                   part1_idx = tblidx;
446                 else if (part2_idx == -1 && table[tblidx].content_type 
447                          && !strcmp (table[tblidx].content_type,
448                                      "application/octet-stream"))
449                   part2_idx = tblidx;
450               }
451           if (part1_idx == -1 && part2_idx == -1 && tblidx >= 2)
452             {
453               /* At least 2 attachments but none are marked.  Thus we
454                  assume that this is the first time we see this
455                  message and we will set the mark now if we see
456                  appropriate content types. */
457               if (table[0].content_type               
458                   && !strcmp (table[0].content_type,
459                               "application/pgp-encrypted"))
460                 part1_idx = 0;
461               if (table[1].content_type             
462                   && !strcmp (table[1].content_type, 
463                               "application/octet-stream"))
464                 part2_idx = 1;
465               if (part1_idx != -1 && part2_idx != -1)
466                 {
467                   mapi_mark_moss_attach (message, table+part1_idx);
468                   mapi_mark_moss_attach (message, table+part2_idx);
469                 }
470             }
471           if (part1_idx == -1 || part2_idx == -1)
472             {
473               log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
474                          SRCNAME, __func__);
475               goto leave;
476             }
477           protocol = PROTOCOL_OPENPGP;
478         }
479       
480       cipherstream = mapi_get_attach_as_stream (message, table+part2_idx);
481       if (!cipherstream)
482         goto leave; /* Problem getting the attachment.  */
483     }
484
485   err = mime_decrypt (protocol, cipherstream, message, 0, 0);
486   log_debug ("mime_decrypt returned %d (%s)", err, gpg_strerror (err));
487   if (err)
488     {
489       char buf[200];
490       
491       snprintf (buf, sizeof buf, "Decryption failed (%s)", gpg_strerror (err));
492       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
493     }
494   cipherstream->Release ();
495   retval = 0;
496
497  leave:
498   mapi_release_attach_table (table);
499   return retval;
500 }
501
502
503 \f
504
505 /* Return an array of strings with the recipients of the message. On
506    success a malloced array is returned containing allocated strings
507    for each recipient.  The end of the array is marked by NULL.
508    Caller is responsible for releasing the array.  On failure NULL is
509    returned.  */
510 static char **
511 get_recipients (LPMESSAGE message)
512 {
513   static SizedSPropTagArray (1L, PropRecipientNum) = {1L, {PR_EMAIL_ADDRESS}};
514   HRESULT hr;
515   LPMAPITABLE lpRecipientTable = NULL;
516   LPSRowSet lpRecipientRows = NULL;
517   unsigned int rowidx;
518   LPSPropValue row;
519   char **rset;
520   int rsetidx;
521
522
523   if (!message)
524     return NULL;
525
526   hr = message->GetRecipientTable (0, &lpRecipientTable);
527   if (FAILED (hr)) 
528     {
529       log_debug_w32 (-1, "%s:%s: GetRecipientTable failed", SRCNAME, __func__);
530       return NULL;
531     }
532
533   hr = HrQueryAllRows (lpRecipientTable, (LPSPropTagArray) &PropRecipientNum,
534                        NULL, NULL, 0L, &lpRecipientRows);
535   if (FAILED (hr)) 
536     {
537       log_debug_w32 (-1, "%s:%s: HrQueryAllRows failed", SRCNAME, __func__);
538       if (lpRecipientTable)
539         lpRecipientTable->Release();
540       return NULL;
541     }
542
543   rset = (char**)xcalloc (lpRecipientRows->cRows+1, sizeof *rset);
544
545   for (rowidx=0, rsetidx=0; rowidx < lpRecipientRows->cRows; rowidx++)
546     {
547       if (!lpRecipientRows->aRow[rowidx].cValues)
548         continue;
549       row = lpRecipientRows->aRow[rowidx].lpProps;
550
551       switch (PROP_TYPE (row->ulPropTag))
552         {
553         case PT_UNICODE:
554           if ((rset[rsetidx] = wchar_to_utf8 (row->Value.lpszW)))
555             rsetidx++;
556           else
557             log_debug ("%s:%s: error converting recipient to utf8\n",
558                        SRCNAME, __func__);
559           break;
560       
561         case PT_STRING8: /* Assume ASCII. */
562           rset[rsetidx++] = xstrdup (row->Value.lpszA);
563           break;
564           
565         default:
566           log_debug ("%s:%s: proptag=0x%08lx not supported\n",
567                      SRCNAME, __func__, row->ulPropTag);
568           break;
569         }
570     }
571
572   if (lpRecipientTable)
573     lpRecipientTable->Release();
574   if (lpRecipientRows)
575     FreeProws(lpRecipientRows); 
576   
577   log_debug ("%s:%s: got %d recipients:\n", SRCNAME, __func__, rsetidx);
578   for (rsetidx=0; rset[rsetidx]; rsetidx++)
579     log_debug ("%s:%s: \t`%s'\n", SRCNAME, __func__, rset[rsetidx]);
580
581   return rset;
582 }
583
584
585 static void
586 release_recipient_array (char **recipients)
587 {
588   int idx;
589
590   if (recipients)
591     {
592       for (idx=0; recipients[idx]; idx++)
593         xfree (recipients[idx]);
594       xfree (recipients);
595     }
596 }
597
598
599
600 static int
601 sign_encrypt (LPMESSAGE message, HWND hwnd, int signflag)
602 {
603   gpg_error_t err;
604   char **recipients;
605
606   recipients = get_recipients (message);
607   if (!recipients || !recipients[0])
608     {
609       MessageBox (hwnd, _("No recipients to encrypt to are given"),
610                   "GpgOL", MB_ICONERROR|MB_OK);
611
612       err = gpg_error (GPG_ERR_GENERAL);
613     }
614   else
615     {
616       if (signflag)
617         err = mime_sign_encrypt (message, PROTOCOL_OPENPGP, recipients);
618       else
619         err = mime_encrypt (message, PROTOCOL_OPENPGP, recipients);
620       if (err)
621         {
622           char buf[200];
623           
624           snprintf (buf, sizeof buf,
625                     _("Encryption failed (%s)"), gpg_strerror (err));
626           MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
627         }
628     }
629   release_recipient_array (recipients);
630   return err;
631 }
632
633
634 /* Sign the MESSAGE.  */
635 int 
636 message_sign (LPMESSAGE message, HWND hwnd)
637 {
638   gpg_error_t err;
639
640   err = mime_sign (message, PROTOCOL_OPENPGP);
641   if (err)
642     {
643       char buf[200];
644       
645       snprintf (buf, sizeof buf,
646                 _("Signing failed (%s)"), gpg_strerror (err));
647       MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
648     }
649   return err;
650 }
651
652
653
654 /* Encrypt the MESSAGE.  */
655 int 
656 message_encrypt (LPMESSAGE message, HWND hwnd)
657 {
658   return sign_encrypt (message, hwnd, 0);
659 }
660
661
662 /* Sign+Encrypt the MESSAGE.  */
663 int 
664 message_sign_encrypt (LPMESSAGE message, HWND hwnd)
665 {
666   return sign_encrypt (message, hwnd, 1);
667 }
668
669
670