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