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