The new framework for creating messages is now in place.
[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 "message.h"
34
35 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
36                                      SRCNAME, __func__, __LINE__); \
37                         } while (0)
38
39
40 /* Convert the clear signed message from INPUT into a PS?MIME signed
41    message and return it in a new allocated buffer.  OUTPUTLEN
42    received the valid length of that buffer; the buffer is guarnateed
43    to be Nul terminated.  */
44 static char *
45 pgp_mime_from_clearsigned (LPSTREAM input, size_t *outputlen)
46 {
47   HRESULT hr;
48   STATSTG statinfo;
49   ULONG nread;
50   char *body = NULL;
51   char *p, *p0, *dest, *mark;
52   char boundary[BOUNDARYSIZE+1];
53   char top_header[200 + 2*BOUNDARYSIZE]; 
54   char sig_header[100 + BOUNDARYSIZE]; 
55   char end_header[10 + BOUNDARYSIZE];
56   size_t reserved_space;
57   int state;
58
59   *outputlen = 0;
60
61   /* Note that our parser does not make use of the micalg parameter.  */
62   generate_boundary (boundary);
63   snprintf (top_header, sizeof top_header,
64             "MIME-Version: 1.0\r\n"
65             "Content-Type: multipart/signed; boundary=\"%s\";\r\n"
66             "              protocol=\"application/pgp-signature\"\r\n"
67             "\r\n"
68             "--%s\r\n", boundary, boundary);
69   snprintf (sig_header, sizeof sig_header,
70             "--%s\r\n"
71             "Content-Type: application/pgp-signature\r\n"
72             "\r\n", boundary);
73   snprintf (end_header, sizeof end_header,
74             "\r\n"
75             "--%s--\r\n", boundary);
76   reserved_space = (strlen (top_header) + strlen (sig_header) 
77                     + strlen (end_header)+ 100);
78
79   hr = input->Stat (&statinfo, STATFLAG_NONAME);
80   if (hr)
81     {
82       log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
83       return NULL;
84     }
85       
86   body = (char*)xmalloc (reserved_space
87                          + (size_t)statinfo.cbSize.QuadPart + 2);
88   hr = input->Read (body+reserved_space,
89                     (size_t)statinfo.cbSize.QuadPart, &nread);
90   if (hr)
91     {
92       log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
93       xfree (body);
94       return NULL;
95     }
96   body[reserved_space + nread] = 0;
97   body[reserved_space + nread+1] = 0;  /* Just in case this is
98                                           accidently an wchar_t. */
99   if (nread != statinfo.cbSize.QuadPart)
100     {
101       log_debug ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
102       xfree (body);
103       return NULL;
104     }
105
106   /* We do in place conversion. */
107   state = 0;
108   dest = NULL;
109   for (p=body+reserved_space; p && *p; p = (p=strchr (p+1, '\n'))? (p+1):NULL)
110     {
111       if (!state && !strncmp (p, "-----BEGIN PGP SIGNED MESSAGE-----", 34)
112           && trailing_ws_p (p+34) )
113         {
114           dest = stpcpy (body, top_header);
115           state = 1;
116         }
117       else if (state == 1)
118         {
119           /* Wait for an empty line.  */
120           if (trailing_ws_p (p))
121             state = 2;
122         }
123       else if (state == 2 && strncmp (p, "-----", 5))
124         {
125           /* Copy signed data. */
126           p0 = p;
127           if (*p == '-' && p[1] == ' ')
128             p +=2;  /* Remove escaping.  */
129           mark = NULL;
130           while (*p && *p != '\n')
131             {
132               if (*p == ' ' || *p == '\t' || *p == '\r')
133                 mark = p;
134               else
135                 mark = NULL;
136               *dest++ = *p++;
137             }
138           if (mark)
139             *mark =0; /* Remove trailing white space.  */
140           if (*p == '\n')
141             {
142               if (p[-1] == '\r')
143                 *dest++ = '\r';
144               *dest++ = '\n';
145             }
146           if (p > p0)
147             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
148         }
149       else if (state == 2)
150         {
151           /* Armor line encountered.  */
152           p0 = p;
153           if (strncmp (p, "-----BEGIN PGP SIGNATURE-----", 29)
154               || !trailing_ws_p (p+29) )
155             fprintf (stderr,"Invalid clear signed message\n");
156           state = 3;
157           dest = stpcpy (dest, sig_header);
158         
159           while (*p && *p != '\n')
160             *dest++ = *p++;
161           if (*p == '\n')
162             {
163               if (p[-1] == '\r')
164                 *dest++ = '\r';
165               *dest++ = '\n';
166             }
167           if (p > p0)
168             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
169         }
170       else if (state == 3 && strncmp (p, "-----", 5))
171         {
172           /* Copy signature.  */
173           p0 = p;
174           while (*p && *p != '\n')
175             *dest++ = *p++;
176           if (*p == '\n')
177             {
178               if (p[-1] == '\r')
179                 *dest++ = '\r';
180               *dest++ = '\n';
181             }
182           if (p > p0)
183             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
184         }
185       else if (state == 3)
186         {
187           /* Armor line encountered.  */
188           p0 = p;
189           if (strncmp (p, "-----END PGP SIGNATURE-----", 27)
190               || !trailing_ws_p (p+27) )
191             fprintf (stderr,"Invalid clear signed message (no end)\n");
192           while (*p && *p != '\n')
193             *dest++ = *p++;
194           if (*p == '\n')
195             {
196               if (p[-1] == '\r')
197                 *dest++ = '\r';
198               *dest++ = '\n';
199             }
200           dest = stpcpy (dest, end_header);
201           if (p > p0)
202             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
203           break; /* Ah well, we can stop here.  */
204         }
205     }
206   if (!dest)
207     {
208       xfree (body);
209       return NULL;
210     }
211   *dest = 0;
212   *outputlen = strlen (body);
213
214   return body;
215 }
216
217
218
219 /* Verify MESSAGE and update the attachments as required.  MSGTYPE
220    should be the type of the message so that the fucntion can decide
221    what to do.  With FORCE set the verification is done regardlessless
222    of a cached signature result. */
223 int
224 message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
225 {
226   mapi_attach_item_t *table = NULL;
227   int moss_idx = -1;
228   int i;
229   char *inbuf;
230   size_t inbuflen;
231   int err;
232
233   switch (msgtype)
234     {
235     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
236       break;
237     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
238       log_debug ("Opaque signed message are not yet supported!");
239       return 0;
240     case MSGTYPE_GPGOL_CLEAR_SIGNED:
241       break;
242     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
243     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
244     case MSGTYPE_GPGOL_PGP_MESSAGE:
245       return -1; /* Should not be called for such a message.  */
246     case MSGTYPE_UNKNOWN:
247     case MSGTYPE_GPGOL:
248       return 0; /* Nothing to do.  */
249     }
250   
251   /* If a verification is forced, we set the cached signature status
252      first to "?" to mark that no verification has yet happened. */
253   if (force)
254     mapi_set_sig_status (message, "?");
255   else if (mapi_has_sig_status (message))
256     return 0; /* Already checked that message.  */
257
258   if (msgtype == MSGTYPE_GPGOL_CLEAR_SIGNED)
259     {
260       /* PGP's clear signed messages are special: All is contained in
261          the body and thus there is no requirement for an
262          attachment.  */
263       LPSTREAM rawstream;
264       
265       rawstream = mapi_get_body_as_stream (message);
266       if (!rawstream)
267         return -1;
268       
269       inbuf = pgp_mime_from_clearsigned (rawstream, &inbuflen);
270       rawstream->Release ();
271       if (!inbuf)
272         return -1;
273     }
274   else
275     {
276       /* PGP/MIME or S/MIME stuff.  */
277       table = mapi_create_attach_table (message, 0);
278       if (!table)
279         return -1; /* No attachment - this should not happen.  */
280
281       for (i=0; !table[i].end_of_table; i++)
282         if (table[i].attach_type == ATTACHTYPE_MOSS)
283           {
284             moss_idx = i;
285             break;
286           }
287       if (moss_idx == -1 && !table[0].end_of_table && table[1].end_of_table)
288         {
289           /* No MOSS flag found in the table but there is only one
290              attachment.  Due to the message type we know that this is
291              the original MOSS message.  We mark this attachment as
292              hidden, so that it won't get displayed.  We further mark
293              it as our original MOSS attachment so that after parsing
294              we have a mean to find it again (see above).  */ 
295           moss_idx = 0;
296           mapi_mark_moss_attach (message, table+0);
297         }
298       
299       if (moss_idx == -1)
300         {
301           mapi_release_attach_table (table);
302           return -1; /* No original attachment - this should not happen.  */
303         }
304
305       inbuf = mapi_get_attach (message, table+0, &inbuflen);
306       if (!inbuf)
307         {
308           mapi_release_attach_table (table);
309           return -1; /* Problem getting the attachment.  */
310         }
311     }
312
313   err = mime_verify (inbuf, inbuflen, message, 0, 
314                      opt.passwd_ttl, NULL, NULL, 0);
315   log_debug ("mime_verify returned %d", err);
316   if (err)
317     {
318       char buf[200];
319       
320       snprintf (buf, sizeof buf, "Verify failed (%s)", gpg_strerror (err));
321       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
322     }
323   xfree (inbuf);
324                     
325   if (err)
326     mapi_set_sig_status (message, gpg_strerror (err));
327   else
328     mapi_set_sig_status (message, "Signature was good");
329
330   mapi_release_attach_table (table);
331   return 0;
332 }
333
334 /* Decrypt MESSAGE, check signature and update the attachments as
335    required.  MSGTYPE should be the type of the message so that the
336    function can decide what to do.  With FORCE set the verification is
337    done regardlessless of a cached signature result - hmmm, should we
338    such a thing for an encrypted message? */
339 int
340 message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
341 {
342   mapi_attach_item_t *table = NULL;
343   int part2_idx;
344   int tblidx;
345   int retval = -1;
346   LPSTREAM cipherstream;
347   gpg_error_t err;
348   int is_opaque = 0;
349   int is_smime = 0;
350
351   switch (msgtype)
352     {
353     case MSGTYPE_UNKNOWN:
354     case MSGTYPE_GPGOL:
355     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
356     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
357     case MSGTYPE_GPGOL_CLEAR_SIGNED:
358       return -1; /* Should not have been called for this.  */
359     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
360       break;
361     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
362       is_opaque = 1;
363       break;
364     case MSGTYPE_GPGOL_PGP_MESSAGE:
365       break;
366     }
367   
368   if (msgtype == MSGTYPE_GPGOL_PGP_MESSAGE)
369     {
370       /* PGP messages are special:  All is contained in the body and thus
371          there is no requirement for an attachment.  */
372       cipherstream = mapi_get_body_as_stream (message);
373       if (!cipherstream)
374         goto leave;
375     }
376   else
377     {
378   
379       /* PGP/MIME or S/MIME stuff.  */
380       table = mapi_create_attach_table (message, 0);
381       if (!table)
382         goto leave; /* No attachment - this should not happen.  */
383
384       if (is_opaque)
385         {
386           /* S/MIME opaque encrypted message: We expect one
387              attachment.  As we don't know ether we are called the
388              first time, we first try to find this attachment by
389              looking at all attachments.  Only if this fails we
390              identify it by its order.  */
391           part2_idx = -1;
392           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
393             if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
394               {
395                 if (part2_idx == -1 && table[tblidx].content_type 
396                     && (!strcmp (table[tblidx].content_type,
397                                  "application/pkcs7-mime")
398                         || !strcmp (table[tblidx].content_type,
399                                     "application/x-pkcs7-mime")))
400                   part2_idx = tblidx;
401               }
402           if (part2_idx == -1 && tblidx >= 1)
403             {
404               /* We have attachments but none are marked.  Thus we
405                  assume that this is the first time we see this
406                  message and we will set the mark now if we see
407                  appropriate content types. */
408               if (table[0].content_type               
409                   && (!strcmp (table[0].content_type, "application/pkcs7-mime")
410                       || !strcmp (table[0].content_type,
411                                   "application/x-pkcs7-mime")))
412                 part2_idx = 0;
413               if (part2_idx != -1)
414                 mapi_mark_moss_attach (message, table+part2_idx);
415             }
416           if (part2_idx == -1)
417             {
418               log_debug ("%s:%s: this is not an S/MIME encrypted message",
419                          SRCNAME, __func__);
420               goto leave;
421             }
422           is_smime = 1;
423         }
424       else 
425         {
426           /* Multipart/encrypted message: We expect 2 attachments.
427              The first one with the version number and the second one
428              with the ciphertext.  As we don't know ether we are
429              called the first time, we first try to find these
430              attachments by looking at all attachments.  Only if this
431              fails we identify them by their order (i.e. the first 2
432              attachments) and mark them as part1 and part2.  */
433           int part1_idx;
434           
435           part1_idx = part2_idx = -1;
436           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
437             if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
438               {
439                 if (part1_idx == -1 && table[tblidx].content_type 
440                     && !strcmp (table[tblidx].content_type,
441                                 "application/pgp-encrypted"))
442                   part1_idx = tblidx;
443                 else if (part2_idx == -1 && table[tblidx].content_type 
444                          && !strcmp (table[tblidx].content_type,
445                                      "application/octet-stream"))
446                   part2_idx = tblidx;
447               }
448           if (part1_idx == -1 && part2_idx == -1 && tblidx >= 2)
449             {
450               /* At least 2 attachments but none are marked.  Thus we
451                  assume that this is the first time we see this
452                  message and we will set the mark now if we see
453                  appropriate content types. */
454               if (table[0].content_type               
455                   && !strcmp (table[0].content_type,
456                               "application/pgp-encrypted"))
457                 part1_idx = 0;
458               if (table[1].content_type             
459                   && !strcmp (table[1].content_type, 
460                               "application/octet-stream"))
461                 part2_idx = 1;
462               if (part1_idx != -1 && part2_idx != -1)
463                 {
464                   mapi_mark_moss_attach (message, table+part1_idx);
465                   mapi_mark_moss_attach (message, table+part2_idx);
466                 }
467             }
468           if (part1_idx == -1 || part2_idx == -1)
469             {
470               log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
471                          SRCNAME, __func__);
472               goto leave;
473             }
474         }
475       
476       cipherstream = mapi_get_attach_as_stream (message, table+part2_idx);
477       if (!cipherstream)
478         goto leave; /* Problem getting the attachment.  */
479     }
480
481   err = mime_decrypt (cipherstream, message, is_smime, opt.passwd_ttl,
482                       NULL, NULL, 0);
483   log_debug ("mime_decrypt returned %d (%s)", err, gpg_strerror (err));
484   if (err)
485     {
486       char buf[200];
487       
488       snprintf (buf, sizeof buf, "Decryption failed (%s)", gpg_strerror (err));
489       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
490     }
491   cipherstream->Release ();
492   retval = 0;
493
494  leave:
495   mapi_release_attach_table (table);
496   return retval;
497 }
498
499
500
501 \f
502 #if 0
503 /* Sign the current message. Returns 0 on success. */
504 int
505 message_sign (LPMESSAGE message, HWND hwnd, int want_html)
506 {
507   HRESULT hr;
508   STATSTG statinfo;
509   LPSTREAM plaintext;
510   size_t plaintextlen;
511   char *signedtext = NULL;
512   int err = 0;
513   gpgme_key_t sign_key = NULL;
514   SPropValue prop;
515   int have_html_attach = 0;
516
517   log_debug ("%s:%s: enter message=%p\n", SRCNAME, __func__, message);
518   
519   /* We don't sign an empty body - a signature on a zero length string
520      is pretty much useless.  We assume that a HTML message always
521      comes with a text/plain alternative. */
522   plaintext = mapi_get_body_as_stream (message);
523   if (!plaintext)
524     plaintextlen = 0;
525   else
526     {
527       hr = input->Stat (&statinfo, STATFLAG_NONAME);
528       if (hr)
529         {
530           log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
531           plaintext->Release ();
532           return gpg_error (GPG_ERR_GENERAL);
533         }
534       plaintextlen = (size_t)statinfo.cbSize.QuadPart;
535     }
536
537   if ( !plaintextlen && !has_attachments (message)) 
538     {
539       log_debug ("%s:%s: leave (empty)", SRCNAME, __func__);
540       plaintext->Release ();
541       return 0; 
542     }
543
544   /* Pop up a dialog box to ask for the signer of the message. */
545   if (signer_dialog_box (&sign_key, NULL, 0) == -1)
546     {
547       log_debug ("%s.%s: leave (dialog failed)\n", SRCNAME, __func__);
548       plaintext->Release ();
549       return gpg_error (GPG_ERR_CANCELED);  
550     }
551
552   /* Sign the plaintext */
553   if (plaintextlen)
554     {
555       err = op_sign (plaintext, &signedtext, 
556                      OP_SIG_CLEAR, sign_key, opt.passwd_ttl);
557       if (err)
558         {
559           MessageBox (hwnd, op_strerror (err),
560                       _("Signing Failure"), MB_ICONERROR|MB_OK);
561           plaintext->Release ();
562           return gpg_error (GPG_ERR_GENERAL);
563         }
564     }
565
566   
567   /* If those brain dead html mails are requested we now figure out
568      whether a HTML body is actually available and move it to an
569      attachment so that the code below will sign it as a regular
570      attachments.  */
571   if (want_html)
572     {
573       log_debug ("Signing HTML is not yet supported\n");
574 //       char *htmltext = loadBody (true);
575       
576 //       if (htmltext && *htmltext)
577 //         {
578 //           if (!createHtmlAttachment (htmltext))
579 //             have_html_attach = 1;
580 //         }
581 //       xfree (htmltext);
582
583       /* If we got a new attachment we need to release the loaded
584          attachment info so that the next getAttachment call will read
585          fresh info. */
586 //       if (have_html_attach)
587 //         free_attach_info ();
588     }
589
590
591   /* Note, there is a side-effect when we have HTML mails: The
592      auto-sign-attch option is ignored.  I regard auto-sign-attach as a
593      silly option anyway. */
594   if ((opt.auto_sign_attach || have_html_attach) && has_attachments ())
595     {
596       unsigned int n;
597       
598       n = getAttachments ();
599       log_debug ("%s:%s: message has %u attachments\n", SRCNAME, __func__, n);
600       for (unsigned int i=0; i < n; i++) 
601         signAttachment (hwnd, i, sign_key, opt.passwd_ttl);
602       /* FIXME: we should throw an error if signing of any attachment
603          failed. */
604     }
605
606   set_x_header (message, "GPGOL-VERSION", PACKAGE_VERSION);
607
608   /* Now that we successfully processed the attachments, we can save
609      the changes to the body.  */
610   if (plaintextlen)
611     {
612       err = set_message_body (message, signedtext, 0);
613       if (err)
614         goto leave;
615
616       /* In case we don't have attachments, Outlook will really insert
617          the following content type into the header.  We use this to
618          declare that the encrypted content of the message is utf-8
619          encoded.  If we have atatchments, OUtlook has its own idea of
620          the content type to use.  */
621       prop.ulPropTag=PR_CONTENT_TYPE_A;
622       prop.Value.lpszA="text/plain; charset=utf-8"; 
623       hr = HrSetOneProp (message, &prop);
624       if (hr)
625         log_error ("%s:%s: can't set content type: hr=%#lx\n",
626                    SRCNAME, __func__, hr);
627     }
628   
629   hr = message->SaveChanges (KEEP_OPEN_READWRITE|FORCE_SAVE);
630   if (hr)
631     {
632       log_error ("%s:%s: SaveChanges(message) failed: hr=%#lx\n",
633                  SRCNAME, __func__, hr); 
634       err = gpg_error (GPG_ERR_GENERAL);
635       goto leave;
636     }
637
638  leave:
639   xfree (signedtext);
640   gpgme_key_release (sign_key);
641   xfree (plaintext);
642   log_debug ("%s:%s: leave (err=%s)\n", SRCNAME, __func__, op_strerror (err));
643   return err;
644 }
645
646 #endif