Add minimalistic protected-headers support
[gpgol.git] / src / mimemaker.cpp
index a7e9959..be51ae6 100644 (file)
 #include "mymapitags.h"
 
 #include "common.h"
-#include "engine.h"
 #include "mapihelp.h"
 #include "mimemaker.h"
 #include "oomhelp.h"
-#include "gpgolstr.h"
 #include "mail.h"
 
 static const unsigned char oid_mimetag[] =
@@ -99,7 +97,7 @@ int
 sink_string_write (sink_t sink, const void *data, size_t datalen)
 {
   Mail *mail = static_cast<Mail *>(sink->cb_data);
-  mail->append_to_inline_body (std::string((char*)data, datalen));
+  mail->appendToInlineBody (std::string((char*)data, datalen));
   return 0;
 }
 
@@ -127,26 +125,6 @@ sink_file_write (sink_t sink, const void *data, size_t datalen)
 }
 
 
-/* Make sure that PROTOCOL is usable or return a suitable protocol.
-   On error PROTOCOL_UNKNOWN is returned.  */
-static protocol_t
-check_protocol (protocol_t protocol)
-{
-  switch (protocol)
-    {
-    case PROTOCOL_UNKNOWN:
-      return PROTOCOL_UNKNOWN;
-    case PROTOCOL_OPENPGP:
-    case PROTOCOL_SMIME:
-      return protocol;
-    }
-
-  log_error ("%s:%s: BUG", SRCNAME, __func__);
-  return PROTOCOL_UNKNOWN;
-}
-
-
-
 /* Create a new MAPI attchment for MESSAGE which will be used to
    prepare the MIME message.  On sucess the stream to write the data
    to is stored at STREAM and the attachment object itself is
@@ -165,6 +143,7 @@ create_mapi_attachment (LPMESSAGE message, sink_t sink,
   sink->cb_data = NULL;
   sink->writefnc = NULL;
   hr = message->CreateAttach(NULL, 0, &pos, &att);
+  memdbg_addRef (att);
   if (hr)
     {
       log_error ("%s:%s: can't create attachment: hr=%#lx\n",
@@ -197,7 +176,7 @@ create_mapi_attachment (LPMESSAGE message, sink_t sink,
 
   /* We better insert a short filename. */
   prop.ulPropTag = PR_ATTACH_FILENAME_A;
-  prop.Value.lpszA = strdup (MIMEATTACHFILENAME);
+  prop.Value.lpszA = xstrdup (MIMEATTACHFILENAME);
   hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
   xfree (prop.Value.lpszA);
   if (hr)
@@ -218,8 +197,8 @@ create_mapi_attachment (LPMESSAGE message, sink_t sink,
   if (!hr)
     {
       prop.ulPropTag = PR_ATTACH_MIME_TAG_A;
-      prop.Value.lpszA = overrideMimeTag ? strdup (overrideMimeTag) :
-                         strdup ("multipart/signed");
+      prop.Value.lpszA = overrideMimeTag ? xstrdup (overrideMimeTag) :
+                         xstrdup ("multipart/signed");
       if (overrideMimeTag)
         {
           log_debug ("%s:%s: using override mimetag: %s\n",
@@ -236,7 +215,7 @@ create_mapi_attachment (LPMESSAGE message, sink_t sink,
     }
 
   punk = NULL;
-  hr = att->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, 0,
+  hr = gpgol_openProperty (att, PR_ATTACH_DATA_BIN, &IID_IStream, 0,
                          (MAPI_CREATE|MAPI_MODIFY), &punk);
   if (FAILED (hr))
     {
@@ -603,6 +582,11 @@ infer_content_type (const char * /*data*/, size_t /*datalen*/,
       { 1, "deb",   "application/x-debian-package" },
       { 1, "dl",    "video/dl" },
       { 1, "doc",   "application/msword" },
+      { 1, "docx",  "application/vnd.openxmlformats-officedocument.wordprocessingml.document" },
+      { 1, "dot",   "application/msword" },
+      { 1, "dotx",  "application/vnd.openxmlformats-officedocument.wordprocessingml.template" },
+      { 1, "docm",  "application/application/vnd.ms-word.document.macroEnabled.12" },
+      { 1, "dotm",  "application/vnd.ms-word.template.macroEnabled.12" },
       { 1, "dv",    "video/dv" },
       { 1, "dvi",   "application/x-dvi" },
       { 1, "eml",   "message/rfc822" },
@@ -661,6 +645,15 @@ infer_content_type (const char * /*data*/, size_t /*datalen*/,
       { 1, "png",   "image/png" },
       { 1, "pps",   "application/vnd.ms-powerpoint" },
       { 1, "ppt",   "application/vnd.ms-powerpoint" },
+      { 1, "pot",   "application/vnd.ms-powerpoint" },
+      { 1, "ppa",   "application/vnd.ms-powerpoint" },
+      { 1, "pptx",  "application/vnd.openxmlformats-officedocument.presentationml.presentation" },
+      { 1, "potx",  "application/vnd.openxmlformats-officedocument.presentationml.template" },
+      { 1, "ppsx",  "application/vnd.openxmlformats-officedocument.presentationml.slideshow" },
+      { 1, "ppam",  "application/vnd.ms-powerpoint.addin.macroEnabled.12" },
+      { 1, "pptm",  "application/vnd.ms-powerpoint.presentation.macroEnabled.12" },
+      { 1, "potm",  "application/vnd.ms-powerpoint.template.macroEnabled.12" },
+      { 1, "ppsm",  "application/vnd.ms-powerpoint.slideshow.macroEnabled.12" },
       { 1, "prf",   "application/pics-rules" },
       { 1, "ps",    "application/postscript" },
       { 1, "qt",    "video/quicktime" },
@@ -689,7 +682,14 @@ infer_content_type (const char * /*data*/, size_t /*datalen*/,
       { 0, "xhtml", "application/xhtml+xml" },
       { 1, "xlb",   "application/vnd.ms-excel" },
       { 1, "xls",   "application/vnd.ms-excel" },
+      { 1, "xlsx",  "application/vnd.ms-excel" },
       { 1, "xlt",   "application/vnd.ms-excel" },
+      { 1, "xla",   "application/vnd.ms-excel" },
+      { 1, "xltx",  "application/vnd.openxmlformats-officedocument.spreadsheetml.template" },
+      { 1, "xlsm",  "application/vnd.ms-excel.sheet.macroEnabled.12" },
+      { 1, "xltm",  "application/vnd.ms-excel.template.macroEnabled.12" },
+      { 1, "xlam",  "application/vnd.ms-excel.addin.macroEnabled.12" },
+      { 1, "xlsb",  "application/application/vnd.ms-excel.sheet.binary.macroEnabled.12" },
       { 0, "xml",   "application/xml" },
       { 0, "xsl",   "application/xml" },
       { 0, "xul",   "application/vnd.mozilla.xul+xml" },
@@ -701,10 +701,19 @@ infer_content_type (const char * /*data*/, size_t /*datalen*/,
 
   *force_b64 = 0;
   if (filename)
-    suffix = strrchr (filename, '.');
+    {
+      const char *dot = strrchr (filename, '.');
+
+      if (dot)
+        {
+          suffix = dot;
+        }
+    }
 
-  if (!suffix.empty())
+  /* Check for at least one char after the dot. */
+  if (suffix.size() > 1)
     {
+      /* Erase the dot */
       suffix.erase(0, 1);
       std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower);
       for (i=0; suffix_table[i].suffix; i++)
@@ -892,7 +901,7 @@ write_part (sink_t sink, const char *data, size_t datalen,
 
   log_debug ("Writing part of length %d%s filename=`%s'\n",
              (int)datalen, is_mapibody? " (body)":"",
-             filename?filename:"[none]");
+             filename ? anonstr (filename) : "[none]");
 
   ct = infer_content_type (data, datalen, filename, is_mapibody, &use_b64);
   use_qp = 0;
@@ -1016,7 +1025,7 @@ write_attachments (sink_t sink,
             {
               continue;
             }
-          buffer = mapi_get_attach (message, 0, table+idx, &buflen);
+          buffer = mapi_get_attach (message, table+idx, &buflen);
           if (!buffer)
             log_debug ("Attachment at index %d not found\n", idx);
           else
@@ -1039,7 +1048,7 @@ write_attachments (sink_t sink,
 static int
 is_related (Mail *mail, mapi_attach_item_t *table)
 {
-  if (!mail || !mail->is_html_alternative () || !table)
+  if (!mail || !mail->isHTMLAlternative () || !table)
     {
       return 0;
     }
@@ -1162,11 +1171,11 @@ finalize_message (LPMESSAGE message, mapi_attach_item_t *att_table,
   prop.ulPropTag = PR_MESSAGE_CLASS_A;
   if (encrypt)
     {
-      prop.Value.lpszA = strdup ("IPM.Note.InfoPathForm.GpgOL.SMIME.MultipartSigned");
+      prop.Value.lpszA = xstrdup ("IPM.Note.InfoPathForm.GpgOL.SMIME.MultipartSigned");
     }
   else
     {
-      prop.Value.lpszA = strdup ("IPM.Note.InfoPathForm.GpgOLS.SMIME.MultipartSigned");
+      prop.Value.lpszA = xstrdup ("IPM.Note.InfoPathForm.GpgOLS.SMIME.MultipartSigned");
     }
 
   if (!is_inline)
@@ -1184,15 +1193,6 @@ finalize_message (LPMESSAGE message, mapi_attach_item_t *att_table,
       return -1;
     }
 
-  /* Set a special property so that we are later able to identify
-     messages signed or encrypted by us.  */
-  if (mapi_set_sig_status (message, "@"))
-    {
-      log_error ("%s:%s: error setting sigstatus",
-                 SRCNAME, __func__);
-      return -1;
-    }
-
   /* We also need to set the message class into our custom
      property. This override is at least required for encrypted
      messages.  */
@@ -1260,48 +1260,6 @@ finalize_message (LPMESSAGE message, mapi_attach_item_t *att_table,
 }
 
 
-\f
-/* Sink write method used by mime_sign.  We write the data to the
-   filter and also to the EXTRASINK but we don't pass a flush request
-   to EXTRASINK. */
-static int
-sink_hashing_write (sink_t hashsink, const void *data, size_t datalen)
-{
-  int rc;
-  engine_filter_t filter = (engine_filter_t) hashsink->cb_data;
-
-  if (!filter || !hashsink->extrasink)
-    {
-      log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
-      return -1;
-    }
-
-  rc = engine_filter (filter, data, datalen);
-  if (!rc && data && datalen)
-    write_buffer (hashsink->extrasink, data, datalen);
-  return rc;
-}
-
-
-/* This function is called by the filter to collect the output which
-   is a detached signature.  */
-static int
-collect_signature (void *opaque, const void *data, size_t datalen)
-{
-  struct databuf_s *db = (databuf_s *)opaque;
-
-  if (db->len + datalen >= db->size)
-    {
-      db->size += datalen + 1024;
-      db->buf = (char*) xrealloc (db->buf, db->size);
-    }
-  memcpy (db->buf + db->len, data, datalen);
-  db->len += datalen;
-
-  return datalen;
-}
-
-
 /* Helper to create the signing header.  This includes enough space
    for later fixup of the micalg parameter.  The MIME version is only
    written if FIRST is set.  */
@@ -1342,7 +1300,7 @@ add_body (Mail *mail, const char *boundary, sink_t sink,
   bool is_alternative = false;
   if (mail)
     {
-      is_alternative = mail->is_html_alternative ();
+      is_alternative = mail->isHTMLAlternative ();
     }
 
   int rc = 0;
@@ -1389,7 +1347,7 @@ add_body (Mail *mail, const char *boundary, sink_t sink,
   /* Now the html body. It is somehow not accessible through PR_HTML,
      OutlookSpy also shows MAPI Unsupported (but shows the data) strange.
      We just cache it. Memory is cheap :-) */
-  char *html_body = mail->take_cached_html_body();
+  char *html_body = mail->takeCachedHTMLBody ();
   if (!html_body)
     {
       log_error ("%s:%s: BUG: Body but no html body in alternative mail?",
@@ -1502,397 +1460,13 @@ add_body_and_attachments (sink_t sink, LPMESSAGE message,
   return rc;
 }
 
-/* Main body of mime_sign without the the code to delete the original
-   attachments.  On success the function returns the current
-   attachment table at R_ATT_TABLE or sets this to NULL on error.  If
-   TMPSINK is set no attachment will be created but the output
-   written to that sink.  */
-static int
-do_mime_sign (LPMESSAGE message, HWND hwnd, protocol_t protocol,
-              mapi_attach_item_t **r_att_table, sink_t tmpsink,
-              unsigned int session_number, const char *sender,
-              Mail *mail)
-{
-  int result = -1;
-  int rc;
-  LPATTACH attach = NULL;
-  struct sink_s sinkmem;
-  sink_t sink = &sinkmem;
-  struct sink_s hashsinkmem;
-  sink_t hashsink = &hashsinkmem;
-  char boundary[BOUNDARYSIZE+1];
-  mapi_attach_item_t *att_table = NULL;
-  char *body = NULL;
-  int n_att_usable;
-  char top_header[BOUNDARYSIZE+200];
-  engine_filter_t filter = NULL;
-  struct databuf_s sigbuffer;
-  char *my_sender = NULL;
-
-  *r_att_table = NULL;
-
-  memset (sink, 0, sizeof *sink);
-  memset (hashsink, 0, sizeof *hashsink);
-  memset (&sigbuffer, 0, sizeof sigbuffer);
-
-  if (tmpsink)
-    {
-      attach = NULL;
-      sink = tmpsink;
-    }
-  else
-    {
-      attach = create_mapi_attachment (message, sink);
-      if (!attach)
-        return -1;
-    }
-
-  /* Take the Body from the mail if possible. This is a fix for
-     GnuPG-Bug-ID: T3614 because the body is not always properly
-     updated in MAPI when sending. */
-  if (mail)
-    {
-      body = mail->take_cached_plain_body ();
-    }
-  /* Get the attachment info and the body.  */
-  if (!body)
-    {
-      body = mapi_get_body (message, NULL);
-    }
-  if (body && !*body)
-    {
-      xfree (body);
-      body = NULL;
-    }
-  att_table = mapi_create_attach_table (message, 0);
-  n_att_usable = count_usable_attachments (att_table);
-  if (!n_att_usable && !body)
-    {
-      log_debug ("%s:%s: can't sign an empty message\n", SRCNAME, __func__);
-      result = gpg_error (GPG_ERR_NO_DATA);
-      goto failure;
-    }
-
-  /* Prepare the signing. */
-  if (engine_create_filter (&filter, collect_signature, &sigbuffer))
-    goto failure;
-
-  if (session_number)
-    {
-      engine_set_session_number (filter, session_number);
-      {
-        char *tmp = mapi_get_subject (message);
-        engine_set_session_title (filter, tmp);
-        xfree (tmp);
-      }
-    }
-
-  if (sender)
-    my_sender = xstrdup (sender);
-  else
-    my_sender = mapi_get_sender (message);
-  if (engine_sign_start (filter, hwnd, protocol, my_sender, &protocol))
-    goto failure;
-
-  protocol = check_protocol (protocol);
-  if (protocol == PROTOCOL_UNKNOWN)
-    {
-      log_error ("%s:%s: no protocol selected", SRCNAME, __func__);
-      goto failure;
-    }
-
-
-  /* Write the top header.  */
-  generate_boundary (boundary);
-  create_top_signing_header (top_header, sizeof top_header,
-                             protocol, 1, boundary, "xxx");
-  if ((rc = write_string (sink, top_header)))
-    goto failure;
-
-  /* Write the boundary so that it is not included in the hashing.  */
-  if ((rc = write_boundary (sink, boundary, 0)))
-    goto failure;
-
-  /* Create a new sink for hashing and write/hash our content.  */
-  hashsink->cb_data = filter;
-  hashsink->extrasink = sink;
-  hashsink->writefnc = sink_hashing_write;
-
-  /* Add the plaintext */
-  if (add_body_and_attachments (hashsink, message, att_table, mail,
-                                body, n_att_usable))
-    goto failure;
-
-  xfree (body);
-  body = NULL;
-
-  /* Here we are ready with the hashing.  Flush the filter and wait
-     for the signing process to finish.  */
-  if ((rc = write_buffer (hashsink, NULL, 0)))
-    goto failure;
-  if ((rc = engine_wait (filter)))
-    goto failure;
-  filter = NULL; /* Not valid anymore.  */
-  hashsink->cb_data = NULL; /* Not needed anymore.  */
-
-
-  /* Write signature attachment.  */
-  if ((rc = write_boundary (sink, boundary, 0)))
-    goto failure;
-
-  if (protocol == PROTOCOL_OPENPGP)
-    {
-      rc = write_string (sink,
-                         "Content-Type: application/pgp-signature\r\n");
-    }
-  else
-    {
-      rc = write_string (sink,
-                         "Content-Transfer-Encoding: base64\r\n"
-                         "Content-Type: application/pkcs7-signature\r\n");
-      /* rc = write_string (sink, */
-      /*                    "Content-Type: application/x-pkcs7-signature\r\n" */
-      /*                    "\tname=\"smime.p7s\"\r\n" */
-      /*                    "Content-Transfer-Encoding: base64\r\n" */
-      /*                    "Content-Disposition: attachment;\r\n" */
-      /*                    "\tfilename=\"smime.p7s\"\r\n"); */
-
-    }
-  /* About the above code:
-     If we would add "Content-Transfer-Encoding: 7bit\r\n" to this
-     attachment, Outlooks does not proceed with sending and even does
-     not return any error.  A wild guess is that while OL adds this
-     header itself, it detects that it already exists and somehow gets
-     into a problem.  It is not a problem with the other parts,
-     though.  Hmmm, triggered by the top levels CT protocol parameter?
-     Anyway, it is not required that we add it as we won't hash it.
-     Note, that this only holds for OpenPGP; for S/MIME we need to set
-     set CTE.  We even write it before the CT because that is the same
-     as Outlook would do it for a missing CTE. */
-  if (rc)
-    goto failure;
-
-  if ((rc = write_string (sink, "\r\n")))
-    goto failure;
-
-  /* Write the signature.  We add an extra CR,LF which should not harm
-     and a terminating 0. */
-  collect_signature (&sigbuffer, "\r\n", 3);
-  if ((rc = write_string (sink, sigbuffer.buf)))
-    goto failure;
-
-
-  /* Write the final boundary and finish the attachment.  */
-  if ((rc = write_boundary (sink, boundary, 1)))
-    goto failure;
-
-  /* Fixup the micalg parameter.  */
-  {
-    HRESULT hr;
-    LARGE_INTEGER off;
-    LPSTREAM stream = (LPSTREAM) sink->cb_data;
-
-    off.QuadPart = 0;
-    hr = stream->Seek (off, STREAM_SEEK_SET, NULL);
-    if (hr)
-      {
-        log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
-                   SRCNAME, __func__, hr);
-        goto failure;
-      }
-
-    create_top_signing_header (top_header, sizeof top_header,
-                               protocol, 1, boundary,
-                               protocol == PROTOCOL_SMIME? "sha1":"pgp-sha1");
-
-    hr = stream->Write (top_header, strlen (top_header), NULL);
-    if (hr)
-      {
-        log_error ("%s:%s: writing fixed micalg failed: hr=%#lx",
-                   SRCNAME, __func__, hr);
-        goto failure;
-      }
-
-    /* Better seek again to the end. */
-    off.QuadPart = 0;
-    hr = stream->Seek (off, STREAM_SEEK_END, NULL);
-    if (hr)
-      {
-        log_error ("%s:%s: seeking back to the end failed: hr=%#lx",
-                   SRCNAME, __func__, hr);
-        goto failure;
-      }
-  }
-
-
-  if (attach)
-    {
-      if (close_mapi_attachment (&attach, sink))
-        goto failure;
-    }
-
-  result = 0;  /* Everything is fine, fall through the cleanup now.  */
-
- failure:
-  engine_cancel (filter);
-  if (attach)
-    cancel_mapi_attachment (&attach, sink);
-  xfree (body);
-  if (result)
-    mapi_release_attach_table (att_table);
-  else
-    *r_att_table = att_table;
-  xfree (sigbuffer.buf);
-  xfree (my_sender);
-  return result;
-}
-
-
-/* Sign the MESSAGE using PROTOCOL.  If PROTOCOL is PROTOCOL_UNKNOWN
-   the engine decides what protocol to use.  On return MESSAGE is
-   modified so that sending it will result in a properly MOSS (that is
-   PGP or S/MIME) signed message.  On failure the function tries to
-   keep the original message intact but there is no 100% guarantee for
-   it. */
-int
-mime_sign (LPMESSAGE message, HWND hwnd, protocol_t protocol,
-           const char *sender, Mail *mail)
-{
-  int result = -1;
-  mapi_attach_item_t *att_table;
-
-  result = do_mime_sign (message, hwnd, protocol, &att_table, 0,
-                         engine_new_session_number (), sender, mail);
-  if (!result)
-    {
-      if (!finalize_message (message, att_table, protocol, 0))
-        result = 0;
-    }
-
-  mapi_release_attach_table (att_table);
-  return result;
-}
-
-
-\f
-/* Sink write method used by mime_encrypt.  */
-int
-sink_encryption_write (sink_t encsink, const void *data, size_t datalen)
-{
-  engine_filter_t filter = (engine_filter_t) encsink->cb_data;
-
-  if (!filter)
-    {
-      log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
-      return -1;
-    }
-
-  return engine_filter (filter, data, datalen);
-}
-
-
-#if 0 /* Not used.  */
-/* Sink write method used by mime_encrypt for writing Base64.  */
-static int
-sink_encryption_write_b64 (sink_t encsink, const void *data, size_t datalen)
-{
-  engine_filter_t filter = encsink->cb_data;
-  int rc;
-  const unsigned char *p;
-  unsigned char inbuf[4];
-  int idx, quads;
-  char outbuf[6];
-  size_t outbuflen;
-
-  if (!filter)
-    {
-      log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
-      return -1;
-    }
-
-  idx = encsink->b64.idx;
-  assert (idx < 4);
-  memcpy (inbuf, encsink->b64.inbuf, idx);
-  quads = encsink->b64.quads;
-
-  if (!data)  /* Flush. */
-    {
-      outbuflen = 0;
-      if (idx)
-        {
-          outbuf[0] = bintoasc[(*inbuf>>2)&077];
-          if (idx == 1)
-            {
-              outbuf[1] = bintoasc[((*inbuf<<4)&060)&077];
-              outbuf[2] = '=';
-              outbuf[3] = '=';
-            }
-          else
-            {
-              outbuf[1] = bintoasc[(((*inbuf<<4)&060)|
-                                    ((inbuf[1]>>4)&017))&077];
-              outbuf[2] = bintoasc[((inbuf[1]<<2)&074)&077];
-              outbuf[3] = '=';
-            }
-          outbuflen = 4;
-          quads++;
-        }
-
-      if (quads)
-        {
-          outbuf[outbuflen++] = '\r';
-          outbuf[outbuflen++] = '\n';
-        }
-
-      if (outbuflen && (rc = engine_filter (filter, outbuf, outbuflen)))
-        return rc;
-      /* Send the flush command to the filter.  */
-      if ((rc = engine_filter (filter, data, datalen)))
-        return rc;
-    }
-  else
-    {
-      for (p = data; datalen; p++, datalen--)
-        {
-          inbuf[idx++] = *p;
-          if (idx > 2)
-            {
-              idx = 0;
-              outbuf[0] = bintoasc[(*inbuf>>2)&077];
-              outbuf[1] = bintoasc[(((*inbuf<<4)&060)
-                                    |((inbuf[1] >> 4)&017))&077];
-              outbuf[2] = bintoasc[(((inbuf[1]<<2)&074)
-                                    |((inbuf[2]>>6)&03))&077];
-              outbuf[3] = bintoasc[inbuf[2]&077];
-              outbuflen = 4;
-              if (++quads >= (64/4))
-                {
-                  quads = 0;
-                  outbuf[4] = '\r';
-                  outbuf[5] = '\n';
-                  outbuflen += 2;
-                }
-              if ((rc = engine_filter (filter, outbuf, outbuflen)))
-                return rc;
-            }
-        }
-    }
-
-  encsink->b64.idx = idx;
-  memcpy (encsink->b64.inbuf, inbuf, idx);
-  encsink->b64.quads = quads;
-
-  return 0;
-}
-#endif /*Not used.*/
-
 
 /* Helper from mime_encrypt.  BOUNDARY is a buffer of at least
    BOUNDARYSIZE+1 bytes which will be set on return from that
    function.  */
 int
 create_top_encryption_header (sink_t sink, protocol_t protocol, char *boundary,
-                              bool is_inline)
+                              bool is_inline, int exchange_major_version)
 {
   int rc;
 
@@ -1915,15 +1489,31 @@ create_top_encryption_header (sink_t sink, protocol_t protocol, char *boundary,
   else if (protocol == PROTOCOL_SMIME)
     {
       *boundary = 0;
-      rc = write_multistring (sink,
-                              "Content-Type: application/pkcs7-mime; "
-                              "smime-type=enveloped-data;\r\n"
-                              "\tname=\"smime.p7m\"\r\n"
-                              "Content-Disposition: attachment; filename=\"smime.p7m\"\r\n"
-                              "Content-Transfer-Encoding: base64\r\n"
-                              "MIME-Version: 1.0\r\n"
-                              "\r\n",
-                              NULL);
+      if (exchange_major_version >= 15)
+        {
+          /*
+             For S/MIME encrypted mails we do not use the S/MIME conversion
+             code anymore. With Exchange 2016 this no longer works. Instead
+             we set an override mime tag, the extended headers in OOM in
+             Mail::update_crypt_oom and let outlook convert the attachment
+             to base64.
+
+             A bit more details can be found in T3853 / T3884
+             */
+          rc = 0;
+        }
+      else
+        {
+          rc = write_multistring (sink,
+                                  "Content-Type: application/pkcs7-mime; "
+                                  "smime-type=enveloped-data;\r\n"
+                                  "\tname=\"smime.p7m\"\r\n"
+                                  "Content-Disposition: attachment; filename=\"smime.p7m\"\r\n"
+                                  "Content-Transfer-Encoding: base64\r\n"
+                                  "MIME-Version: 1.0\r\n"
+                                  "\r\n",
+                                  NULL);
+        }
     }
   else
     {
@@ -1961,402 +1551,6 @@ create_top_encryption_header (sink_t sink, protocol_t protocol, char *boundary,
 }
 
 
-/* Encrypt the MESSAGE.  */
-int
-mime_encrypt (LPMESSAGE message, HWND hwnd,
-              protocol_t protocol, char **recipients,
-              const char *sender, Mail* mail)
-{
-  int result = -1;
-  int rc;
-  LPATTACH attach = nullptr;
-  struct sink_s sinkmem;
-  sink_t sink = &sinkmem;
-  struct sink_s encsinkmem;
-  sink_t encsink = &encsinkmem;
-  char boundary[BOUNDARYSIZE+1];
-  mapi_attach_item_t *att_table = NULL;
-  char *body = NULL;
-  int n_att_usable;
-  engine_filter_t filter = NULL;
-  char *my_sender = NULL;
-  bool is_inline = mail && mail->do_pgp_inline ();
-
-  memset (sink, 0, sizeof *sink);
-  memset (encsink, 0, sizeof *encsink);
-
-  /* Get the attachment info and the body.  We need to do this before
-     creating the engine's filter because sending the cancel to
-     the engine with nothing for the engine to process.  Will result
-     in an error. This is actually a bug in our engine code but
-     we better avoid triggering this bug because the engine
-     sometimes hangs.  Fixme: Needs a proper fix. */
-
-
-  /* Take the Body from the mail if possible. This is a fix for
-     GnuPG-Bug-ID: T3614 because the body is not always properly
-     updated in MAPI when sending. */
-  if (mail)
-    {
-      body = mail->take_cached_plain_body ();
-    }
-  if (!body)
-    {
-      body = mapi_get_body (message, NULL);
-    }
-  if (body && !*body)
-    {
-      xfree (body);
-      body = NULL;
-    }
-  att_table = mapi_create_attach_table (message, 0);
-  n_att_usable = count_usable_attachments (att_table);
-  if (!n_att_usable && !body)
-    {
-      log_debug ("%s:%s: can't encrypt an empty message\n", SRCNAME, __func__);
-      result = gpg_error (GPG_ERR_NO_DATA);
-      goto failure;
-    }
-
-  if (n_att_usable && is_inline)
-    {
-      log_debug ("%s:%s: PGP Inline not supported for attachments. Using PGP MIME",
-                 SRCNAME, __func__);
-      is_inline = false;
-    }
-
-  if (!is_inline || !mail)
-    {
-      attach = create_mapi_attachment (message, sink);
-      if (!attach)
-        return -1;
-    }
-  else
-    {
-      sink->cb_data = mail;
-      sink->writefnc = sink_string_write;
-    }
-
-  /* Prepare the encryption.  We do this early as it is quite common
-     that some recipient keys are not available and thus the
-     encryption will fail early. */
-  if (engine_create_filter (&filter, write_buffer_for_cb, sink))
-    goto failure;
-
-  engine_set_session_number (filter, engine_new_session_number ());
-  {
-    char *tmp = mapi_get_subject (message);
-    engine_set_session_title (filter, tmp);
-    xfree (tmp);
-  }
-
-  if (sender)
-    my_sender = xstrdup (sender);
-  else
-    my_sender = mapi_get_sender (message);
-  if (engine_encrypt_prepare (filter, hwnd, protocol, 0,
-                              my_sender, recipients, &protocol))
-    goto failure;
-  if (engine_encrypt_start (filter, 0))
-    goto failure;
-
-  protocol = check_protocol (protocol);
-  if (protocol == PROTOCOL_UNKNOWN)
-    goto failure;
-
-  if (protocol != PROTOCOL_OPENPGP)
-    {
-      log_debug ("%s:%s: Inline not supported for S/MIME. Using MIME",
-                 SRCNAME, __func__);
-      is_inline = false;
-    }
-
-  /* Write the top header.  */
-  rc = create_top_encryption_header (sink, protocol, boundary,
-                                     is_inline);
-  if (rc)
-    goto failure;
-
-  /* Create a new sink for encrypting the following stuff.  */
-  encsink->cb_data = filter;
-  encsink->writefnc = sink_encryption_write;
-
-  /* Add the plaintext */
-  if (is_inline && body)
-    {
-      if ((rc = write_multistring (encsink, body, NULL)))
-        {
-          log_error ("%s:%s: Adding the body failed.",
-                     SRCNAME, __func__);
-          goto failure;
-        }
-    }
-  else if (add_body_and_attachments (encsink, message, att_table, mail,
-                                     body, n_att_usable))
-    {
-      goto failure;
-    }
-
-  xfree (body);
-  body = NULL;
-
-  /* Flush the encryption sink and wait for the encryption to get
-     ready.  */
-  if ((rc = write_buffer (encsink, NULL, 0)))
-    goto failure;
-  if ((rc = engine_wait (filter)))
-    goto failure;
-  filter = NULL; /* Not valid anymore.  */
-  encsink->cb_data = NULL; /* Not needed anymore.  */
-
-    if (!sink->enc_counter)
-    {
-      log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
-      goto failure;
-    }
-
-  /* Write the final boundary (for OpenPGP) and finish the attachment.  */
-  if (*boundary && (rc = write_boundary (sink, boundary, 1)))
-    goto failure;
-
-  if (attach && close_mapi_attachment (&attach, sink))
-    goto failure;
-
-  if (finalize_message (message, att_table, protocol, 1, is_inline))
-    goto failure;
-
-  result = 0;  /* Everything is fine, fall through the cleanup now.  */
-
- failure:
-  engine_cancel (filter);
-  if (attach)
-    {
-      cancel_mapi_attachment (&attach, sink);
-    }
-  xfree (body);
-  mapi_release_attach_table (att_table);
-  xfree (my_sender);
-  return result;
-}
-
-
-
-\f
-/* Sign and Encrypt the MESSAGE.  */
-int
-mime_sign_encrypt (LPMESSAGE message, HWND hwnd,
-                   protocol_t protocol, char **recipients,
-                   const char *sender, Mail *mail)
-{
-  int result = -1;
-  int rc = 0;
-  HRESULT hr;
-  LPATTACH attach;
-  LPSTREAM tmpstream = NULL;
-  struct sink_s sinkmem;
-  sink_t sink = &sinkmem;
-  struct sink_s encsinkmem;
-  sink_t encsink = &encsinkmem;
-  struct sink_s tmpsinkmem;
-  sink_t tmpsink = &tmpsinkmem;
-  char boundary[BOUNDARYSIZE+1];
-  mapi_attach_item_t *att_table = NULL;
-  engine_filter_t filter = NULL;
-  unsigned int session_number;
-  char *my_sender = NULL;
-
-  memset (sink, 0, sizeof *sink);
-  memset (encsink, 0, sizeof *encsink);
-  memset (tmpsink, 0, sizeof *tmpsink);
-
-  attach = create_mapi_attachment (message, sink);
-  if (!attach)
-    return -1;
-
-  /* First check that we are not trying to process an empty message
-     which might lock up our engine.  Unfortunately we need to
-     duplicate the code we use in do_mime_sign here.  FIXME: The
-     engine should be fixed instead of using such a workaround.  */
-  {
-    char *body;
-
-    body = mapi_get_body (message, NULL);
-    if (body && !*body)
-    {
-      xfree (body);
-      body = NULL;
-    }
-    att_table = mapi_create_attach_table (message, 0);
-    if (!count_usable_attachments (att_table) && !body)
-      result = gpg_error (GPG_ERR_NO_DATA);
-    xfree (body);
-    if (att_table)
-      {
-        mapi_release_attach_table (att_table);
-        att_table = NULL;
-      }
-    if (gpg_err_code (result) == GPG_ERR_NO_DATA)
-      {
-        log_debug ("%s:%s: can't sign+encrypt an empty message\n",
-                   SRCNAME, __func__);
-        goto failure;
-      }
-  }
-
-
-  /* Create a temporary sink to construct the signed data.  */
-  hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
-                         (SOF_UNIQUEFILENAME | STGM_DELETEONRELEASE
-                          | STGM_CREATE | STGM_READWRITE),
-                         NULL, GpgOLStr("GPG"), &tmpstream);
-  if (FAILED (hr))
-    {
-      log_error ("%s:%s: can't create temp file: hr=%#lx\n",
-                 SRCNAME, __func__, hr);
-      rc = -1;
-      goto failure;
-    }
-  tmpsink->cb_data = tmpstream;
-  tmpsink->writefnc = sink_std_write;
-
-
-  /* Prepare the encryption.  We do this early as it is quite common
-     that some recipients are not available and thus the encryption
-     will fail early.  This is also required to allow the UIserver to
-     figure out the protocol to use if we have not forced one.  */
-  if (engine_create_filter (&filter, write_buffer_for_cb, sink))
-    goto failure;
-
-  session_number = engine_new_session_number ();
-  engine_set_session_number (filter, session_number);
-  {
-    char *tmp = mapi_get_subject (message);
-    engine_set_session_title (filter, tmp);
-    xfree (tmp);
-  }
-
-  if (sender)
-    my_sender = xstrdup (sender);
-  else
-    my_sender = mapi_get_sender (message);
-  if ((rc=engine_encrypt_prepare (filter, hwnd,
-                                  protocol, ENGINE_FLAG_SIGN_FOLLOWS,
-                                  my_sender, recipients, &protocol)))
-    goto failure;
-
-  protocol = check_protocol (protocol);
-  if (protocol == PROTOCOL_UNKNOWN)
-    goto failure;
-
-  /* Now sign the message.  This creates another attachment with the
-     complete MIME object of the signed message.  We can't do the
-     encryption in streaming mode while running the encryption because
-     we need to fix up that ugly micalg parameter after having created
-     the signature.  Note that the protocol to use is taken from the
-     encryption operation. */
-  if (do_mime_sign (message, hwnd, protocol, &att_table, tmpsink,
-                    session_number, sender, mail))
-    goto failure;
-
-  /* Now send the actual ENCRYPT command.  This split up between
-     prepare and start is necessary to help with the implementarion of
-     the UI-server.  If we would send the ENCRYPT command immediately
-     the UI-server might block while reading from the input stream
-     because we are first going to do a sign operation which in trun
-     needs the attention of the UI server.  A more robust but
-     complicated approach to the UI-server would be to delay the
-     reading (and thus the start of the underlying encrypt operation)
-     until the first byte has been received. */
-  if ((rc=engine_encrypt_start (filter, 0)))
-    goto failure;
-
-  /* Write the top header.  */
-  rc = create_top_encryption_header (sink, protocol, boundary);
-  if (rc)
-    goto failure;
-
-  /* Create a new sink for encrypting the temporary attachment with
-     the signed message.  */
-  encsink->cb_data = filter;
-  encsink->writefnc = sink_encryption_write;
-
-  /* Copy the temporary stream to the encryption sink.  */
-  {
-    LARGE_INTEGER off;
-    ULONG nread;
-    char buffer[4096];
-
-    off.QuadPart = 0;
-    hr = tmpstream->Seek (off, STREAM_SEEK_SET, NULL);
-    if (hr)
-      {
-        log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
-                   SRCNAME, __func__, hr);
-        rc = gpg_error (GPG_ERR_EIO);
-        goto failure;
-      }
-
-    for (;;)
-      {
-        hr = tmpstream->Read (buffer, sizeof buffer, &nread);
-        if (hr)
-          {
-            log_error ("%s:%s: IStream::Read failed: hr=%#lx",
-                       SRCNAME, __func__, hr);
-            rc = gpg_error (GPG_ERR_EIO);
-            goto failure;
-          }
-        if (!nread)
-          break;  /* EOF */
-        rc = write_buffer (encsink, buffer, nread);
-        if (rc)
-          {
-            log_error ("%s:%s: writing tmpstream to encsink failed: %s",
-                       SRCNAME, __func__, gpg_strerror (rc));
-            goto failure;
-          }
-      }
-  }
-
-
-  /* Flush the encryption sink and wait for the encryption to get
-     ready.  */
-  if ((rc = write_buffer (encsink, NULL, 0)))
-    goto failure;
-  if ((rc = engine_wait (filter)))
-    goto failure;
-  filter = NULL; /* Not valid anymore.  */
-  encsink->cb_data = NULL; /* Not needed anymore.  */
-
-  if (!sink->enc_counter)
-    {
-      log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
-      goto failure;
-    }
-
-  /* Write the final boundary (for OpenPGP) and finish the attachment.  */
-  if (*boundary && (rc = write_boundary (sink, boundary, 1)))
-    goto failure;
-
-  if (close_mapi_attachment (&attach, sink))
-    goto failure;
-
-  if (finalize_message (message, att_table, protocol, 1))
-    goto failure;
-
-  result = 0;  /* Everything is fine, fall through the cleanup now.  */
-
- failure:
-  if (result)
-    log_debug ("%s:%s: failed rc=%d (%s) <%s>", SRCNAME, __func__, rc,
-               gpg_strerror (rc), gpg_strsource (rc));
-  engine_cancel (filter);
-  gpgol_release (tmpstream);
-  mapi_release_attach_table (att_table);
-  xfree (my_sender);
-  return result;
-}
-
 int
 restore_msg_from_moss (LPMESSAGE message, LPDISPATCH moss_att,
                        msgtype_t type, char *msgcls)
@@ -2419,14 +1613,6 @@ restore_msg_from_moss (LPMESSAGE message, LPDISPATCH moss_att,
       goto done;
     }
 
-  /* Set a special property so that we are later able to identify
-     messages signed or encrypted by us.  */
-  if (mapi_set_sig_status (message, "@"))
-    {
-      log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
-      goto done;
-    }
-
   err = 0;
 done:
   xfree (orig);