s/should_inline_crypt/do_pgp_inline/
[gpgol.git] / src / mimemaker.cpp
1 /* mimemaker.c - Construct MIME message out of a MAPI
2  * Copyright (C) 2007, 2008 g10 Code GmbH
3  * Copyright (C) 2015 by Bundesamt für Sicherheit in der Informationstechnik
4  * Software engineering by Intevation GmbH
5  *
6  * This file is part of GpgOL.
7  *
8  * GpgOL is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * GpgOL is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <assert.h>
30 #include <string.h>
31 #include <ctype.h>
32
33 #define COBJMACROS
34 #include <windows.h>
35 #include <objidl.h>
36
37 #include "mymapi.h"
38 #include "mymapitags.h"
39
40 #include "common.h"
41 #include "engine.h"
42 #include "mapihelp.h"
43 #include "mimemaker.h"
44 #include "oomhelp.h"
45 #include "gpgolstr.h"
46 #include "mail.h"
47
48 static const unsigned char oid_mimetag[] =
49     {0x2A, 0x86, 0x48, 0x86, 0xf7, 0x14, 0x03, 0x0a, 0x04};
50
51 /* The base-64 list used for base64 encoding. */
52 static unsigned char bintoasc[64+1] = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
53                                        "abcdefghijklmnopqrstuvwxyz"
54                                        "0123456789+/");
55
56
57 /* Object used to collect data in a memory buffer.  */
58 struct databuf_s
59 {
60   size_t len;      /* Used length.  */
61   size_t size;     /* Allocated length of BUF.  */
62   char *buf;       /* Malloced buffer.  */
63 };
64
65
66 /*** local prototypes  ***/
67 static int write_multistring (sink_t sink, const char *text1,
68                               ...) GPGOL_GCC_A_SENTINEL(0);
69
70
71
72
73 \f
74 /* Standard write method used with a sink_t object.  */
75 int
76 sink_std_write (sink_t sink, const void *data, size_t datalen)
77 {
78   HRESULT hr;
79   LPSTREAM stream = static_cast<LPSTREAM>(sink->cb_data);
80
81   if (!stream)
82     {
83       log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
84       return -1;
85     }
86   if (!data)
87     return 0;  /* Flush - nothing to do here.  */
88
89   hr = stream->Write(data, datalen, NULL);
90   if (hr)
91     {
92       log_error ("%s:%s: Write failed: hr=%#lx", SRCNAME, __func__, hr);
93       return -1;
94     }
95   return 0;
96 }
97
98 int
99 sink_string_write (sink_t sink, const void *data, size_t datalen)
100 {
101   Mail *mail = static_cast<Mail *>(sink->cb_data);
102   mail->append_to_inline_body (std::string((char*)data, datalen));
103   return 0;
104 }
105
106 /* Write method used with a sink_t that contains a file object.  */
107 int
108 sink_file_write (sink_t sink, const void *data, size_t datalen)
109 {
110   HANDLE hFile = sink->cb_data;
111   DWORD written = 0;
112
113   if (!hFile || hFile == INVALID_HANDLE_VALUE)
114     {
115       log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
116       return -1;
117     }
118   if (!data)
119     return 0;  /* Flush - nothing to do here.  */
120
121   if (!WriteFile (hFile, data, datalen, &written, NULL))
122     {
123       log_error ("%s:%s: Write failed: ", SRCNAME, __func__);
124       return -1;
125     }
126   return 0;
127 }
128
129
130 /* Make sure that PROTOCOL is usable or return a suitable protocol.
131    On error PROTOCOL_UNKNOWN is returned.  */
132 static protocol_t
133 check_protocol (protocol_t protocol)
134 {
135   switch (protocol)
136     {
137     case PROTOCOL_UNKNOWN:
138       return PROTOCOL_UNKNOWN;
139     case PROTOCOL_OPENPGP:
140     case PROTOCOL_SMIME:
141       return protocol;
142     }
143
144   log_error ("%s:%s: BUG", SRCNAME, __func__);
145   return PROTOCOL_UNKNOWN;
146 }
147
148
149
150 /* Create a new MAPI attchment for MESSAGE which will be used to
151    prepare the MIME message.  On sucess the stream to write the data
152    to is stored at STREAM and the attachment object itself is
153    returned.  The caller needs to call SaveChanges.  Returns NULL on
154    failure in which case STREAM will be set to NULL.  */
155 LPATTACH
156 create_mapi_attachment (LPMESSAGE message, sink_t sink)
157 {
158   HRESULT hr;
159   ULONG pos;
160   SPropValue prop;
161   LPATTACH att = NULL;
162   LPUNKNOWN punk;
163
164   sink->cb_data = NULL;
165   sink->writefnc = NULL;
166   hr = message->CreateAttach(NULL, 0, &pos, &att);
167   if (hr)
168     {
169       log_error ("%s:%s: can't create attachment: hr=%#lx\n",
170                  SRCNAME, __func__, hr);
171       return NULL;
172     }
173
174   prop.ulPropTag = PR_ATTACH_METHOD;
175   prop.Value.ul = ATTACH_BY_VALUE;
176   hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
177   if (hr)
178     {
179       log_error ("%s:%s: can't set attach method: hr=%#lx\n",
180                  SRCNAME, __func__, hr);
181       goto failure;
182     }
183
184   /* Mark that attachment so that we know why it has been created.  */
185   if (get_gpgolattachtype_tag (message, &prop.ulPropTag) )
186     goto failure;
187   prop.Value.l = ATTACHTYPE_MOSSTEMPL;
188   hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
189   if (hr)
190     {
191       log_error ("%s:%s: can't set %s property: hr=%#lx\n",
192                  SRCNAME, __func__, "GpgOL Attach Type", hr);
193       goto failure;
194     }
195
196
197   /* We better insert a short filename. */
198   prop.ulPropTag = PR_ATTACH_FILENAME_A;
199   prop.Value.lpszA = strdup (MIMEATTACHFILENAME);
200   hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
201   xfree (prop.Value.lpszA);
202   if (hr)
203     {
204       log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
205                  SRCNAME, __func__, hr);
206       goto failure;
207     }
208
209
210   /* Even for encrypted messages we need to set the MAPI property to
211      multipart/signed.  This seems to be a part of the trigger which
212      leads OL to process such a message in a special way.  */
213   prop.ulPropTag = PR_ATTACH_TAG;
214   prop.Value.bin.cb  = sizeof oid_mimetag;
215   prop.Value.bin.lpb = (LPBYTE)oid_mimetag;
216   hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
217   if (!hr)
218     {
219       prop.ulPropTag = PR_ATTACH_MIME_TAG_A;
220       prop.Value.lpszA = strdup("multipart/signed");
221       hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
222       xfree (prop.Value.lpszA);
223     }
224   if (hr)
225     {
226       log_error ("%s:%s: can't set attach mime tag: hr=%#lx\n",
227                  SRCNAME, __func__, hr);
228       goto failure;
229     }
230
231   punk = NULL;
232   hr = att->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, 0,
233                          (MAPI_CREATE|MAPI_MODIFY), &punk);
234   if (FAILED (hr))
235     {
236       log_error ("%s:%s: can't create output stream: hr=%#lx\n",
237                  SRCNAME, __func__, hr);
238       goto failure;
239     }
240   sink->cb_data = (LPSTREAM)punk;
241   sink->writefnc = sink_std_write;
242   return att;
243
244  failure:
245   gpgol_release (att);
246   return NULL;
247 }
248
249
250 /* Write data to a sink_t.  */
251 int
252 write_buffer (sink_t sink, const void *data, size_t datalen)
253 {
254   if (!sink || !sink->writefnc)
255     {
256       log_error ("%s:%s: sink not properly setup", SRCNAME, __func__);
257       return -1;
258     }
259   return sink->writefnc (sink, data, datalen);
260 }
261
262 /* Same as above but used for passing as callback function.  This
263    fucntion does not return an error code but the number of bytes
264    written.  */
265 int
266 write_buffer_for_cb (void *opaque, const void *data, size_t datalen)
267 {
268   sink_t sink = (sink_t) opaque;
269   sink->enc_counter += datalen;
270   return write_buffer (sink, data, datalen) ? -1 : datalen;
271 }
272
273
274 /* Write the string TEXT to the IStream STREAM.  Returns 0 on sucsess,
275    prints an error message and returns -1 on error.  */
276 int
277 write_string (sink_t sink, const char *text)
278 {
279   return write_buffer (sink, text, strlen (text));
280 }
281
282
283 /* Write the string TEXT1 and all folloing arguments of type (const
284    char*) to the SINK.  The list of argumens needs to be terminated
285    with a NULL.  Returns 0 on sucsess, prints an error message and
286    returns -1 on error.  */
287 static int
288 write_multistring (sink_t sink, const char *text1, ...)
289 {
290   va_list arg_ptr;
291   int rc;
292   const char *s;
293
294   va_start (arg_ptr, text1);
295   s = text1;
296   do
297     rc = write_string (sink, s);
298   while (!rc && (s=va_arg (arg_ptr, const char *)));
299   va_end (arg_ptr);
300   return rc;
301 }
302
303
304
305 /* Helper to write a boundary to the output sink.  The leading LF
306    will be written as well.  */
307 int
308 write_boundary (sink_t sink, const char *boundary, int lastone)
309 {
310   int rc = write_string (sink, "\r\n--");
311   if (!rc)
312     rc = write_string (sink, boundary);
313   if (!rc)
314     rc = write_string (sink, lastone? "--\r\n":"\r\n");
315   return rc;
316 }
317
318
319 /* Write DATALEN bytes of DATA to SINK in base64 encoding.  This
320    creates a complete Base64 chunk including the trailing fillers.  */
321 int
322 write_b64 (sink_t sink, const void *data, size_t datalen)
323 {
324   int rc;
325   const unsigned char *p;
326   unsigned char inbuf[4];
327   int idx, quads;
328   char outbuf[2048];
329   size_t outlen;
330
331   log_debug ("  writing base64 of length %d\n", (int)datalen);
332   idx = quads = 0;
333   outlen = 0;
334   for (p = (const unsigned char*)data; datalen; p++, datalen--)
335     {
336       inbuf[idx++] = *p;
337       if (idx > 2)
338         {
339           /* We need space for a quad and a possible CR,LF.  */
340           if (outlen+4+2 >= sizeof outbuf)
341             {
342               if ((rc = write_buffer (sink, outbuf, outlen)))
343                 return rc;
344               outlen = 0;
345             }
346           outbuf[outlen++] = bintoasc[(*inbuf>>2)&077];
347           outbuf[outlen++] = bintoasc[(((*inbuf<<4)&060)
348                                        |((inbuf[1] >> 4)&017))&077];
349           outbuf[outlen++] = bintoasc[(((inbuf[1]<<2)&074)
350                                        |((inbuf[2]>>6)&03))&077];
351           outbuf[outlen++] = bintoasc[inbuf[2]&077];
352           idx = 0;
353           if (++quads >= (64/4))
354             {
355               quads = 0;
356               outbuf[outlen++] = '\r';
357               outbuf[outlen++] = '\n';
358             }
359         }
360     }
361
362   /* We need space for a quad and a final CR,LF.  */
363   if (outlen+4+2 >= sizeof outbuf)
364     {
365       if ((rc = write_buffer (sink, outbuf, outlen)))
366         return rc;
367       outlen = 0;
368     }
369   if (idx)
370     {
371       outbuf[outlen++] = bintoasc[(*inbuf>>2)&077];
372       if (idx == 1)
373         {
374           outbuf[outlen++] = bintoasc[((*inbuf<<4)&060)&077];
375           outbuf[outlen++] = '=';
376           outbuf[outlen++] = '=';
377         }
378       else
379         {
380           outbuf[outlen++] = bintoasc[(((*inbuf<<4)&060)
381                                     |((inbuf[1]>>4)&017))&077];
382           outbuf[outlen++] = bintoasc[((inbuf[1]<<2)&074)&077];
383           outbuf[outlen++] = '=';
384         }
385       ++quads;
386     }
387
388   if (quads)
389     {
390       outbuf[outlen++] = '\r';
391       outbuf[outlen++] = '\n';
392     }
393
394   if (outlen)
395     {
396       if ((rc = write_buffer (sink, outbuf, outlen)))
397         return rc;
398     }
399
400   return 0;
401 }
402
403 /* Write DATALEN bytes of DATA to SINK in quoted-prinable encoding. */
404 static int
405 write_qp (sink_t sink, const void *data, size_t datalen)
406 {
407   int rc;
408   const unsigned char *p;
409   char outbuf[80];  /* We only need 76 octect + 2 for the lineend. */
410   int outidx;
411
412   /* Check whether the current character is followed by a line ending.
413      Note that the end of the etxt also counts as a lineending */
414 #define nextlf_p() ((datalen > 2 && p[1] == '\r' && p[2] == '\n') \
415                     || (datalen > 1 && p[1] == '\n')              \
416                     || datalen == 1 )
417
418   /* Macro to insert a soft line break if needed.  */
419 # define do_softlf(n) \
420           do {                                                        \
421             if (outidx + (n) > 76                                     \
422                 || (outidx + (n) == 76 && !nextlf_p()))               \
423               {                                                       \
424                 outbuf[outidx++] = '=';                               \
425                 outbuf[outidx++] = '\r';                              \
426                 outbuf[outidx++] = '\n';                              \
427                 if ((rc = write_buffer (sink, outbuf, outidx)))       \
428                   return rc;                                          \
429                 outidx = 0;                                           \
430               }                                                       \
431           } while (0)
432
433   log_debug ("  writing qp of length %d\n", (int)datalen);
434   outidx = 0;
435   for (p = (const unsigned char*) data; datalen; p++, datalen--)
436     {
437       if ((datalen > 1 && *p == '\r' && p[1] == '\n') || *p == '\n')
438         {
439           /* Line break.  */
440           outbuf[outidx++] = '\r';
441           outbuf[outidx++] = '\n';
442           if ((rc = write_buffer (sink, outbuf, outidx)))
443             return rc;
444           outidx = 0;
445           if (*p == '\r')
446             {
447               p++;
448               datalen--;
449             }
450         }
451       else if (*p == '\t' || *p == ' ')
452         {
453           /* Check whether tab or space is followed by a line break
454              which forbids verbatim encoding.  If we are already at
455              the end of the buffer we take that as a line end too. */
456           if (nextlf_p())
457             {
458               do_softlf (3);
459               outbuf[outidx++] = '=';
460               outbuf[outidx++] = tohex ((*p>>4)&15);
461               outbuf[outidx++] = tohex (*p&15);
462             }
463           else
464             {
465               do_softlf (1);
466               outbuf[outidx++] = *p;
467             }
468
469         }
470       else if (!outidx && *p == '.' && nextlf_p () )
471         {
472           /* We better protect a line with just a single dot.  */
473           outbuf[outidx++] = '=';
474           outbuf[outidx++] = tohex ((*p>>4)&15);
475           outbuf[outidx++] = tohex (*p&15);
476         }
477       else if (!outidx && datalen >= 5 && !memcmp (p, "From ", 5))
478         {
479           /* Protect the 'F' so that MTAs won't prefix the "From "
480              with an '>' */
481           outbuf[outidx++] = '=';
482           outbuf[outidx++] = tohex ((*p>>4)&15);
483           outbuf[outidx++] = tohex (*p&15);
484         }
485       else if (*p >= '!' && *p <= '~' && *p != '=')
486         {
487           do_softlf (1);
488           outbuf[outidx++] = *p;
489         }
490       else
491         {
492           do_softlf (3);
493           outbuf[outidx++] = '=';
494           outbuf[outidx++] = tohex ((*p>>4)&15);
495           outbuf[outidx++] = tohex (*p&15);
496         }
497     }
498   if (outidx)
499     {
500       outbuf[outidx++] = '\r';
501       outbuf[outidx++] = '\n';
502       if ((rc = write_buffer (sink, outbuf, outidx)))
503         return rc;
504     }
505
506 # undef do_softlf
507 # undef nextlf_p
508   return 0;
509 }
510
511
512 /* Write DATALEN bytes of DATA to SINK in plain ascii encoding. */
513 static int
514 write_plain (sink_t sink, const void *data, size_t datalen)
515 {
516   int rc;
517   const unsigned char *p;
518   char outbuf[100];
519   int outidx;
520
521   log_debug ("  writing ascii of length %d\n", (int)datalen);
522   outidx = 0;
523   for (p = (const unsigned char*) data; datalen; p++, datalen--)
524     {
525       if ((datalen > 1 && *p == '\r' && p[1] == '\n') || *p == '\n')
526         {
527           outbuf[outidx++] = '\r';
528           outbuf[outidx++] = '\n';
529           if ((rc = write_buffer (sink, outbuf, outidx)))
530             return rc;
531           outidx = 0;
532           if (*p == '\r')
533             {
534               p++;
535               datalen--;
536             }
537         }
538       else if (!outidx && *p == '.'
539                && ( (datalen > 2 && p[1] == '\r' && p[2] == '\n')
540                     || (datalen > 1 && p[1] == '\n')
541                     || datalen == 1))
542         {
543           /* Better protect a line with just a single dot.  We do
544              this by adding a space.  */
545           outbuf[outidx++] = *p;
546           outbuf[outidx++] = ' ';
547         }
548       else if (outidx > 80)
549         {
550           /* We should never be called for too long lines - QP should
551              have been used.  */
552           log_error ("%s:%s: BUG: line longer than exepcted",
553                      SRCNAME, __func__);
554           return -1;
555         }
556       else
557         outbuf[outidx++] = *p;
558     }
559
560   if (outidx)
561     {
562       outbuf[outidx++] = '\r';
563       outbuf[outidx++] = '\n';
564       if ((rc = write_buffer (sink, outbuf, outidx)))
565         return rc;
566     }
567
568   return 0;
569 }
570
571
572 /* Infer the conent type from the FILENAME.  The return value is
573    a static string there won't be an error return.  In case Bae 64
574    encoding is required for the type true will be stored at FORCE_B64;
575    however, this is only a shortcut and if that is not set, the caller
576    should infer the encoding by other means. */
577 static const char *
578 infer_content_type (const char * /*data*/, size_t /*datalen*/,
579                     const char *filename, int is_mapibody, int *force_b64)
580 {
581   static struct {
582     char b64;
583     const char *suffix;
584     const char *ct;
585   } suffix_table[] =
586     {
587       { 1, "3gp",   "video/3gpp" },
588       { 1, "abw",   "application/x-abiword" },
589       { 1, "ai",    "application/postscript" },
590       { 1, "au",    "audio/basic" },
591       { 1, "bin",   "application/octet-stream" },
592       { 1, "class", "application/java-vm" },
593       { 1, "cpt",   "application/mac-compactpro" },
594       { 0, "css",   "text/css" },
595       { 0, "csv",   "text/comma-separated-values" },
596       { 1, "deb",   "application/x-debian-package" },
597       { 1, "dl",    "video/dl" },
598       { 1, "doc",   "application/msword" },
599       { 1, "dv",    "video/dv" },
600       { 1, "dvi",   "application/x-dvi" },
601       { 1, "eml",   "message/rfc822" },
602       { 1, "eps",   "application/postscript" },
603       { 1, "fig",   "application/x-xfig" },
604       { 1, "flac",  "application/x-flac" },
605       { 1, "fli",   "video/fli" },
606       { 1, "gif",   "image/gif" },
607       { 1, "gl",    "video/gl" },
608       { 1, "gnumeric", "application/x-gnumeric" },
609       { 1, "hqx",   "application/mac-binhex40" },
610       { 1, "hta",   "application/hta" },
611       { 0, "htm",   "text/html" },
612       { 0, "html",  "text/html" },
613       { 0, "ics",   "text/calendar" },
614       { 1, "jar",   "application/java-archive" },
615       { 1, "jpeg",  "image/jpeg" },
616       { 1, "jpg",   "image/jpeg" },
617       { 1, "js",    "application/x-javascript" },
618       { 1, "latex", "application/x-latex" },
619       { 1, "lha",   "application/x-lha" },
620       { 1, "lzh",   "application/x-lzh" },
621       { 1, "lzx",   "application/x-lzx" },
622       { 1, "m3u",   "audio/mpegurl" },
623       { 1, "m4a",   "audio/mpeg" },
624       { 1, "mdb",   "application/msaccess" },
625       { 1, "midi",  "audio/midi" },
626       { 1, "mov",   "video/quicktime" },
627       { 1, "mp2",   "audio/mpeg" },
628       { 1, "mp3",   "audio/mpeg" },
629       { 1, "mp4",   "video/mp4" },
630       { 1, "mpeg",  "video/mpeg" },
631       { 1, "mpega", "audio/mpeg" },
632       { 1, "mpg",   "video/mpeg" },
633       { 1, "mpga",  "audio/mpeg" },
634       { 1, "msi",   "application/x-msi" },
635       { 1, "mxu",   "video/vnd.mpegurl" },
636       { 1, "nb",    "application/mathematica" },
637       { 1, "oda",   "application/oda" },
638       { 1, "odb",   "application/vnd.oasis.opendocument.database" },
639       { 1, "odc",   "application/vnd.oasis.opendocument.chart" },
640       { 1, "odf",   "application/vnd.oasis.opendocument.formula" },
641       { 1, "odg",   "application/vnd.oasis.opendocument.graphics" },
642       { 1, "odi",   "application/vnd.oasis.opendocument.image" },
643       { 1, "odm",   "application/vnd.oasis.opendocument.text-master" },
644       { 1, "odp",   "application/vnd.oasis.opendocument.presentation" },
645       { 1, "ods",   "application/vnd.oasis.opendocument.spreadsheet" },
646       { 1, "odt",   "application/vnd.oasis.opendocument.text" },
647       { 1, "ogg",   "application/ogg" },
648       { 1, "otg",   "application/vnd.oasis.opendocument.graphics-template" },
649       { 1, "oth",   "application/vnd.oasis.opendocument.text-web" },
650       { 1, "otp",  "application/vnd.oasis.opendocument.presentation-template"},
651       { 1, "ots",   "application/vnd.oasis.opendocument.spreadsheet-template"},
652       { 1, "ott",   "application/vnd.oasis.opendocument.text-template" },
653       { 1, "pdf",   "application/pdf" },
654       { 1, "png",   "image/png" },
655       { 1, "pps",   "application/vnd.ms-powerpoint" },
656       { 1, "ppt",   "application/vnd.ms-powerpoint" },
657       { 1, "prf",   "application/pics-rules" },
658       { 1, "ps",    "application/postscript" },
659       { 1, "qt",    "video/quicktime" },
660       { 1, "rar",   "application/rar" },
661       { 1, "rdf",   "application/rdf+xml" },
662       { 1, "rpm",   "application/x-redhat-package-manager" },
663       { 0, "rss",   "application/rss+xml" },
664       { 1, "ser",   "application/java-serialized-object" },
665       { 0, "sh",    "application/x-sh" },
666       { 0, "shtml", "text/html" },
667       { 1, "sid",   "audio/prs.sid" },
668       { 0, "smil",  "application/smil" },
669       { 1, "snd",   "audio/basic" },
670       { 0, "svg",   "image/svg+xml" },
671       { 1, "tar",   "application/x-tar" },
672       { 0, "texi",  "application/x-texinfo" },
673       { 0, "texinfo", "application/x-texinfo" },
674       { 1, "tif",   "image/tiff" },
675       { 1, "tiff",  "image/tiff" },
676       { 1, "torrent", "application/x-bittorrent" },
677       { 1, "tsp",   "application/dsptype" },
678       { 0, "vrml",  "model/vrml" },
679       { 1, "vsd",   "application/vnd.visio" },
680       { 1, "wp5",   "application/wordperfect5.1" },
681       { 1, "wpd",   "application/wordperfect" },
682       { 0, "xhtml", "application/xhtml+xml" },
683       { 1, "xlb",   "application/vnd.ms-excel" },
684       { 1, "xls",   "application/vnd.ms-excel" },
685       { 1, "xlt",   "application/vnd.ms-excel" },
686       { 0, "xml",   "application/xml" },
687       { 0, "xsl",   "application/xml" },
688       { 0, "xul",   "application/vnd.mozilla.xul+xml" },
689       { 1, "zip",   "application/zip" },
690       { 0, NULL, NULL }
691     };
692   int i;
693   std::string suffix;
694
695   *force_b64 = 0;
696   if (filename)
697     suffix = strrchr (filename, '.');
698
699   if (!suffix.empty())
700     {
701       suffix.erase(0, 1);
702       std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower);
703       for (i=0; suffix_table[i].suffix; i++)
704         {
705           if (!strcmp (suffix_table[i].suffix, suffix.c_str()))
706             {
707               if (suffix_table[i].b64)
708                 *force_b64 = 1;
709               return suffix_table[i].ct;
710             }
711         }
712     }
713
714   /* Not found via filename, look at the content.  */
715
716   if (is_mapibody == 1)
717     {
718       return "text/plain";
719     }
720   else if (is_mapibody == 2)
721     {
722       return "text/html";
723     }
724   return "application/octet-stream";
725 }
726
727 /* Figure out the best encoding to be used for the part.  Return values are
728      0: Plain ASCII.
729      1: Quoted Printable
730      2: Base64  */
731 static int
732 infer_content_encoding (const void *data, size_t datalen)
733 {
734   const unsigned char *p;
735   int need_qp;
736   size_t len, maxlen, highbin, lowbin, ntotal;
737
738   ntotal = datalen;
739   len = maxlen = lowbin = highbin = 0;
740   need_qp = 0;
741   for (p = (const unsigned char*) data; datalen; p++, datalen--)
742     {
743       len++;
744       if ((*p & 0x80))
745         highbin++;
746       else if ((datalen > 1 && *p == '\r' && p[1] == '\n') || *p == '\n')
747         {
748           len--;
749           if (len > maxlen)
750             maxlen = len;
751           len = 0;
752         }
753       else if (*p == '\r')
754         {
755           /* CR not followed by a linefeed. */
756           lowbin++;
757         }
758       else if (*p == '\t' || *p == ' ' || *p == '\f')
759         ;
760       else if (*p < ' ' || *p == 127)
761         lowbin++;
762       else if (len == 1 && datalen > 2
763                && *p == '-' && p[1] == '-' && p[2] == ' '
764                && ( (datalen > 4 && p[3] == '\r' && p[4] == '\n')
765                     || (datalen > 3 && p[3] == '\n')
766                     || datalen == 3))
767         {
768           /* This is a "-- \r\n" line, thus it indicates the usual
769              signature line delimiter.  We need to protect the
770              trailing space.  */
771           need_qp = 1;
772         }
773       else if (len == 1 && datalen > 5 && !memcmp (p, "--=-=", 5))
774         {
775           /* This look pretty much like a our own boundary.
776              We better protect it by forcing QP encoding.  */
777           need_qp = 1;
778         }
779       else if (len == 1 && datalen >= 5 && !memcmp (p, "From ", 5))
780         {
781           /* The usual From hack is required so that MTAs do not
782              prefix it with an '>'.  */
783           need_qp = 1;
784         }
785     }
786   if (len > maxlen)
787     maxlen = len;
788
789   if (maxlen <= 76 && !lowbin && !highbin && !need_qp)
790     return 0; /* Plain ASCII is sufficient.  */
791
792   /* Somewhere in the Outlook documentation 20% is mentioned as
793      discriminating value for Base64.  Though our counting won't be
794      identical we use that value to behave closely to it. */
795   if (ntotal && ((float)(lowbin+highbin))/ntotal < 0.20)
796     return 1; /* Use quoted printable.  */
797
798   return 2;   /* Use base64.  */
799 }
800
801
802 /* Convert an utf8 input string to RFC2047 base64 encoding which
803    is the subset of RFC2047 outlook likes.
804    Return value needs to be freed.
805    */
806 static char *
807 utf8_to_rfc2047b (const char *input)
808 {
809   char *ret,
810        *encoded;
811   int inferred_encoding = 0;
812   if (!input)
813     {
814       return NULL;
815     }
816   inferred_encoding = infer_content_encoding (input, strlen (input));
817   if (!inferred_encoding)
818     {
819       return xstrdup (input);
820     }
821   log_debug ("%s:%s: Encoding attachment filename. With: %s ",
822              SRCNAME, __func__, inferred_encoding == 2 ? "Base64" : "QP");
823
824   if (inferred_encoding == 2)
825     {
826       encoded = b64_encode (input, strlen (input));
827       if (gpgrt_asprintf (&ret, "=?utf-8?B?%s?=", encoded) == -1)
828         {
829           log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
830           xfree (encoded);
831           return NULL;
832         }
833     }
834   else
835     {
836       /* There is a Bug here. If you encode 4 Byte UTF-8 outlook can't
837          handle it itself. And sends out a message with ?? inserted in
838          that place. This triggers an invalid signature. */
839       encoded = qp_encode (input, strlen (input), NULL);
840       if (gpgrt_asprintf (&ret, "=?utf-8?Q?%s?=", encoded) == -1)
841         {
842           log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
843           xfree (encoded);
844           return NULL;
845         }
846     }
847   xfree (encoded);
848   return ret;
849 }
850
851 /* Write a MIME part to SINK.  First the BOUNDARY is written (unless
852    it is NULL) then the DATA is analyzed and appropriate headers are
853    written.  If FILENAME is given it will be added to the part's
854    header.  IS_MAPIBODY should be passed as true if the data has been
855    retrieved from the body property.  */
856 static int
857 write_part (sink_t sink, const char *data, size_t datalen,
858             const char *boundary, const char *filename, int is_mapibody,
859             const char *content_id = NULL)
860 {
861   int rc;
862   const char *ct;
863   int use_b64, use_qp, is_text;
864   char *encoded_filename;
865
866   if (filename)
867     {
868       /* If there is a filename strip the directory part.  Take care
869          that there might be slashes or backslashes.  */
870       const char *s1 = strrchr (filename, '/');
871       const char *s2 = strrchr (filename, '\\');
872
873       if (!s1)
874         s1 = s2;
875       else if (s1 && s2 && s2 > s1)
876         s1 = s2;
877
878       if (s1)
879         filename = s1;
880       if (*filename && filename[1] == ':')
881         filename += 2;
882       if (!*filename)
883         filename = NULL;
884     }
885
886   log_debug ("Writing part of length %d%s filename=`%s'\n",
887              (int)datalen, is_mapibody? " (body)":"",
888              filename?filename:"[none]");
889
890   ct = infer_content_type (data, datalen, filename, is_mapibody, &use_b64);
891   use_qp = 0;
892   if (!use_b64)
893     {
894       switch (infer_content_encoding (data, datalen))
895         {
896         case 0: break;
897         case 1: use_qp = 1; break;
898         default: use_b64 = 1; break;
899         }
900     }
901   is_text = !strncmp (ct, "text/", 5);
902
903   if (boundary)
904     if ((rc = write_boundary (sink, boundary, 0)))
905       return rc;
906   if ((rc=write_multistring (sink,
907                              "Content-Type: ", ct,
908                              (is_text || filename? ";\r\n" :"\r\n"),
909                              NULL)))
910     return rc;
911
912   /* OL inserts a charset parameter in many cases, so we do it right
913      away for all text parts.  We can assume us-ascii if no special
914      encoding is required.  */
915   if (is_text)
916     if ((rc=write_multistring (sink,
917                                "\tcharset=\"",
918                                (!use_qp && !use_b64? "us-ascii" : "utf-8"),
919                                filename ? "\";\r\n" : "\"\r\n",
920                                NULL)))
921       return rc;
922
923   encoded_filename = utf8_to_rfc2047b (filename);
924   if (encoded_filename)
925     if ((rc=write_multistring (sink,
926                                "\tname=\"", encoded_filename, "\"\r\n",
927                                NULL)))
928       return rc;
929
930   /* Note that we need to output even 7bit because OL inserts that
931      anyway.  */
932   if ((rc = write_multistring (sink,
933                                "Content-Transfer-Encoding: ",
934                                (use_b64? "base64\r\n":
935                                 use_qp? "quoted-printable\r\n":"7bit\r\n"),
936                                NULL)))
937     return rc;
938
939   if (content_id)
940     {
941       if ((rc=write_multistring (sink,
942                                  "Content-ID: <", content_id, ">\r\n",
943                                  NULL)))
944         return rc;
945     }
946   else if (encoded_filename)
947     if ((rc=write_multistring (sink,
948                                "Content-Disposition: attachment;\r\n"
949                                "\tfilename=\"", encoded_filename, "\"\r\n",
950                                NULL)))
951       return rc;
952
953   xfree(encoded_filename);
954
955   /* Write delimiter.  */
956   if ((rc = write_string (sink, "\r\n")))
957     return rc;
958
959   /* Write the content.  */
960   if (use_b64)
961     rc = write_b64 (sink, data, datalen);
962   else if (use_qp)
963     rc = write_qp (sink, data, datalen);
964   else
965     rc = write_plain (sink, data, datalen);
966
967   return rc;
968 }
969
970
971 /* Return the number of attachments in TABLE to be put into the MIME
972    message.  */
973 int
974 count_usable_attachments (mapi_attach_item_t *table)
975 {
976   int idx, count = 0;
977
978   if (table)
979     for (idx=0; !table[idx].end_of_table; idx++)
980       if (table[idx].attach_type == ATTACHTYPE_UNKNOWN
981           && table[idx].method == ATTACH_BY_VALUE)
982         count++;
983   return count;
984 }
985
986 /* Write out all attachments from TABLE separated by BOUNDARY to SINK.
987    This function needs to be syncronized with count_usable_attachments.
988    If only_related is 1 only include attachments for multipart/related they
989    are excluded otherwise. */
990 static int
991 write_attachments (sink_t sink,
992                    LPMESSAGE message, mapi_attach_item_t *table,
993                    const char *boundary, int only_related)
994 {
995   int idx, rc;
996   char *buffer;
997   size_t buflen;
998
999   if (table)
1000     for (idx=0; !table[idx].end_of_table; idx++)
1001       if (table[idx].attach_type == ATTACHTYPE_UNKNOWN
1002           && table[idx].method == ATTACH_BY_VALUE)
1003         {
1004           if (only_related && !table[idx].content_id)
1005             {
1006               continue;
1007             }
1008           else if (!only_related && table[idx].content_id)
1009             {
1010               continue;
1011             }
1012           buffer = mapi_get_attach (message, 0, table+idx, &buflen);
1013           if (!buffer)
1014             log_debug ("Attachment at index %d not found\n", idx);
1015           else
1016             log_debug ("Attachment at index %d: length=%d\n", idx, (int)buflen);
1017           if (!buffer)
1018             return -1;
1019           rc = write_part (sink, buffer, buflen, boundary,
1020                            table[idx].filename, 0, table[idx].content_id);
1021           if (rc)
1022             {
1023               log_error ("Write part returned err: %i", rc);
1024             }
1025           xfree (buffer);
1026         }
1027   return 0;
1028 }
1029
1030 /* Returns 1 if all attachments are related. 2 if there is a
1031    related and a mixed attachment. 0 if there are no other parts*/
1032 static int
1033 is_related (Mail *mail, mapi_attach_item_t *table)
1034 {
1035   if (!mail || !mail->is_html_alternative () || !table)
1036     {
1037       return 0;
1038     }
1039
1040   int related = 0;
1041   int mixed = 0;
1042   for (int idx = 0; !table[idx].end_of_table; idx++)
1043     {
1044       if (table[idx].content_id)
1045         {
1046           related = 1;
1047         }
1048       else
1049         {
1050           mixed = 1;
1051         }
1052     }
1053   return mixed + related;
1054 }
1055
1056
1057 /* Delete all attachments from TABLE except for the one we just created */
1058 static int
1059 delete_all_attachments (LPMESSAGE message, mapi_attach_item_t *table)
1060 {
1061   HRESULT hr;
1062   int idx;
1063
1064   if (table)
1065     for (idx=0; !table[idx].end_of_table; idx++)
1066       {
1067         if (table[idx].attach_type == ATTACHTYPE_MOSSTEMPL
1068             && table[idx].filename
1069             && !strcmp (table[idx].filename, MIMEATTACHFILENAME))
1070           continue;
1071         hr = message->DeleteAttach (table[idx].mapipos, 0, NULL, 0);
1072         if (hr)
1073           {
1074             log_error ("%s:%s: DeleteAttach failed: hr=%#lx\n",
1075                        SRCNAME, __func__, hr);
1076             return -1;
1077           }
1078       }
1079   return 0;
1080 }
1081
1082
1083 /* Commit changes to the attachment ATTACH and release the object.
1084    SINK needs to be passed as well and will also be closed.  Note that
1085    the address of ATTACH is expected so that the fucntion can set it
1086    to NULL. */
1087 int
1088 close_mapi_attachment (LPATTACH *attach, sink_t sink)
1089 {
1090   HRESULT hr;
1091   LPSTREAM stream = sink ? (LPSTREAM) sink->cb_data : NULL;
1092
1093   if (!stream)
1094     {
1095       log_error ("%s:%s: sink not setup", SRCNAME, __func__);
1096       return -1;
1097     }
1098   hr = stream->Commit (0);
1099   if (hr)
1100     {
1101       log_error ("%s:%s: Commiting output stream failed: hr=%#lx",
1102                  SRCNAME, __func__, hr);
1103       return -1;
1104     }
1105   gpgol_release (stream);
1106   sink->cb_data = NULL;
1107   hr = (*attach)->SaveChanges (0);
1108   if (hr)
1109     {
1110       log_error ("%s:%s: SaveChanges of the attachment failed: hr=%#lx\n",
1111                  SRCNAME, __func__, hr);
1112       return -1;
1113     }
1114   gpgol_release ((*attach));
1115   *attach = NULL;
1116   return 0;
1117 }
1118
1119
1120 /* Cancel changes to the attachment ATTACH and release the object.
1121    SINK needs to be passed as well and will also be closed.  Note that
1122    the address of ATTACH is expected so that the fucntion can set it
1123    to NULL. */
1124 void
1125 cancel_mapi_attachment (LPATTACH *attach, sink_t sink)
1126 {
1127   LPSTREAM stream = sink ? (LPSTREAM) sink->cb_data : NULL;
1128
1129   if (stream)
1130     {
1131       stream->Revert();
1132       gpgol_release (stream);
1133       sink->cb_data = NULL;
1134     }
1135   if (*attach)
1136     {
1137       /* Fixme: Should we try to delete it or is there a Revert method? */
1138       gpgol_release ((*attach));
1139       *attach = NULL;
1140     }
1141 }
1142
1143
1144
1145 /* Do the final processing for a message. */
1146 int
1147 finalize_message (LPMESSAGE message, mapi_attach_item_t *att_table,
1148                   protocol_t protocol, int encrypt, bool is_inline)
1149 {
1150   HRESULT hr = 0;
1151   SPropValue prop;
1152   SPropTagArray proparray;
1153
1154   /* Set the message class.  */
1155   prop.ulPropTag = PR_MESSAGE_CLASS_A;
1156   if (encrypt)
1157     {
1158       prop.Value.lpszA = strdup ("IPM.Note.InfoPathForm.GpgOL.SMIME.MultipartSigned");
1159     }
1160   else
1161     {
1162       prop.Value.lpszA = strdup ("IPM.Note.InfoPathForm.GpgOLS.SMIME.MultipartSigned");
1163     }
1164
1165   if (!is_inline)
1166     {
1167       /* For inline we stick with IPM.Note because Exchange Online would
1168          error out if we tried our S/MIME conversion trick with a text
1169          plain message */
1170       hr = message->SetProps(1, &prop, NULL);
1171     }
1172   xfree(prop.Value.lpszA);
1173   if (hr)
1174     {
1175       log_error ("%s:%s: error setting the message class: hr=%#lx\n",
1176                  SRCNAME, __func__, hr);
1177       return -1;
1178     }
1179
1180   /* Set a special property so that we are later able to identify
1181      messages signed or encrypted by us.  */
1182   if (mapi_set_sig_status (message, "@"))
1183     {
1184       log_error ("%s:%s: error setting sigstatus",
1185                  SRCNAME, __func__);
1186       return -1;
1187     }
1188
1189   /* We also need to set the message class into our custom
1190      property. This override is at least required for encrypted
1191      messages.  */
1192   if (is_inline && mapi_set_gpgol_msg_class (message,
1193                                           (encrypt?
1194                                            (protocol == PROTOCOL_SMIME?
1195                                             "IPM.Note.GpgOL.OpaqueEncrypted" :
1196                                             "IPM.Note.GpgOL.PGPMessage") :
1197                                             "IPM.Note.GpgOL.ClearSigned")))
1198     {
1199       log_error ("%s:%s: error setting gpgol msgclass",
1200                  SRCNAME, __func__);
1201       return -1;
1202     }
1203   if (!is_inline && mapi_set_gpgol_msg_class (message,
1204                                 (encrypt?
1205                                  (protocol == PROTOCOL_SMIME?
1206                                   "IPM.Note.GpgOL.OpaqueEncrypted" :
1207                                   "IPM.Note.GpgOL.MultipartEncrypted") :
1208                                  "IPM.Note.GpgOL.MultipartSigned")))
1209     {
1210       log_error ("%s:%s: error setting gpgol msgclass",
1211                  SRCNAME, __func__);
1212       return -1;
1213     }
1214
1215   proparray.cValues = 1;
1216   proparray.aulPropTag[0] = PR_BODY;
1217   hr = message->DeleteProps (&proparray, NULL);
1218   if (hr)
1219     {
1220       log_debug_w32 (hr, "%s:%s: deleting PR_BODY failed",
1221                      SRCNAME, __func__);
1222     }
1223
1224   proparray.cValues = 1;
1225   proparray.aulPropTag[0] = PR_BODY_HTML;
1226   hr = message->DeleteProps (&proparray, NULL);
1227   if (hr)
1228     {
1229       log_debug_w32 (hr, "%s:%s: deleting PR_BODY_HTML failed",
1230                      SRCNAME, __func__);
1231     }
1232
1233   /* Now delete all parts of the MAPI message except for the one
1234      attachment we just created.  */
1235   if (delete_all_attachments (message, att_table))
1236     {
1237       log_error ("%s:%s: error deleting attachments",
1238                  SRCNAME, __func__);
1239       return -1;
1240     }
1241
1242   /* Remove the draft info so that we don't leak the information on
1243      whether the message has been signed etc.  */
1244   mapi_set_gpgol_draft_info (message, NULL);
1245
1246   if (mapi_save_changes (message, KEEP_OPEN_READWRITE|FORCE_SAVE))
1247     {
1248       log_error ("%s:%s: error saving changes.",
1249                  SRCNAME, __func__);
1250       return -1;
1251     }
1252   return 0;
1253 }
1254
1255
1256 \f
1257 /* Sink write method used by mime_sign.  We write the data to the
1258    filter and also to the EXTRASINK but we don't pass a flush request
1259    to EXTRASINK. */
1260 static int
1261 sink_hashing_write (sink_t hashsink, const void *data, size_t datalen)
1262 {
1263   int rc;
1264   engine_filter_t filter = (engine_filter_t) hashsink->cb_data;
1265
1266   if (!filter || !hashsink->extrasink)
1267     {
1268       log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
1269       return -1;
1270     }
1271
1272   rc = engine_filter (filter, data, datalen);
1273   if (!rc && data && datalen)
1274     write_buffer (hashsink->extrasink, data, datalen);
1275   return rc;
1276 }
1277
1278
1279 /* This function is called by the filter to collect the output which
1280    is a detached signature.  */
1281 static int
1282 collect_signature (void *opaque, const void *data, size_t datalen)
1283 {
1284   struct databuf_s *db = (databuf_s *)opaque;
1285
1286   if (db->len + datalen >= db->size)
1287     {
1288       db->size += datalen + 1024;
1289       db->buf = (char*) xrealloc (db->buf, db->size);
1290     }
1291   memcpy (db->buf + db->len, data, datalen);
1292   db->len += datalen;
1293
1294   return datalen;
1295 }
1296
1297
1298 /* Helper to create the signing header.  This includes enough space
1299    for later fixup of the micalg parameter.  The MIME version is only
1300    written if FIRST is set.  */
1301 void
1302 create_top_signing_header (char *buffer, size_t buflen, protocol_t protocol,
1303                            int first, const char *boundary, const char *micalg)
1304 {
1305   snprintf (buffer, buflen,
1306             "%s"
1307             "Content-Type: multipart/signed;\r\n"
1308             "\tprotocol=\"application/%s\";\r\n"
1309             "\tmicalg=%-15.15s;\r\n"
1310             "\tboundary=\"%s\"\r\n"
1311             "\r\n",
1312             first? "MIME-Version: 1.0\r\n":"",
1313             (protocol==PROTOCOL_OPENPGP? "pgp-signature":"pkcs7-signature"),
1314             micalg, boundary);
1315 }
1316
1317 /* Add the body, either as multipart/alternative or just as the
1318   simple body part. Depending on the format set in outlook. To
1319   avoid memory duplication it takes the plain body as parameter.
1320
1321   Boundary is the potential outer boundary of a multipart/mixed
1322   mail. If it is null we assume the multipart/alternative is
1323   the only part.
1324
1325   return is zero on success.
1326 */
1327 static int
1328 add_body (Mail *mail, const char *boundary, sink_t sink,
1329           const char *plain_body)
1330 {
1331   if (!plain_body)
1332     {
1333       return 0;
1334     }
1335   bool is_alternative = false;
1336   if (mail)
1337     {
1338       is_alternative = mail->is_html_alternative ();
1339     }
1340
1341   int rc = 0;
1342   if (!is_alternative || !plain_body)
1343     {
1344       if (plain_body)
1345         {
1346           rc = write_part (sink, plain_body, strlen (plain_body),
1347                            boundary, NULL, 1);
1348         }
1349       /* Just the plain body or no body. We are done. */
1350       return rc;
1351     }
1352
1353   /* Add a new multipart / mixed element. */
1354   if (boundary && write_boundary (sink, boundary, 0))
1355     {
1356       TRACEPOINT;
1357       return 1;
1358     }
1359
1360   /* Now for the multipart/alternative part. We never do HTML only. */
1361   char alt_boundary [BOUNDARYSIZE+1];
1362   generate_boundary (alt_boundary);
1363
1364   if ((rc=write_multistring (sink,
1365                             "Content-Type: multipart/alternative;\r\n",
1366                             "\tboundary=\"", alt_boundary, "\"\r\n",
1367                             "\r\n",  /* <-- extra line */
1368                             NULL)))
1369     {
1370       TRACEPOINT;
1371       return rc;
1372     }
1373
1374   /* Now the plain body part */
1375   if ((rc = write_part (sink, plain_body, strlen (plain_body),
1376                        alt_boundary, NULL, 1)))
1377     {
1378       TRACEPOINT;
1379       return rc;
1380     }
1381
1382   /* Now the html body. It is somehow not accessible through PR_HTML,
1383      OutlookSpy also shows MAPI Unsupported (but shows the data) strange.
1384      We just cache it. Memory is cheap :-) */
1385   char *html_body = mail->take_cached_html_body();
1386   if (!html_body)
1387     {
1388       log_error ("%s:%s: BUG: Body but no html body in alternative mail?",
1389                  SRCNAME, __func__);
1390       return -1;
1391     }
1392
1393   rc = write_part (sink, html_body, strlen (html_body),
1394                    alt_boundary, NULL, 2);
1395   xfree (html_body);
1396   if (rc)
1397     {
1398       TRACEPOINT;
1399       return rc;
1400     }
1401   /* Finish our multipart */
1402   return write_boundary (sink, alt_boundary, 1);
1403 }
1404
1405 /* Add the body and attachments. Does multipart handling. */
1406 int
1407 add_body_and_attachments (sink_t sink, LPMESSAGE message,
1408                           mapi_attach_item_t *att_table, Mail *mail,
1409                           const char *body, int n_att_usable)
1410 {
1411   int related = is_related (mail, att_table);
1412   int rc = 0;
1413   char inner_boundary[BOUNDARYSIZE+1];
1414   char outer_boundary[BOUNDARYSIZE+1];
1415   *outer_boundary = 0;
1416   *inner_boundary = 0;
1417
1418   if (((body && n_att_usable) || n_att_usable > 1) && related == 1)
1419     {
1420       /* A body and at least one attachment or more than one attachment  */
1421       generate_boundary (outer_boundary);
1422       if ((rc=write_multistring (sink,
1423                                  "Content-Type: multipart/related;\r\n",
1424                                  "\tboundary=\"", outer_boundary, "\"\r\n",
1425                                  "\r\n", /* <--- Outlook adds an extra line. */
1426                                  NULL)))
1427         return rc;
1428     }
1429   else if ((body && n_att_usable) || n_att_usable > 1)
1430     {
1431       generate_boundary (outer_boundary);
1432       if ((rc=write_multistring (sink,
1433                                  "Content-Type: multipart/mixed;\r\n",
1434                                  "\tboundary=\"", outer_boundary, "\"\r\n",
1435                                  "\r\n", /* <--- Outlook adds an extra line. */
1436                                  NULL)))
1437         return rc;
1438     }
1439
1440   /* Only one part.  */
1441   if (*outer_boundary && related == 2)
1442     {
1443       /* We have attachments that are related to the body and unrelated
1444          attachments. So we need another part. */
1445       if ((rc=write_boundary (sink, outer_boundary, 0)))
1446         {
1447           return rc;
1448         }
1449       generate_boundary (inner_boundary);
1450       if ((rc=write_multistring (sink,
1451                                  "Content-Type: multipart/related;\r\n",
1452                                  "\tboundary=\"", inner_boundary, "\"\r\n",
1453                                  "\r\n", /* <--- Outlook adds an extra line. */
1454                                  NULL)))
1455         {
1456           return rc;
1457         }
1458     }
1459
1460
1461   if ((rc=add_body (mail, *inner_boundary ? inner_boundary :
1462                           *outer_boundary ? outer_boundary : NULL,
1463                     sink, body)))
1464     {
1465       log_error ("%s:%s: Adding the body failed.",
1466                  SRCNAME, __func__);
1467       return rc;
1468     }
1469   if (!rc && n_att_usable && related)
1470     {
1471       /* Write the related attachments. */
1472       rc = write_attachments (sink, message, att_table,
1473                               *inner_boundary? inner_boundary :
1474                               *outer_boundary? outer_boundary : NULL, 1);
1475       if (rc)
1476         {
1477           return rc;
1478         }
1479       /* Close the related part if neccessary.*/
1480       if (*inner_boundary && (rc=write_boundary (sink, inner_boundary, 1)))
1481         {
1482           return rc;
1483         }
1484     }
1485
1486   /* Now write the other attachments */
1487   if (!rc && n_att_usable)
1488     rc = write_attachments (sink, message, att_table,
1489                             *outer_boundary? outer_boundary : NULL, 0);
1490
1491   /* Finish the possible multipart/mixed. */
1492   if (*outer_boundary && (rc = write_boundary (sink, outer_boundary, 1)))
1493     return rc;
1494
1495   return rc;
1496 }
1497
1498 /* Main body of mime_sign without the the code to delete the original
1499    attachments.  On success the function returns the current
1500    attachment table at R_ATT_TABLE or sets this to NULL on error.  If
1501    TMPSINK is set no attachment will be created but the output
1502    written to that sink.  */
1503 static int
1504 do_mime_sign (LPMESSAGE message, HWND hwnd, protocol_t protocol,
1505               mapi_attach_item_t **r_att_table, sink_t tmpsink,
1506               unsigned int session_number, const char *sender,
1507               Mail *mail)
1508 {
1509   int result = -1;
1510   int rc;
1511   LPATTACH attach = NULL;
1512   struct sink_s sinkmem;
1513   sink_t sink = &sinkmem;
1514   struct sink_s hashsinkmem;
1515   sink_t hashsink = &hashsinkmem;
1516   char boundary[BOUNDARYSIZE+1];
1517   mapi_attach_item_t *att_table = NULL;
1518   char *body = NULL;
1519   int n_att_usable;
1520   char top_header[BOUNDARYSIZE+200];
1521   engine_filter_t filter = NULL;
1522   struct databuf_s sigbuffer;
1523   char *my_sender = NULL;
1524
1525   *r_att_table = NULL;
1526
1527   memset (sink, 0, sizeof *sink);
1528   memset (hashsink, 0, sizeof *hashsink);
1529   memset (&sigbuffer, 0, sizeof sigbuffer);
1530
1531   if (tmpsink)
1532     {
1533       attach = NULL;
1534       sink = tmpsink;
1535     }
1536   else
1537     {
1538       attach = create_mapi_attachment (message, sink);
1539       if (!attach)
1540         return -1;
1541     }
1542
1543   /* Take the Body from the mail if possible. This is a fix for
1544      GnuPG-Bug-ID: T3614 because the body is not always properly
1545      updated in MAPI when sending. */
1546   if (mail)
1547     {
1548       body = mail->take_cached_plain_body ();
1549     }
1550   /* Get the attachment info and the body.  */
1551   if (!body)
1552     {
1553       body = mapi_get_body (message, NULL);
1554     }
1555   if (body && !*body)
1556     {
1557       xfree (body);
1558       body = NULL;
1559     }
1560   att_table = mapi_create_attach_table (message, 0);
1561   n_att_usable = count_usable_attachments (att_table);
1562   if (!n_att_usable && !body)
1563     {
1564       log_debug ("%s:%s: can't sign an empty message\n", SRCNAME, __func__);
1565       result = gpg_error (GPG_ERR_NO_DATA);
1566       goto failure;
1567     }
1568
1569   /* Prepare the signing. */
1570   if (engine_create_filter (&filter, collect_signature, &sigbuffer))
1571     goto failure;
1572
1573   if (session_number)
1574     {
1575       engine_set_session_number (filter, session_number);
1576       {
1577         char *tmp = mapi_get_subject (message);
1578         engine_set_session_title (filter, tmp);
1579         xfree (tmp);
1580       }
1581     }
1582
1583   if (sender)
1584     my_sender = xstrdup (sender);
1585   else
1586     my_sender = mapi_get_sender (message);
1587   if (engine_sign_start (filter, hwnd, protocol, my_sender, &protocol))
1588     goto failure;
1589
1590   protocol = check_protocol (protocol);
1591   if (protocol == PROTOCOL_UNKNOWN)
1592     {
1593       log_error ("%s:%s: no protocol selected", SRCNAME, __func__);
1594       goto failure;
1595     }
1596
1597
1598   /* Write the top header.  */
1599   generate_boundary (boundary);
1600   create_top_signing_header (top_header, sizeof top_header,
1601                              protocol, 1, boundary, "xxx");
1602   if ((rc = write_string (sink, top_header)))
1603     goto failure;
1604
1605   /* Write the boundary so that it is not included in the hashing.  */
1606   if ((rc = write_boundary (sink, boundary, 0)))
1607     goto failure;
1608
1609   /* Create a new sink for hashing and write/hash our content.  */
1610   hashsink->cb_data = filter;
1611   hashsink->extrasink = sink;
1612   hashsink->writefnc = sink_hashing_write;
1613
1614   /* Add the plaintext */
1615   if (add_body_and_attachments (hashsink, message, att_table, mail,
1616                                 body, n_att_usable))
1617     goto failure;
1618
1619   xfree (body);
1620   body = NULL;
1621
1622   /* Here we are ready with the hashing.  Flush the filter and wait
1623      for the signing process to finish.  */
1624   if ((rc = write_buffer (hashsink, NULL, 0)))
1625     goto failure;
1626   if ((rc = engine_wait (filter)))
1627     goto failure;
1628   filter = NULL; /* Not valid anymore.  */
1629   hashsink->cb_data = NULL; /* Not needed anymore.  */
1630
1631
1632   /* Write signature attachment.  */
1633   if ((rc = write_boundary (sink, boundary, 0)))
1634     goto failure;
1635
1636   if (protocol == PROTOCOL_OPENPGP)
1637     {
1638       rc = write_string (sink,
1639                          "Content-Type: application/pgp-signature\r\n");
1640     }
1641   else
1642     {
1643       rc = write_string (sink,
1644                          "Content-Transfer-Encoding: base64\r\n"
1645                          "Content-Type: application/pkcs7-signature\r\n");
1646       /* rc = write_string (sink, */
1647       /*                    "Content-Type: application/x-pkcs7-signature\r\n" */
1648       /*                    "\tname=\"smime.p7s\"\r\n" */
1649       /*                    "Content-Transfer-Encoding: base64\r\n" */
1650       /*                    "Content-Disposition: attachment;\r\n" */
1651       /*                    "\tfilename=\"smime.p7s\"\r\n"); */
1652
1653     }
1654   /* About the above code:
1655      If we would add "Content-Transfer-Encoding: 7bit\r\n" to this
1656      attachment, Outlooks does not proceed with sending and even does
1657      not return any error.  A wild guess is that while OL adds this
1658      header itself, it detects that it already exists and somehow gets
1659      into a problem.  It is not a problem with the other parts,
1660      though.  Hmmm, triggered by the top levels CT protocol parameter?
1661      Anyway, it is not required that we add it as we won't hash it.
1662      Note, that this only holds for OpenPGP; for S/MIME we need to set
1663      set CTE.  We even write it before the CT because that is the same
1664      as Outlook would do it for a missing CTE. */
1665   if (rc)
1666     goto failure;
1667
1668   if ((rc = write_string (sink, "\r\n")))
1669     goto failure;
1670
1671   /* Write the signature.  We add an extra CR,LF which should not harm
1672      and a terminating 0. */
1673   collect_signature (&sigbuffer, "\r\n", 3);
1674   if ((rc = write_string (sink, sigbuffer.buf)))
1675     goto failure;
1676
1677
1678   /* Write the final boundary and finish the attachment.  */
1679   if ((rc = write_boundary (sink, boundary, 1)))
1680     goto failure;
1681
1682   /* Fixup the micalg parameter.  */
1683   {
1684     HRESULT hr;
1685     LARGE_INTEGER off;
1686     LPSTREAM stream = (LPSTREAM) sink->cb_data;
1687
1688     off.QuadPart = 0;
1689     hr = stream->Seek (off, STREAM_SEEK_SET, NULL);
1690     if (hr)
1691       {
1692         log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
1693                    SRCNAME, __func__, hr);
1694         goto failure;
1695       }
1696
1697     create_top_signing_header (top_header, sizeof top_header,
1698                                protocol, 1, boundary,
1699                                protocol == PROTOCOL_SMIME? "sha1":"pgp-sha1");
1700
1701     hr = stream->Write (top_header, strlen (top_header), NULL);
1702     if (hr)
1703       {
1704         log_error ("%s:%s: writing fixed micalg failed: hr=%#lx",
1705                    SRCNAME, __func__, hr);
1706         goto failure;
1707       }
1708
1709     /* Better seek again to the end. */
1710     off.QuadPart = 0;
1711     hr = stream->Seek (off, STREAM_SEEK_END, NULL);
1712     if (hr)
1713       {
1714         log_error ("%s:%s: seeking back to the end failed: hr=%#lx",
1715                    SRCNAME, __func__, hr);
1716         goto failure;
1717       }
1718   }
1719
1720
1721   if (attach)
1722     {
1723       if (close_mapi_attachment (&attach, sink))
1724         goto failure;
1725     }
1726
1727   result = 0;  /* Everything is fine, fall through the cleanup now.  */
1728
1729  failure:
1730   engine_cancel (filter);
1731   if (attach)
1732     cancel_mapi_attachment (&attach, sink);
1733   xfree (body);
1734   if (result)
1735     mapi_release_attach_table (att_table);
1736   else
1737     *r_att_table = att_table;
1738   xfree (sigbuffer.buf);
1739   xfree (my_sender);
1740   return result;
1741 }
1742
1743
1744 /* Sign the MESSAGE using PROTOCOL.  If PROTOCOL is PROTOCOL_UNKNOWN
1745    the engine decides what protocol to use.  On return MESSAGE is
1746    modified so that sending it will result in a properly MOSS (that is
1747    PGP or S/MIME) signed message.  On failure the function tries to
1748    keep the original message intact but there is no 100% guarantee for
1749    it. */
1750 int
1751 mime_sign (LPMESSAGE message, HWND hwnd, protocol_t protocol,
1752            const char *sender, Mail *mail)
1753 {
1754   int result = -1;
1755   mapi_attach_item_t *att_table;
1756
1757   result = do_mime_sign (message, hwnd, protocol, &att_table, 0,
1758                          engine_new_session_number (), sender, mail);
1759   if (!result)
1760     {
1761       if (!finalize_message (message, att_table, protocol, 0))
1762         result = 0;
1763     }
1764
1765   mapi_release_attach_table (att_table);
1766   return result;
1767 }
1768
1769
1770 \f
1771 /* Sink write method used by mime_encrypt.  */
1772 int
1773 sink_encryption_write (sink_t encsink, const void *data, size_t datalen)
1774 {
1775   engine_filter_t filter = (engine_filter_t) encsink->cb_data;
1776
1777   if (!filter)
1778     {
1779       log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
1780       return -1;
1781     }
1782
1783   return engine_filter (filter, data, datalen);
1784 }
1785
1786
1787 #if 0 /* Not used.  */
1788 /* Sink write method used by mime_encrypt for writing Base64.  */
1789 static int
1790 sink_encryption_write_b64 (sink_t encsink, const void *data, size_t datalen)
1791 {
1792   engine_filter_t filter = encsink->cb_data;
1793   int rc;
1794   const unsigned char *p;
1795   unsigned char inbuf[4];
1796   int idx, quads;
1797   char outbuf[6];
1798   size_t outbuflen;
1799
1800   if (!filter)
1801     {
1802       log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
1803       return -1;
1804     }
1805
1806   idx = encsink->b64.idx;
1807   assert (idx < 4);
1808   memcpy (inbuf, encsink->b64.inbuf, idx);
1809   quads = encsink->b64.quads;
1810
1811   if (!data)  /* Flush. */
1812     {
1813       outbuflen = 0;
1814       if (idx)
1815         {
1816           outbuf[0] = bintoasc[(*inbuf>>2)&077];
1817           if (idx == 1)
1818             {
1819               outbuf[1] = bintoasc[((*inbuf<<4)&060)&077];
1820               outbuf[2] = '=';
1821               outbuf[3] = '=';
1822             }
1823           else
1824             {
1825               outbuf[1] = bintoasc[(((*inbuf<<4)&060)|
1826                                     ((inbuf[1]>>4)&017))&077];
1827               outbuf[2] = bintoasc[((inbuf[1]<<2)&074)&077];
1828               outbuf[3] = '=';
1829             }
1830           outbuflen = 4;
1831           quads++;
1832         }
1833
1834       if (quads)
1835         {
1836           outbuf[outbuflen++] = '\r';
1837           outbuf[outbuflen++] = '\n';
1838         }
1839
1840       if (outbuflen && (rc = engine_filter (filter, outbuf, outbuflen)))
1841         return rc;
1842       /* Send the flush command to the filter.  */
1843       if ((rc = engine_filter (filter, data, datalen)))
1844         return rc;
1845     }
1846   else
1847     {
1848       for (p = data; datalen; p++, datalen--)
1849         {
1850           inbuf[idx++] = *p;
1851           if (idx > 2)
1852             {
1853               idx = 0;
1854               outbuf[0] = bintoasc[(*inbuf>>2)&077];
1855               outbuf[1] = bintoasc[(((*inbuf<<4)&060)
1856                                     |((inbuf[1] >> 4)&017))&077];
1857               outbuf[2] = bintoasc[(((inbuf[1]<<2)&074)
1858                                     |((inbuf[2]>>6)&03))&077];
1859               outbuf[3] = bintoasc[inbuf[2]&077];
1860               outbuflen = 4;
1861               if (++quads >= (64/4))
1862                 {
1863                   quads = 0;
1864                   outbuf[4] = '\r';
1865                   outbuf[5] = '\n';
1866                   outbuflen += 2;
1867                 }
1868               if ((rc = engine_filter (filter, outbuf, outbuflen)))
1869                 return rc;
1870             }
1871         }
1872     }
1873
1874   encsink->b64.idx = idx;
1875   memcpy (encsink->b64.inbuf, inbuf, idx);
1876   encsink->b64.quads = quads;
1877
1878   return 0;
1879 }
1880 #endif /*Not used.*/
1881
1882
1883 /* Helper from mime_encrypt.  BOUNDARY is a buffer of at least
1884    BOUNDARYSIZE+1 bytes which will be set on return from that
1885    function.  */
1886 int
1887 create_top_encryption_header (sink_t sink, protocol_t protocol, char *boundary,
1888                               bool is_inline)
1889 {
1890   int rc;
1891
1892   if (is_inline)
1893     {
1894       *boundary = 0;
1895       rc = 0;
1896       /* This would be nice and worked for Google Sync but it failed
1897          for Microsoft Exchange Online *sigh* so we put the body
1898          instead into the oom body property and stick with IPM Note.
1899       rc = write_multistring (sink,
1900                               "MIME-Version: 1.0\r\n"
1901                               "Content-Type: text/plain;\r\n"
1902                               "\tcharset=\"iso-8859-1\"\r\n"
1903                               "Content-Transfer-Encoding: 7BIT\r\n"
1904                               "\r\n",
1905                               NULL);
1906      */
1907     }
1908   else if (protocol == PROTOCOL_SMIME)
1909     {
1910       *boundary = 0;
1911       rc = write_multistring (sink,
1912                               "MIME-Version: 1.0\r\n"
1913                               "Content-Type: application/pkcs7-mime;\r\n"
1914                               "\tsmime-type=enveloped-data;\r\n"
1915                               "\tname=\"smime.p7m\"\r\n"
1916                               "Content-Transfer-Encoding: base64\r\n"
1917                               "\r\n",
1918                               NULL);
1919     }
1920   else
1921     {
1922       generate_boundary (boundary);
1923       rc = write_multistring (sink,
1924                               "MIME-Version: 1.0\r\n"
1925                               "Content-Type: multipart/encrypted;\r\n"
1926                               "\tprotocol=\"application/pgp-encrypted\";\r\n",
1927                               "\tboundary=\"", boundary, "\"\r\n",
1928                               NULL);
1929       if (rc)
1930         return rc;
1931
1932       /* Write the PGP/MIME encrypted part.  */
1933       rc = write_boundary (sink, boundary, 0);
1934       if (rc)
1935         return rc;
1936       rc = write_multistring (sink,
1937                               "Content-Type: application/pgp-encrypted\r\n"
1938                               "\r\n"
1939                               "Version: 1\r\n", NULL);
1940       if (rc)
1941         return rc;
1942
1943       /* And start the second part.  */
1944       rc = write_boundary (sink, boundary, 0);
1945       if (rc)
1946         return rc;
1947       rc = write_multistring (sink,
1948                               "Content-Type: application/octet-stream\r\n"
1949                               "\r\n", NULL);
1950      }
1951
1952   return rc;
1953 }
1954
1955
1956 /* Encrypt the MESSAGE.  */
1957 int
1958 mime_encrypt (LPMESSAGE message, HWND hwnd,
1959               protocol_t protocol, char **recipients,
1960               const char *sender, Mail* mail)
1961 {
1962   int result = -1;
1963   int rc;
1964   LPATTACH attach = nullptr;
1965   struct sink_s sinkmem;
1966   sink_t sink = &sinkmem;
1967   struct sink_s encsinkmem;
1968   sink_t encsink = &encsinkmem;
1969   char boundary[BOUNDARYSIZE+1];
1970   mapi_attach_item_t *att_table = NULL;
1971   char *body = NULL;
1972   int n_att_usable;
1973   engine_filter_t filter = NULL;
1974   char *my_sender = NULL;
1975   bool is_inline = mail && mail->do_pgp_inline ();
1976
1977   memset (sink, 0, sizeof *sink);
1978   memset (encsink, 0, sizeof *encsink);
1979
1980   /* Get the attachment info and the body.  We need to do this before
1981      creating the engine's filter because sending the cancel to
1982      the engine with nothing for the engine to process.  Will result
1983      in an error. This is actually a bug in our engine code but
1984      we better avoid triggering this bug because the engine
1985      sometimes hangs.  Fixme: Needs a proper fix. */
1986
1987
1988   /* Take the Body from the mail if possible. This is a fix for
1989      GnuPG-Bug-ID: T3614 because the body is not always properly
1990      updated in MAPI when sending. */
1991   if (mail)
1992     {
1993       body = mail->take_cached_plain_body ();
1994     }
1995   if (!body)
1996     {
1997       body = mapi_get_body (message, NULL);
1998     }
1999   if (body && !*body)
2000     {
2001       xfree (body);
2002       body = NULL;
2003     }
2004   att_table = mapi_create_attach_table (message, 0);
2005   n_att_usable = count_usable_attachments (att_table);
2006   if (!n_att_usable && !body)
2007     {
2008       log_debug ("%s:%s: can't encrypt an empty message\n", SRCNAME, __func__);
2009       result = gpg_error (GPG_ERR_NO_DATA);
2010       goto failure;
2011     }
2012
2013   if (n_att_usable && is_inline)
2014     {
2015       log_debug ("%s:%s: PGP Inline not supported for attachments. Using PGP MIME",
2016                  SRCNAME, __func__);
2017       is_inline = false;
2018     }
2019
2020   if (!is_inline || !mail)
2021     {
2022       attach = create_mapi_attachment (message, sink);
2023       if (!attach)
2024         return -1;
2025     }
2026   else
2027     {
2028       sink->cb_data = mail;
2029       sink->writefnc = sink_string_write;
2030     }
2031
2032   /* Prepare the encryption.  We do this early as it is quite common
2033      that some recipient keys are not available and thus the
2034      encryption will fail early. */
2035   if (engine_create_filter (&filter, write_buffer_for_cb, sink))
2036     goto failure;
2037
2038   engine_set_session_number (filter, engine_new_session_number ());
2039   {
2040     char *tmp = mapi_get_subject (message);
2041     engine_set_session_title (filter, tmp);
2042     xfree (tmp);
2043   }
2044
2045   if (sender)
2046     my_sender = xstrdup (sender);
2047   else
2048     my_sender = mapi_get_sender (message);
2049   if (engine_encrypt_prepare (filter, hwnd, protocol, 0,
2050                               my_sender, recipients, &protocol))
2051     goto failure;
2052   if (engine_encrypt_start (filter, 0))
2053     goto failure;
2054
2055   protocol = check_protocol (protocol);
2056   if (protocol == PROTOCOL_UNKNOWN)
2057     goto failure;
2058
2059   if (protocol != PROTOCOL_OPENPGP)
2060     {
2061       log_debug ("%s:%s: Inline not supported for S/MIME. Using MIME",
2062                  SRCNAME, __func__);
2063       is_inline = false;
2064     }
2065
2066   /* Write the top header.  */
2067   rc = create_top_encryption_header (sink, protocol, boundary,
2068                                      is_inline);
2069   if (rc)
2070     goto failure;
2071
2072   /* Create a new sink for encrypting the following stuff.  */
2073   encsink->cb_data = filter;
2074   encsink->writefnc = sink_encryption_write;
2075
2076   /* Add the plaintext */
2077   if (is_inline && body)
2078     {
2079       if ((rc = write_multistring (encsink, body, NULL)))
2080         {
2081           log_error ("%s:%s: Adding the body failed.",
2082                      SRCNAME, __func__);
2083           goto failure;
2084         }
2085     }
2086   else if (add_body_and_attachments (encsink, message, att_table, mail,
2087                                      body, n_att_usable))
2088     {
2089       goto failure;
2090     }
2091
2092   xfree (body);
2093   body = NULL;
2094
2095   /* Flush the encryption sink and wait for the encryption to get
2096      ready.  */
2097   if ((rc = write_buffer (encsink, NULL, 0)))
2098     goto failure;
2099   if ((rc = engine_wait (filter)))
2100     goto failure;
2101   filter = NULL; /* Not valid anymore.  */
2102   encsink->cb_data = NULL; /* Not needed anymore.  */
2103
2104     if (!sink->enc_counter)
2105     {
2106       log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
2107       goto failure;
2108     }
2109
2110   /* Write the final boundary (for OpenPGP) and finish the attachment.  */
2111   if (*boundary && (rc = write_boundary (sink, boundary, 1)))
2112     goto failure;
2113
2114   if (attach && close_mapi_attachment (&attach, sink))
2115     goto failure;
2116
2117   if (finalize_message (message, att_table, protocol, 1, is_inline))
2118     goto failure;
2119
2120   result = 0;  /* Everything is fine, fall through the cleanup now.  */
2121
2122  failure:
2123   engine_cancel (filter);
2124   if (attach)
2125     {
2126       cancel_mapi_attachment (&attach, sink);
2127     }
2128   xfree (body);
2129   mapi_release_attach_table (att_table);
2130   xfree (my_sender);
2131   return result;
2132 }
2133
2134
2135
2136 \f
2137 /* Sign and Encrypt the MESSAGE.  */
2138 int
2139 mime_sign_encrypt (LPMESSAGE message, HWND hwnd,
2140                    protocol_t protocol, char **recipients,
2141                    const char *sender, Mail *mail)
2142 {
2143   int result = -1;
2144   int rc = 0;
2145   HRESULT hr;
2146   LPATTACH attach;
2147   LPSTREAM tmpstream = NULL;
2148   struct sink_s sinkmem;
2149   sink_t sink = &sinkmem;
2150   struct sink_s encsinkmem;
2151   sink_t encsink = &encsinkmem;
2152   struct sink_s tmpsinkmem;
2153   sink_t tmpsink = &tmpsinkmem;
2154   char boundary[BOUNDARYSIZE+1];
2155   mapi_attach_item_t *att_table = NULL;
2156   engine_filter_t filter = NULL;
2157   unsigned int session_number;
2158   char *my_sender = NULL;
2159
2160   memset (sink, 0, sizeof *sink);
2161   memset (encsink, 0, sizeof *encsink);
2162   memset (tmpsink, 0, sizeof *tmpsink);
2163
2164   attach = create_mapi_attachment (message, sink);
2165   if (!attach)
2166     return -1;
2167
2168   /* First check that we are not trying to process an empty message
2169      which might lock up our engine.  Unfortunately we need to
2170      duplicate the code we use in do_mime_sign here.  FIXME: The
2171      engine should be fixed instead of using such a workaround.  */
2172   {
2173     char *body;
2174
2175     body = mapi_get_body (message, NULL);
2176     if (body && !*body)
2177     {
2178       xfree (body);
2179       body = NULL;
2180     }
2181     att_table = mapi_create_attach_table (message, 0);
2182     if (!count_usable_attachments (att_table) && !body)
2183       result = gpg_error (GPG_ERR_NO_DATA);
2184     xfree (body);
2185     if (att_table)
2186       {
2187         mapi_release_attach_table (att_table);
2188         att_table = NULL;
2189       }
2190     if (gpg_err_code (result) == GPG_ERR_NO_DATA)
2191       {
2192         log_debug ("%s:%s: can't sign+encrypt an empty message\n",
2193                    SRCNAME, __func__);
2194         goto failure;
2195       }
2196   }
2197
2198
2199   /* Create a temporary sink to construct the signed data.  */
2200   hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
2201                          (SOF_UNIQUEFILENAME | STGM_DELETEONRELEASE
2202                           | STGM_CREATE | STGM_READWRITE),
2203                          NULL, GpgOLStr("GPG"), &tmpstream);
2204   if (FAILED (hr))
2205     {
2206       log_error ("%s:%s: can't create temp file: hr=%#lx\n",
2207                  SRCNAME, __func__, hr);
2208       rc = -1;
2209       goto failure;
2210     }
2211   tmpsink->cb_data = tmpstream;
2212   tmpsink->writefnc = sink_std_write;
2213
2214
2215   /* Prepare the encryption.  We do this early as it is quite common
2216      that some recipients are not available and thus the encryption
2217      will fail early.  This is also required to allow the UIserver to
2218      figure out the protocol to use if we have not forced one.  */
2219   if (engine_create_filter (&filter, write_buffer_for_cb, sink))
2220     goto failure;
2221
2222   session_number = engine_new_session_number ();
2223   engine_set_session_number (filter, session_number);
2224   {
2225     char *tmp = mapi_get_subject (message);
2226     engine_set_session_title (filter, tmp);
2227     xfree (tmp);
2228   }
2229
2230   if (sender)
2231     my_sender = xstrdup (sender);
2232   else
2233     my_sender = mapi_get_sender (message);
2234   if ((rc=engine_encrypt_prepare (filter, hwnd,
2235                                   protocol, ENGINE_FLAG_SIGN_FOLLOWS,
2236                                   my_sender, recipients, &protocol)))
2237     goto failure;
2238
2239   protocol = check_protocol (protocol);
2240   if (protocol == PROTOCOL_UNKNOWN)
2241     goto failure;
2242
2243   /* Now sign the message.  This creates another attachment with the
2244      complete MIME object of the signed message.  We can't do the
2245      encryption in streaming mode while running the encryption because
2246      we need to fix up that ugly micalg parameter after having created
2247      the signature.  Note that the protocol to use is taken from the
2248      encryption operation. */
2249   if (do_mime_sign (message, hwnd, protocol, &att_table, tmpsink,
2250                     session_number, sender, mail))
2251     goto failure;
2252
2253   /* Now send the actual ENCRYPT command.  This split up between
2254      prepare and start is necessary to help with the implementarion of
2255      the UI-server.  If we would send the ENCRYPT command immediately
2256      the UI-server might block while reading from the input stream
2257      because we are first going to do a sign operation which in trun
2258      needs the attention of the UI server.  A more robust but
2259      complicated approach to the UI-server would be to delay the
2260      reading (and thus the start of the underlying encrypt operation)
2261      until the first byte has been received. */
2262   if ((rc=engine_encrypt_start (filter, 0)))
2263     goto failure;
2264
2265   /* Write the top header.  */
2266   rc = create_top_encryption_header (sink, protocol, boundary);
2267   if (rc)
2268     goto failure;
2269
2270   /* Create a new sink for encrypting the temporary attachment with
2271      the signed message.  */
2272   encsink->cb_data = filter;
2273   encsink->writefnc = sink_encryption_write;
2274
2275   /* Copy the temporary stream to the encryption sink.  */
2276   {
2277     LARGE_INTEGER off;
2278     ULONG nread;
2279     char buffer[4096];
2280
2281     off.QuadPart = 0;
2282     hr = tmpstream->Seek (off, STREAM_SEEK_SET, NULL);
2283     if (hr)
2284       {
2285         log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
2286                    SRCNAME, __func__, hr);
2287         rc = gpg_error (GPG_ERR_EIO);
2288         goto failure;
2289       }
2290
2291     for (;;)
2292       {
2293         hr = tmpstream->Read (buffer, sizeof buffer, &nread);
2294         if (hr)
2295           {
2296             log_error ("%s:%s: IStream::Read failed: hr=%#lx",
2297                        SRCNAME, __func__, hr);
2298             rc = gpg_error (GPG_ERR_EIO);
2299             goto failure;
2300           }
2301         if (!nread)
2302           break;  /* EOF */
2303         rc = write_buffer (encsink, buffer, nread);
2304         if (rc)
2305           {
2306             log_error ("%s:%s: writing tmpstream to encsink failed: %s",
2307                        SRCNAME, __func__, gpg_strerror (rc));
2308             goto failure;
2309           }
2310       }
2311   }
2312
2313
2314   /* Flush the encryption sink and wait for the encryption to get
2315      ready.  */
2316   if ((rc = write_buffer (encsink, NULL, 0)))
2317     goto failure;
2318   if ((rc = engine_wait (filter)))
2319     goto failure;
2320   filter = NULL; /* Not valid anymore.  */
2321   encsink->cb_data = NULL; /* Not needed anymore.  */
2322
2323   if (!sink->enc_counter)
2324     {
2325       log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
2326       goto failure;
2327     }
2328
2329   /* Write the final boundary (for OpenPGP) and finish the attachment.  */
2330   if (*boundary && (rc = write_boundary (sink, boundary, 1)))
2331     goto failure;
2332
2333   if (close_mapi_attachment (&attach, sink))
2334     goto failure;
2335
2336   if (finalize_message (message, att_table, protocol, 1))
2337     goto failure;
2338
2339   result = 0;  /* Everything is fine, fall through the cleanup now.  */
2340
2341  failure:
2342   if (result)
2343     log_debug ("%s:%s: failed rc=%d (%s) <%s>", SRCNAME, __func__, rc,
2344                gpg_strerror (rc), gpg_strsource (rc));
2345   engine_cancel (filter);
2346   gpgol_release (tmpstream);
2347   mapi_release_attach_table (att_table);
2348   xfree (my_sender);
2349   return result;
2350 }
2351
2352 int
2353 restore_msg_from_moss (LPMESSAGE message, LPDISPATCH moss_att,
2354                        msgtype_t type, char *msgcls)
2355 {
2356   struct sink_s sinkmem;
2357   sink_t sink = &sinkmem;
2358   char *orig = NULL;
2359   int err = -1;
2360   char boundary[BOUNDARYSIZE+1];
2361
2362   (void)msgcls;
2363
2364   LPATTACH new_attach = create_mapi_attachment (message,
2365                                                 sink);
2366   log_debug ("Restore message from moss called.");
2367   if (!new_attach)
2368     {
2369       log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
2370       goto done;
2371     }
2372   // TODO MORE
2373   if (type == MSGTYPE_SMIME)
2374     {
2375       create_top_encryption_header (sink, PROTOCOL_SMIME, boundary);
2376     }
2377   else if (type == MSGTYPE_GPGOL_MULTIPART_ENCRYPTED)
2378     {
2379       create_top_encryption_header (sink, PROTOCOL_OPENPGP, boundary);
2380     }
2381   else
2382     {
2383       log_error ("%s:%s: Unsupported messagetype: %i",
2384                  SRCNAME, __func__, type);
2385       goto done;
2386     }
2387
2388   orig = get_pa_string (moss_att, PR_ATTACH_DATA_BIN_DASL);
2389
2390   if (!orig)
2391     {
2392       log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
2393       goto done;
2394     }
2395
2396   if (write_string (sink, orig))
2397     {
2398       log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
2399       goto done;
2400     }
2401
2402   if (*boundary && write_boundary (sink, boundary, 1))
2403     {
2404       log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
2405       goto done;
2406     }
2407
2408   if (close_mapi_attachment (&new_attach, sink))
2409     {
2410       log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
2411       goto done;
2412     }
2413
2414   /* Set a special property so that we are later able to identify
2415      messages signed or encrypted by us.  */
2416   if (mapi_set_sig_status (message, "@"))
2417     {
2418       log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
2419       goto done;
2420     }
2421
2422   err = 0;
2423 done:
2424   xfree (orig);
2425   return err;
2426 }