[git] GpgOL - branch, nomapi, updated. gpgol-1.4.0-23-g3985969

by Andre Heinecke cvs at cvs.gnupg.org
Wed Sep 14 14:23:54 CEST 2016


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GnuPG extension for MS Outlook".

The branch, nomapi has been updated
       via  3985969e5557b472f74ac4cd7e247aeeb555b1fe (commit)
       via  4229a56e2e3b1018cafec9a833eb36d948041f51 (commit)
      from  553db6ba3cfaca7275669abbc5024037363630be (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 3985969e5557b472f74ac4cd7e247aeeb555b1fe
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Wed Sep 14 14:18:44 2016 +0200

    Start refactoring mimeparser into mimedataprovider
    
    * src/mail.cpp (Mail::parsing_done): Use new API.
    * src/mailparser.cpp: Move out body / attachment handling.
    * src/mailparser.h: Update accordingly.
    * src/mapihelp.cpp (mapi_mark_or_create_moss_attach): Fix return
    for multipart/signed.
    * src/mimedataprovider.cpp, src/mimedataprovider.h: Rework.
    
    --
    The MimeDataprovider now handles the transition from a MIME Message
    into a signed / signature or for crypto messages into a (html)body
    with attachments. It can be wrapped into GpgME::Data.
    
    After creation of a MimeDataprovider no more MAPI structures
    are needed and everything is currently done in memory.
    
    The mimeparser code is based on the old mimeparser.c but a bit
    simplyfied and restructured to split out the actual mime parsing
    from the crypto calls.
    
    This is a first working version and is not fully working
    yet.

diff --git a/src/mail.cpp b/src/mail.cpp
index 6043ed1..98a0be6 100644
--- a/src/mail.cpp
+++ b/src/mail.cpp
@@ -327,10 +327,10 @@ void Mail::parsing_done()
 {
   m_needs_wipe = true;
   /* Update the body */
-  const auto html = m_parser->get_utf8_html_body();
-  if (!html->empty())
+  const auto html = m_parser->get_html_body();
+  if (!html.empty())
     {
-      if (put_oom_string (m_mailitem, "HTMLBody", html->c_str()))
+      if (put_oom_string (m_mailitem, "HTMLBody", html.c_str()))
         {
           log_error ("%s:%s: Failed to modify html body of item.",
                      SRCNAME, __func__);
@@ -339,8 +339,8 @@ void Mail::parsing_done()
     }
   else
     {
-      const auto body = m_parser->get_utf8_text_body();
-      if (put_oom_string (m_mailitem, "Body", body->c_str()))
+      const auto body = m_parser->get_body();
+      if (put_oom_string (m_mailitem, "Body", body.c_str()))
         {
           log_error ("%s:%s: Failed to modify body of item.",
                      SRCNAME, __func__);
diff --git a/src/mailparser.cpp b/src/mailparser.cpp
index c73de1f..e34c749 100644
--- a/src/mailparser.cpp
+++ b/src/mailparser.cpp
@@ -33,9 +33,8 @@
 using namespace GpgME;
 
 MailParser::MailParser(LPSTREAM instream, msgtype_t type):
-    m_body (std::shared_ptr<std::string>(new std::string())),
-    m_htmlbody (std::shared_ptr<std::string>(new std::string())),
-    m_input (Data(new MimeDataProvider(instream))),
+    m_inputprovider  (new MimeDataProvider(instream)),
+    m_outputprovider (new MimeDataProvider()),
     m_type (type),
     m_error (false)
 {
@@ -46,6 +45,8 @@ MailParser::MailParser(LPSTREAM instream, msgtype_t type):
 MailParser::~MailParser()
 {
   log_debug ("%s:%s", SRCNAME, __func__);
+  delete m_inputprovider;
+  delete m_outputprovider;
 }
 
 static void
@@ -91,29 +92,37 @@ MailParser::parse()
   auto ctx = Context::createForProtocol (protocol);
   ctx->setArmor(true);
 
+  Data output(m_outputprovider);
+  Data input(m_inputprovider);
+  log_debug ("%s:%s: decrypt: %i verify: %i with protocol: %s",
+             SRCNAME, __func__,
+             decrypt, verify,
+             protocol == OpenPGP ? "OpenPGP" :
+             protocol == CMS ? "CMS" : "Unknown");
   if (decrypt)
     {
-      Data output;
-      log_debug ("%s:%s: Decrypting with protocol: %s",
-                 SRCNAME, __func__,
-                 protocol == OpenPGP ? "OpenPGP" :
-                 protocol == CMS ? "CMS" : "Unknown");
-      auto combined_result = ctx->decryptAndVerify(m_input, output);
+      auto combined_result = ctx->decryptAndVerify(input, output);
       m_decrypt_result = combined_result.first;
       m_verify_result = combined_result.second;
-      if (m_decrypt_result.error())
+    }
+  else
+    {
+      const auto sig = m_inputprovider->signature();
+      /* Ignore the first two bytes if we did not decrypt. */
+      input.seek (2, SEEK_SET);
+      if (sig)
         {
-          MessageBox (NULL, "Decryption failed.", "Failed", MB_OK);
+          sig->seek (0, SEEK_SET);
+          m_verify_result = ctx->verifyDetachedSignature(*sig, input);
         }
-      char buf[2048];
-      size_t bRead;
-      output.seek (0, SEEK_SET);
-      while ((bRead = output.read (buf, 2048)) > 0)
+      else
         {
-          (*m_body).append(buf, bRead);
+          m_verify_result = ctx->verifyOpaqueSignature(input, output);
         }
-      log_debug ("Body is: %s", m_body->c_str());
     }
+  log_debug ("%s:%s: decrypt err: %i verify err: %i",
+             SRCNAME, __func__, m_decrypt_result.error().code(),
+             m_verify_result.error().code());
 
   if (opt.enable_debug)
     {
@@ -121,27 +130,50 @@ MailParser::parse()
        ss << m_decrypt_result << '\n' << m_verify_result;
        log_debug ("Decrypt / Verify result: %s", ss.str().c_str());
     }
+  /*
   Attachment *att = new Attachment ();
   att->write ("Hello attachment", strlen ("Hello attachment"));
   att->set_display_name ("The Attachment.txt");
   m_attachments.push_back (std::shared_ptr<Attachment>(att));
+  */
   return std::string();
 }
 
-std::shared_ptr<std::string>
-MailParser::get_utf8_html_body()
+const std::string
+MailParser::get_html_body() const
 {
-  return m_htmlbody;
+  if (m_outputprovider)
+    {
+      return m_outputprovider->get_html_body();
+    }
+  else
+    {
+      return std::string();
+    }
 }
 
-std::shared_ptr<std::string>
-MailParser::get_utf8_text_body()
+const std::string
+MailParser::get_body() const
 {
-  return m_body;
+  if (m_outputprovider)
+    {
+      return m_outputprovider->get_body();
+    }
+  else
+    {
+      return std::string();
+    }
 }
 
 std::vector<std::shared_ptr<Attachment> >
-MailParser::get_attachments()
+MailParser::get_attachments() const
 {
-  return m_attachments;
+  if (m_outputprovider)
+    {
+      return m_outputprovider->get_attachments();
+    }
+  else
+    {
+      return std::vector<std::shared_ptr<Attachment> >();
+    }
 }
diff --git a/src/mailparser.h b/src/mailparser.h
index f92c313..53fb25d 100644
--- a/src/mailparser.h
+++ b/src/mailparser.h
@@ -34,6 +34,7 @@
 #include <gpgme++/data.h>
 
 class Attachment;
+class MimeDataProvider;
 
 class MailParser
 {
@@ -53,22 +54,19 @@ public:
     * empty string on success or an error message on failure. */
   std::string parse();
 
-  /** Get the Body converted to utf8. Call parse first. */
-  std::shared_ptr<std::string> get_utf8_text_body();
+  /** Get the Body. Call parse first. */
+  const std::string get_body() const;
 
-  /** Get an alternative? HTML Body converted to utf8. Call parse first. */
-  std::shared_ptr<std::string> get_utf8_html_body();
+  /** Get an alternative? HTML Body. Call parse first. */
+  const std::string get_html_body() const;
 
   /** Get the decrypted / verified attachments. Call parse first.
   */
-  std::vector<std::shared_ptr<Attachment> > get_attachments();
+  std::vector<std::shared_ptr<Attachment> > get_attachments() const;
 private:
-  std::vector<std::shared_ptr<Attachment> > m_attachments;
-  std::shared_ptr<std::string> m_body;
-  std::shared_ptr<std::string> m_htmlbody;
-
   /* State variables */
-  GpgME::Data m_input;
+  MimeDataProvider *m_inputprovider;
+  MimeDataProvider *m_outputprovider;
   msgtype_t m_type;
   bool m_error;
   GpgME::DecryptionResult m_decrypt_result;
diff --git a/src/mapihelp.cpp b/src/mapihelp.cpp
index 854e6f4..9c25abc 100644
--- a/src/mapihelp.cpp
+++ b/src/mapihelp.cpp
@@ -3530,7 +3530,9 @@ mapi_mark_or_create_moss_attach (LPMESSAGE message, msgtype_t msgtype)
     {
       /* Found existing moss attachment */
       mapi_release_attach_table (table);
-      return part2;
+      if (part2)
+        return part2;
+      return part1;
     }
 
   if (msgtype == MSGTYPE_GPGOL_CLEAR_SIGNED ||
diff --git a/src/mimedataprovider.cpp b/src/mimedataprovider.cpp
index d6628f3..56622d6 100644
--- a/src/mimedataprovider.cpp
+++ b/src/mimedataprovider.cpp
@@ -20,31 +20,454 @@
 #include "common.h"
 
 #include "mimedataprovider.h"
+#include "parsetlv.h"
+#include "rfc822parse.h"
+#include "rfc2047parse.h"
+#include "attachment.h"
 
 /* The maximum length of a line we are able to process.  RFC822 allows
    only for 1000 bytes; thus 2000 seems to be a reasonable value. */
 #define LINEBUFSIZE 2000
 
-/* How much data is read from the underlying stream in a collect
-   call. */
+/* How much data is read at once in collect */
 #define BUFSIZE 8192
 
 #include <gpgme++/error.h>
 
+/* To keep track of the MIME message structures we use a linked list
+   with each item corresponding to one part. */
+struct mimestruct_item_s;
+typedef struct mimestruct_item_s *mimestruct_item_t;
+struct mimestruct_item_s
+{
+  mimestruct_item_t next;
+  unsigned int level;   /* Level in the hierarchy of that part.  0
+                           indicates the outer body.  */
+  char *filename;       /* Malloced filename or NULL.  */
+  char *charset;        /* Malloced charset or NULL.  */
+  char content_type[1]; /* String with the content type. */
+};
+
+/* The context object we use to track information. */
+struct mime_context
+{
+  rfc822parse_t msg;  /* The handle of the RFC822 parser. */
+
+  int verify_mode;    /* True if we want to verify a signature. */
+  int no_mail_header; /* True if we want to bypass all MIME parsing.  */
+
+  int nesting_level;  /* Current MIME nesting level. */
+  int in_data;        /* We are currently in data (body or attachment). */
+  int body_seen;      /* True if we have seen a part we consider the
+                         body of the message.  */
+
+  int collect_attachment; /* True if we are collecting an attachment */
+  std::shared_ptr<Attachment> current_attachment; /* A pointer to the current
+                                                     attachment */
+  int collect_body;       /* True if we are collcting the body */
+  int collect_html_body;  /* True if we are collcting the html body */
+  int collect_signeddata; /* True if we are collecting the signed data. */
+  int collect_signature;  /* True if we are collecting a signature.  */
+  int start_hashing;      /* Flag used to start collecting signed data. */
+  int hashing_level;      /* MIME level where we started hashing. */
+  int is_qp_encoded;      /* Current part is QP encoded. */
+  int is_base64_encoded;  /* Current part is base 64 encoded. */
+  int is_body;            /* The current part belongs to the body.  */
+  int is_opaque_signed;   /* Flag indicating opaque signed S/MIME. */
+  int may_be_opaque_signed;/* Hack, see code.  */
+  protocol_t protocol;    /* The detected crypto protocol.  */
+
+  int part_counter;       /* Counts the number of processed parts. */
+  int any_boundary;       /* Indicates whether we have seen any
+                             boundary which means that we are actually
+                             working on a MIME message and not just on
+                             plain rfc822 message.  */
+
+  /* A linked list describing the structure of the mime message.  This
+     list gets build up while parsing the message.  */
+  mimestruct_item_t mimestruct;
+  mimestruct_item_t *mimestruct_tail;
+  mimestruct_item_t mimestruct_cur;
+
+  int any_attachments_created;  /* True if we created a new atatchment.  */
+
+  b64_state_t base64;     /* The state of the Base-64 decoder.  */
+
+  gpg_error_t parser_error;   /* Indicates that we encountered a error from
+                                 the parser. */
+};
+typedef struct mime_context *mime_context_t;
+
+/* Print the message event EVENT. */
+static void
+debug_message_event (rfc822parse_event_t event)
+{
+  const char *s;
+
+  switch (event)
+    {
+    case RFC822PARSE_OPEN: s= "Open"; break;
+    case RFC822PARSE_CLOSE: s= "Close"; break;
+    case RFC822PARSE_CANCEL: s= "Cancel"; break;
+    case RFC822PARSE_T2BODY: s= "T2Body"; break;
+    case RFC822PARSE_FINISH: s= "Finish"; break;
+    case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
+    case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break;
+    case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break;
+    case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
+    case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
+    case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break;
+    case RFC822PARSE_PREAMBLE: s= "Preamble"; break;
+    case RFC822PARSE_EPILOGUE: s= "Epilogue"; break;
+    default: s= "[unknown event]"; break;
+    }
+  log_mime_parser ("%s: rfc822 event %s\n", SRCNAME, s);
+}
+
+/* Returns true if the BER encoded data in BUFFER is CMS signed data.
+   LENGTH gives the length of the buffer, for correct detection LENGTH
+   should be at least about 24 bytes.  */
+#if 0
+static int
+is_cms_signed_data (const char *buffer, size_t length)
+{
+  const char *p = buffer;
+  size_t n = length;
+  tlvinfo_t ti;
+
+  if (parse_tlv (&p, &n, &ti))
+    return 0;
+  if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_SEQUENCE
+        && ti.is_cons) )
+    return 0;
+  if (parse_tlv (&p, &n, &ti))
+    return 0;
+  if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_OBJECT_ID
+        && !ti.is_cons && ti.length) || ti.length > n)
+    return 0;
+  if (ti.length == 9 && !memcmp (p, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x02", 9))
+    return 1;
+  return 0;
+}
+#endif
+
+/* Process the transition to body event.
+
+   This means we have received the empty line indicating the body and
+   should now check the headers to see what to do about this part.  */
+static int
+t2body (MimeDataProvider *provider, rfc822parse_t msg)
+{
+  rfc822parse_field_t field;
+  mime_context_t ctx = provider->mime_context ();
+  const char *ctmain, *ctsub;
+  const char *s;
+  size_t off;
+  char *p;
+  int is_text = 0;
+  int not_inline_text = 0;
+  char *filename = NULL;
+  char *charset = NULL;
+
+  /* Figure out the encoding.  */
+  ctx->is_qp_encoded = 0;
+  ctx->is_base64_encoded = 0;
+  p = rfc822parse_get_field (msg, "Content-Transfer-Encoding", -1, &off);
+  if (p)
+    {
+      if (!stricmp (p+off, "quoted-printable"))
+        ctx->is_qp_encoded = 1;
+      else if (!stricmp (p+off, "base64"))
+        {
+          ctx->is_base64_encoded = 1;
+          b64_init (&ctx->base64);
+        }
+      free (p);
+    }
+
+  /* Get the filename from the header.  */
+  field = rfc822parse_parse_field (msg, "Content-Disposition", -1);
+  if (field)
+    {
+      s = rfc822parse_query_parameter (field, "filename", 0);
+      if (s)
+        filename = rfc2047_parse (s);
+      s = rfc822parse_query_parameter (field, NULL, 1);
+      if (s && strcmp (s, "inline"))
+        not_inline_text = 1;
+      rfc822parse_release_field (field);
+    }
+
+  /* Process the Content-type and all its parameters.  */
+  ctmain = ctsub = NULL;
+  field = rfc822parse_parse_field (msg, "Content-Type", -1);
+  if (field)
+    ctmain = rfc822parse_query_media_type (field, &ctsub);
+  if (!ctmain)
+    {
+      /* Either there is no content type field or it is faulty; in
+         both cases we fall back to text/plain.  */
+      ctmain = "text";
+      ctsub  = "plain";
+    }
+
+  log_mime_parser ("%s:%s: ctx=%p, ct=`%s/%s'\n",
+                   SRCNAME, __func__, ctx, ctmain, ctsub);
+
+  s = rfc822parse_query_parameter (field, "charset", 0);
+  if (s)
+    charset = xstrdup (s);
+
+  if (!filename)
+    {
+      /* Check for Content-Type name if Content-Disposition filename
+         was not found */
+      s = rfc822parse_query_parameter (field, "name", 0);
+      if (s)
+        filename = rfc2047_parse (s);
+    }
+
+  /* Update our idea of the entire MIME structure.  */
+  {
+    mimestruct_item_t ms;
+
+    ms = (mimestruct_item_t) xmalloc (sizeof *ms + strlen (ctmain) + 1 + strlen (ctsub));
+    ctx->mimestruct_cur = ms;
+    *ctx->mimestruct_tail = ms;
+    ctx->mimestruct_tail = &ms->next;
+    ms->next = NULL;
+    strcpy (stpcpy (stpcpy (ms->content_type, ctmain), "/"), ctsub);
+    ms->level = ctx->nesting_level;
+    ms->filename = filename;
+    filename = NULL;
+    ms->charset = charset;
+    charset = NULL;
+  }
+
+  if (!strcmp (ctmain, "multipart"))
+    {
+      /* We don't care about the top level multipart layer but wait
+         until it comes to the actual parts which then will get stored
+         as attachments.
+
+         For now encapsulated signed or encrypted containers are not
+         processed in a special way as they should.  Except for the
+         simple verify mode. */
+      if (!provider->signature()
+          && !strcmp (ctsub, "signed")
+          && (s = rfc822parse_query_parameter (field, "protocol", 0)))
+        {
+          if (!strcmp (s, "application/pgp-signature"))
+            ctx->protocol = PROTOCOL_OPENPGP;
+          else if (!strcmp (s, "application/pkcs7-signature")
+                   || !strcmp (s, "application/x-pkcs7-signature"))
+            ctx->protocol = PROTOCOL_SMIME;
+          else
+            ctx->protocol = PROTOCOL_UNKNOWN;
+
+          /* Need to start the hashing after the next boundary. */
+          ctx->start_hashing = 1;
+        }
+    }
+  else if (!strcmp (ctmain, "text"))
+    {
+      is_text = !strcmp (ctsub, "html")? 2:1;
+    }
+  else if (ctx->nesting_level == 1 && !provider->signature()
+           && !strcmp (ctmain, "application")
+           && ((ctx->protocol == PROTOCOL_OPENPGP
+                && !strcmp (ctsub, "pgp-signature"))
+               || (ctx->protocol == PROTOCOL_SMIME
+                   && (!strcmp (ctsub, "pkcs7-signature")
+                       || !strcmp (ctsub, "x-pkcs7-signature")))))
+    {
+      /* This is the second part of a MOSS signature.  We only support
+         here full messages thus checking the nesting level is
+         sufficient.  We do this only for the first signature (i.e. if
+         sig_data has not been set yet).  We also do this only while
+         in verify mode because we don't want to write a full MUA.  */
+      ctx->collect_signature = 1;
+      log_mime_parser ("Collecting signature now");
+    }
+  else /* Other type. */
+    {
+      /* Check whether this attachment is an opaque signed S/MIME
+         part.  We use a counter to later check that there is only one
+         such part. */
+      if (!strcmp (ctmain, "application")
+          && (!strcmp (ctsub, "pkcs7-mime")
+              || !strcmp (ctsub, "x-pkcs7-mime")))
+        {
+          const char *smtype = rfc822parse_query_parameter (field,
+                                                            "smime-type", 0);
+          if (smtype && !strcmp (smtype, "signed-data"))
+            ctx->is_opaque_signed++;
+          else
+            {
+              /* CryptoEx is notorious in setting wrong MIME header.
+                 Mark that so we can test later if possible. */
+              ctx->may_be_opaque_signed++;
+            }
+        }
+
+      ctx->collect_attachment = 1;
+    }
+  rfc822parse_release_field (field); /* (Content-type) */
+  ctx->in_data = 1;
+
+  /* Need to start an attachment if we have seen a content disposition
+     other then the inline type. */
+  if (is_text && not_inline_text)
+    ctx->collect_attachment = 1;
+
+  log_mime_parser ("%s:%s: this body: nesting=%d partno=%d is_text=%d, is_opq=%d"
+                   " charset=\"%s\"\n",
+                   SRCNAME, __func__,
+                   ctx->nesting_level, ctx->part_counter, is_text,
+                   ctx->is_opaque_signed,
+                   ctx->mimestruct_cur->charset?ctx->mimestruct_cur->charset:"");
+
+  /* If this is a text part, decide whether we treat it as our body. */
+  if (is_text && !not_inline_text)
+    {
+      ctx->collect_attachment = 1;
+      ctx->body_seen = 1;
+      if (is_text == 2)
+        {
+          ctx->collect_html_body = 1;
+          ctx->collect_body = 0;
+        }
+      else
+        {
+          ctx->collect_body = 1;
+          ctx->collect_html_body = 0;
+        }
+    }
+  else if (ctx->collect_attachment)
+    {
+      /* Now that if we have an attachment prepare a new MAPI
+         attachment.  */
+      ctx->current_attachment = provider->create_attachment();
+    }
+
+  return 0;
+}
+
 static int
 message_cb (void *opaque, rfc822parse_event_t event,
             rfc822parse_t msg)
 {
-  (void) opaque;
-  (void) event;
-  (void) msg;
-  return 0;
+  int retval = 0;
+
+  MimeDataProvider *provider = static_cast<MimeDataProvider*> (opaque);
+
+  mime_context_t ctx = provider->mime_context();
+
+  debug_message_event (event);
+  if (ctx->no_mail_header)
+    {
+      /* Assume that this is not a regular mail but plain text. */
+      if (event == RFC822PARSE_OPEN)
+        return 0; /*  We need to skip the OPEN event.  */
+      if (!ctx->body_seen)
+        {
+          log_mime_parser ("%s:%s: assuming this is plain text without headers\n",
+                           SRCNAME, __func__);
+          ctx->in_data = 1;
+          ctx->collect_attachment = 2; /* 2 so we don't skip the first line. */
+          ctx->body_seen = 1;
+          /* Create a fake MIME structure.  */
+          /* Fixme: We might want to take it from the enclosing message.  */
+          {
+            const char ctmain[] = "text";
+            const char ctsub[] = "plain";
+            mimestruct_item_t ms;
+
+            ms = (mimestruct_item_t) xmalloc (sizeof *ms + strlen (ctmain) + 1 + strlen (ctsub));
+            ctx->mimestruct_cur = ms;
+            *ctx->mimestruct_tail = ms;
+            ctx->mimestruct_tail = &ms->next;
+            ms->next = NULL;
+            strcpy (stpcpy (stpcpy (ms->content_type, ctmain), "/"), ctsub);
+            ms->level = 0;
+            ms->filename = NULL;
+            ms->charset = NULL;
+          }
+          ctx->collect_body = 1;
+        }
+      return 0;
+    }
+
+  if (event == RFC822PARSE_BEGIN_HEADER || event == RFC822PARSE_T2BODY)
+    {
+      /* We need to check here whether to start collecting signed data
+         because attachments might come without header lines and thus
+         we won't see the BEGIN_HEADER event. */
+      if (ctx->start_hashing == 1)
+        {
+          ctx->start_hashing = 2;
+          ctx->hashing_level = ctx->nesting_level;
+          ctx->collect_signeddata = 1;
+        }
+    }
+
+
+  switch (event)
+    {
+    case RFC822PARSE_T2BODY:
+      retval = t2body (provider, msg);
+      break;
+
+    case RFC822PARSE_LEVEL_DOWN:
+      ctx->nesting_level++;
+      break;
+
+    case RFC822PARSE_LEVEL_UP:
+      if (ctx->nesting_level)
+        ctx->nesting_level--;
+      else
+        {
+          log_error ("%s: ctx=%p, invalid structure: bad nesting level\n",
+                     SRCNAME, ctx);
+          ctx->parser_error = gpg_error (GPG_ERR_GENERAL);
+        }
+      break;
+
+    case RFC822PARSE_BOUNDARY:
+    case RFC822PARSE_LAST_BOUNDARY:
+      ctx->any_boundary = 1;
+      ctx->in_data = 0;
+      ctx->collect_attachment = 0;
+      ctx->collect_body = 0;
+
+      if (ctx->start_hashing == 2 && ctx->hashing_level == ctx->nesting_level)
+        {
+          ctx->start_hashing = 3; /* Avoid triggering it again. */
+          ctx->collect_signeddata = 0;
+        }
+      break;
+
+    case RFC822PARSE_BEGIN_HEADER:
+      ctx->part_counter++;
+      break;
+
+    default:  /* Ignore all other events. */
+      break;
+    }
+
+  return retval;
+}
+
+MimeDataProvider::MimeDataProvider() :
+  m_signature(nullptr)
+{
+  m_mime_ctx = (mime_context_t) xcalloc (1, sizeof *m_mime_ctx);
+  m_mime_ctx->msg = rfc822parse_open (message_cb, this);
+  m_mime_ctx->mimestruct_tail = &m_mime_ctx->mimestruct;
 }
 
 MimeDataProvider::MimeDataProvider(LPSTREAM stream):
-  m_collect(true),
-  m_parser(rfc822parse_open (message_cb, this)),
-  m_current_encoding(None)
+  MimeDataProvider()
 {
   if (stream)
     {
@@ -55,7 +478,6 @@ MimeDataProvider::MimeDataProvider(LPSTREAM stream):
       log_error ("%s:%s called without stream ", SRCNAME, __func__);
       return;
     }
-  b64_init (&m_base64_context);
   log_mime_parser ("%s:%s Collecting data.", SRCNAME, __func__);
   collect_data (stream);
   log_mime_parser ("%s:%s Data collected.", SRCNAME, __func__);
@@ -65,6 +487,11 @@ MimeDataProvider::MimeDataProvider(LPSTREAM stream):
 MimeDataProvider::~MimeDataProvider()
 {
   log_debug ("%s:%s", SRCNAME, __func__);
+  free (m_mime_ctx);
+  if (m_signature)
+    {
+      delete m_signature;
+    }
 }
 
 bool
@@ -80,7 +507,7 @@ MimeDataProvider::read(void *buffer, size_t size)
 {
   log_mime_parser ("%s:%s: Reading: " SIZE_T_FORMAT "Bytes",
                  SRCNAME, __func__, size);
-  ssize_t bRead = m_data.read (buffer, size);
+  ssize_t bRead = m_crypto_data.read (buffer, size);
   if (opt.enable_debug & DBG_MIME_PARSER)
     {
       std::string buf ((char *)buffer, bRead);
@@ -90,29 +517,6 @@ MimeDataProvider::read(void *buffer, size_t size)
   return bRead;
 }
 
-void
-MimeDataProvider::decode_and_collect(char *line, size_t pos)
-{
-  /* We are inside the data.  That should be the actual
-     ciphertext in the given encoding. Add it to our internal
-     cache. */
-  int slbrk = 0;
-  size_t len;
-
-  if (m_current_encoding == Quoted)
-    len = qp_decode (line, pos, &slbrk);
-  else if (m_current_encoding == Base64)
-    len = b64_decode (&m_base64_context, line, pos);
-  else
-    len = pos;
-  m_data.write (line, len);
-  if (m_current_encoding != Encoding::Base64 && !slbrk)
-    {
-      m_data.write ("\r\n", 2);
-    }
-  return;
-}
-
 /* Split some raw data into lines and handle them accordingly.
    returns the amount of bytes not taken from the input buffer.
 */
@@ -124,6 +528,7 @@ MimeDataProvider::collect_input_lines(const char *input, size_t insize)
   size_t pos = 0;
   size_t nleft = insize;
   size_t not_taken = nleft;
+  size_t len = 0;
 
   /* Split the raw data into lines */
   for (; nleft; nleft--, s++)
@@ -146,7 +551,10 @@ MimeDataProvider::collect_input_lines(const char *input, size_t insize)
               pos--;
             }
 
-          if (rfc822parse_insert (m_parser,
+          log_mime_parser("%s:%s: Parsing line=`%.*s'\n",
+                          SRCNAME, __func__, (int)pos, linebuf);
+          /* Check the next state */
+          if (rfc822parse_insert (m_mime_ctx->msg,
                                   (unsigned char*) linebuf,
                                   pos))
             {
@@ -154,11 +562,107 @@ MimeDataProvider::collect_input_lines(const char *input, size_t insize)
                          SRCNAME, __func__, strerror (errno));
               return not_taken;
             }
+
           /* If we are currently in a collecting state actually
              collect that line */
-          if (m_collect)
+          if (m_mime_ctx->collect_signeddata)
             {
-              decode_and_collect (linebuf, pos);
+              /* Save the signed data.  Note that we need to delay
+                 the CR/LF because the last line ending belongs to the
+                 next boundary. */
+              if (m_mime_ctx->collect_signeddata == 2)
+                {
+                  m_crypto_data.write ("\r\n", 2);
+                }
+              log_debug ("Writing signeddata: %s pos: %i", linebuf, pos);
+              m_crypto_data.write (linebuf, pos);
+              m_mime_ctx->collect_signeddata = 2;
+            }
+          if (m_mime_ctx->in_data && m_mime_ctx->collect_attachment)
+            {
+              /* We are inside of an attachment part.  Write it out. */
+              if (m_mime_ctx->collect_attachment == 1)  /* Skip the first line. */
+                m_mime_ctx->collect_attachment = 2;
+
+              int slbrk = 0;
+              if (m_mime_ctx->is_qp_encoded)
+                len = qp_decode (linebuf, pos, &slbrk);
+              else if (m_mime_ctx->is_base64_encoded)
+                len = b64_decode (&m_mime_ctx->base64, linebuf, pos);
+              else
+                len = pos;
+
+              if (m_mime_ctx->collect_body)
+                {
+                  m_body += std::string(linebuf, len);
+                  if (!m_mime_ctx->is_base64_encoded && !slbrk)
+                    {
+                      m_body += "\r\n";
+                    }
+                }
+              else if (m_mime_ctx->collect_html_body)
+                {
+                  m_html_body += std::string(linebuf, len);
+                  if (!m_mime_ctx->is_base64_encoded && !slbrk)
+                    {
+                      m_body += "\r\n";
+                    }
+                }
+              else if (m_mime_ctx->current_attachment && len)
+                {
+                  m_mime_ctx->current_attachment->write(linebuf, len);
+                  if (!m_mime_ctx->is_base64_encoded && !slbrk)
+                    {
+                      m_mime_ctx->current_attachment->write("\r\n", 2);
+                    }
+                }
+              else
+                {
+                  log_mime_parser ("%s:%s Collecting ended / failed.",
+                                   SRCNAME, __func__);
+                }
+            }
+          else if (m_mime_ctx->in_data && m_mime_ctx->collect_signature)
+            {
+              /* We are inside of a signature attachment part.  */
+              if (m_mime_ctx->collect_signature == 1)  /* Skip the first line. */
+                m_mime_ctx->collect_signature = 2;
+              else
+                {
+                  int slbrk = 0;
+
+                  if (m_mime_ctx->is_qp_encoded)
+                    len = qp_decode (linebuf, pos, &slbrk);
+                  else if (m_mime_ctx->is_base64_encoded)
+                    len = b64_decode (&m_mime_ctx->base64, linebuf, pos);
+                  else
+                    len = pos;
+                  if (!m_signature)
+                    {
+                      m_signature = new GpgME::Data();
+                    }
+                  if (len)
+                    m_signature->write(linebuf, len);
+                  if (!m_mime_ctx->is_base64_encoded && !slbrk)
+                    m_signature->write("\r\n", 2);
+                }
+            }
+          else if (m_mime_ctx->in_data)
+            {
+              /* We are inside the data.  That should be the actual
+                 ciphertext in the given encoding. */
+              int slbrk = 0;
+
+              if (m_mime_ctx->is_qp_encoded)
+                len = qp_decode (linebuf, pos, &slbrk);
+              else if (m_mime_ctx->is_base64_encoded)
+                len = b64_decode (&m_mime_ctx->base64, linebuf, pos);
+              else
+                len = pos;
+              if (len)
+                m_crypto_data.write(linebuf, len);
+              if (!m_mime_ctx->is_base64_encoded && !slbrk)
+                m_crypto_data.write("\r\n", 2);
             }
           /* Continue with next line. */
           pos = 0;
@@ -206,8 +710,63 @@ MimeDataProvider::collect_data(LPSTREAM stream)
     }
 }
 
+ssize_t MimeDataProvider::write(const void *buffer, size_t bufSize)
+{
+    m_rawbuf += std::string ((const char*)buffer, bufSize);
+    size_t not_taken = collect_input_lines (m_rawbuf.c_str(),
+                                            m_rawbuf.size());
+
+    if (not_taken == m_rawbuf.size())
+      {
+        log_error ("%s:%s: Write failed to consume anything.\n"
+                   "Buffer too small?",
+                   SRCNAME, __func__);
+        return bufSize;
+      }
+    log_mime_parser ("%s:%s: Write Consumed: " SIZE_T_FORMAT " bytes",
+                     SRCNAME, __func__, m_rawbuf.size() - not_taken);
+    m_rawbuf.erase (0, m_rawbuf.size() - not_taken);
+    return bufSize;
+}
+
 off_t
 MimeDataProvider::seek(off_t offset, int whence)
 {
-  return m_data.seek (offset, whence);
+  return m_crypto_data.seek (offset, whence);
+}
+
+GpgME::Data *
+MimeDataProvider::signature() const
+{
+  return m_signature;
+}
+
+std::shared_ptr<Attachment>
+MimeDataProvider::create_attachment()
+{
+  log_mime_parser ("%s:%s: Creating attachment.",
+                   SRCNAME, __func__);
+
+  auto attach = std::shared_ptr<Attachment> (new Attachment());
+  attach->set_attach_type (ATTACHTYPE_FROMMOSS);
+  m_mime_ctx->any_attachments_created = 1;
+
+  /* And now for the real name.  We avoid storing the name "smime.p7m"
+     because that one is used at several places in the mapi conversion
+     functions.  */
+  if (m_mime_ctx->mimestruct_cur && m_mime_ctx->mimestruct_cur->filename)
+    {
+      if (!strcmp (m_mime_ctx->mimestruct_cur->filename, "smime.p7m"))
+        {
+          attach->set_display_name ("x-smime.p7m");
+        }
+      else
+        {
+          attach->set_display_name (m_mime_ctx->mimestruct_cur->filename);
+        }
+    }
+  m_attachments.push_back (attach);
+
+  return attach;
+  /* TODO handle encoding */
 }
diff --git a/src/mimedataprovider.h b/src/mimedataprovider.h
index 8b9d8ee..9722d00 100644
--- a/src/mimedataprovider.h
+++ b/src/mimedataprovider.h
@@ -26,19 +26,38 @@
 #include "rfc822parse.h"
 
 #include <string>
+struct mime_context;
+typedef struct mime_context *mime_context_t;
+class Attachment;
 
 /** This class does simple one level mime parsing to find crypto
   data.
 
   Use the mimedataprovider on a body or attachment stream. It
   will do the conversion from MIME to PGP / CMS data on the fly.
+  Similarly when writing it will split up the data into a body /
+  html body and attachments.
 
-  The raw mime data from the underlying stream is "collected" and
-  parsed into Crypto data which is then buffered in "databuf".
+  A detached signature will be made available through the
+  signature function.
+
+  When reading the raw mime data from the underlying stream is
+  "collected" and parsed into crypto data which is then
+  buffered in an internal gpgme data stucture.
+
+  For historicial reasons this class both provides reading
+  and writing to be able to reuse the same mimeparser code.
+  Similarly using the C-Style parsing code is for historic
+  reason because as this class was created to have a data
+  container unrelated of the Outlook Object model (after
+  creation) the mimeparser code already existed and was
+  stable.
 */
 class MimeDataProvider : public GpgME::DataProvider
 {
 public:
+  /* Create an empty dataprovider, useful for writing to. */
+  MimeDataProvider();
   /* Read and parse the stream. Does not hold a reference
      to the stream but releases it after read. */
   MimeDataProvider(LPSTREAM stream);
@@ -51,33 +70,53 @@ public:
     the conversion code interanally to convert mime
     data into PGP/CMS Data that GpgME can work with. */
   ssize_t read(void *buffer, size_t bufSize);
-  ssize_t write(const void *buffer, size_t bufSize) {
-      (void)buffer; (void)bufSize; return -1;
-  }
+
+  ssize_t write(const void *buffer, size_t bufSize);
+
   /* Seek the underlying stream. This discards the internal
      buffers as the offset is not mapped. Should not really
      be used but can be used to reset the DataProvider. */
   off_t seek(off_t offset, int whence);
+
   /* Noop */
   void release() {}
 
-  /* The the data of the signature part. */
-  const GpgME::Data &get_signature_data();
+  /* The the data of the signature part.
+
+     If not null then this is a pointer to the signature
+     data that is valid for the lifetime of this object.
+  */
+  GpgME::Data *signature() const;
+
+  /* Add an attachment to the list */
+  std::shared_ptr<Attachment> create_attachment();
+
+  mime_context_t mime_context() {return m_mime_ctx;}
+
+  const std::string get_body() const {return m_body;}
+  const std::string get_html_body() const {return m_html_body;}
+  const std::vector <std::shared_ptr<Attachment> > get_attachments() const
+    {return m_attachments;}
 private:
   /* Collect the crypto data from mime. */
   void collect_data(LPSTREAM stream);
   /* Collect a single line. */
   size_t collect_input_lines(const char *input, size_t size);
-  /* Move actual data into the databuffer. */
-  void decode_and_collect(char *line, size_t pos);
-  enum Encoding {None, Base64, Quoted};
+  /* A detached signature found in the input */
   std::string m_sig_data;
-  GpgME::Data m_data;
-  GpgME::Data m_signature;
+  /* The data to be passed to the crypto operation */
+  GpgME::Data m_crypto_data;
+  /* The plaintext body. */
+  std::string m_body;
+  /* The plaintext html body. */
+  std::string m_html_body;
+  /* A detachted signature found in the mail */
+  GpgME::Data *m_signature;
+  /* Internal helper to read line based */
   std::string m_rawbuf;
-  bool m_collect;
-  rfc822parse_t m_parser;
-  Encoding m_current_encoding;
-  b64_state_t m_base64_context;
+  /* The mime context */
+  mime_context_t m_mime_ctx;
+  /* List of attachments. */
+  std::vector<std::shared_ptr<Attachment> > m_attachments;
 };
 #endif // MIMEDATAPROVIDER_H

commit 4229a56e2e3b1018cafec9a833eb36d948041f51
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Wed Sep 14 14:16:30 2016 +0200

    Fix c++ linkage of rfc2047parse
    
    * src/rfc2047parse.h: Add Include Guard and c++ linkage.

diff --git a/src/rfc2047parse.h b/src/rfc2047parse.h
index 593b6fd..f5c9af5 100644
--- a/src/rfc2047parse.h
+++ b/src/rfc2047parse.h
@@ -1,3 +1,5 @@
+#ifndef SRC_RFC2047PARSE_H
+#define SRC_RFC2047PARSE_H
 /* @file rfc2047parse.h
  * @brief Parser for filenames encoded according to rfc2047
  *
@@ -19,6 +21,13 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#ifdef __cplusplus
+extern "C" {
+#if 0
+}
+#endif
+#endif
+
 /** @brief Try to parse a string according to rfc2047.
   *
   * On error the error is logged and a copy of the original
@@ -29,3 +38,7 @@
   */
 char *
 rfc2047_parse (const char *input);
+#ifdef __cplusplus
+}
+#endif
+#endif // SRC_RFC2047PARSE_H

-----------------------------------------------------------------------

Summary of changes:
 src/mail.cpp             |  10 +-
 src/mailparser.cpp       |  82 ++++--
 src/mailparser.h         |  18 +-
 src/mapihelp.cpp         |   4 +-
 src/mimedataprovider.cpp | 635 ++++++++++++++++++++++++++++++++++++++++++++---
 src/mimedataprovider.h   |  71 ++++--
 src/rfc2047parse.h       |  13 +
 7 files changed, 738 insertions(+), 95 deletions(-)


hooks/post-receive
-- 
GnuPG extension for MS Outlook
http://git.gnupg.org




More information about the Gnupg-commits mailing list