tools: Change mime-maker to write out CR,LF.
authorWerner Koch <wk@gnupg.org>
Thu, 29 Sep 2016 10:29:27 +0000 (12:29 +0200)
committerWerner Koch <wk@gnupg.org>
Thu, 29 Sep 2016 15:56:37 +0000 (17:56 +0200)
* tools/mime-maker.c (struct part_s): Add field PARTID.
(struct mime_maker_context_s): Add field PARTID_COUNTER.
(dump_parts): Print part ids.
(mime_maker_add_header): Assign PARTID.
(mime_maker_add_container): Ditto.
(mime_maker_get_partid): New.
(write_ct_with_boundary): Remove.
(add_header): Strip trailing white spaces.
(write_header): Remove trailing spaces trimming.  Add arg BOUNDARY.
Handle emdedded LFs.
(write_gap, write_boundary, write_body): New.
(write_tree): Use new functions.
--

These changes prepare for forthcoming enhancements.

Signed-off-by: Werner Koch <wk@gnupg.org>
tools/mime-maker.c
tools/mime-maker.h

index 07783d9..99185cf 100644 (file)
@@ -54,6 +54,7 @@ struct part_s
   size_t bodylen;       /* Length of BODY.   */
   char *body;           /* Malloced buffer with the body.  This is the
                          * non-encoded value.  */
+  unsigned int partid;   /* The part ID.  */
 };
 typedef struct part_s *part_t;
 
@@ -70,6 +71,8 @@ struct mime_maker_context_s
   part_t mail;                 /* The MIME tree.  */
   part_t current_part;
 
+  unsigned int partid_counter; /* Counter assign part ids.  */
+
   int boundary_counter;  /* Used to create easy to read boundaries.  */
   char *boundary_suffix; /* Random string used in the boundaries.  */
 
@@ -159,7 +162,7 @@ dump_parts (part_t part, int level)
 
   for (; part; part = part->next)
     {
-      log_debug ("%*s[part]\n", level*2, "");
+      log_debug ("%*s[part %u]\n", level*2, "", part->partid);
       for (hdr = part->headers; hdr; hdr = hdr->next)
         {
           log_debug ("%*s%s: %s\n", level*2, "", hdr->name, hdr->value);
@@ -300,6 +303,7 @@ add_header (part_t part, const char *name, const char *value)
   header_t hdr;
   size_t namelen;
   const char *s;
+  char *p;
 
   if (!value)
     {
@@ -338,6 +342,18 @@ add_header (part_t part, const char *name, const char *value)
       return err;
     }
 
+  for (p = hdr->value + strlen (hdr->value) - 1;
+       (p >= hdr->value
+        && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'));
+       p--)
+    *p = 0;
+  if (!(p >= hdr->value))
+    {
+      xfree (hdr->value);
+      xfree (hdr);
+      return gpg_error (GPG_ERR_INV_VALUE);  /* Only spaces.  */
+    }
+
   if (part)
     {
       *part->headers_tail = hdr;
@@ -390,6 +406,7 @@ mime_maker_add_header (mime_maker_t ctx, const char *name, const char *value)
       part = xtrycalloc (1, sizeof *part);
       if (!part)
         return gpg_error_from_syserror ();
+      part->partid = ++ctx->partid_counter;
       part->headers_tail = &part->headers;
       log_assert (!ctx->current_part->next);
       ctx->current_part->next = part;
@@ -507,6 +524,7 @@ mime_maker_add_container (mime_maker_t ctx)
     }
 
   part = part->child;
+  part->partid = ++ctx->partid_counter;
   ctx->current_part = part;
 
   return 0;
@@ -532,31 +550,79 @@ mime_maker_end_container (mime_maker_t ctx)
 }
 
 
-/* Write the Content-Type header with the boundary value.  */
+/* Return the part-ID of the current part. */
+unsigned int
+mime_maker_get_partid (mime_maker_t ctx)
+{
+  if (ensure_part (ctx, NULL))
+    return 0; /* Ooops.  */
+  return ctx->current_part->partid;
+}
+
+
+/* Write a header and handle emdedded LFs.  If BOUNDARY is not NULL it
+ * is appended to the value.  */
+/* Fixme: Add automatic line wrapping.  */
 static gpg_error_t
-write_ct_with_boundary (mime_maker_t ctx,
-                        const char *value, const char *boundary)
+write_header (mime_maker_t ctx, const char *name, const char *value,
+              const char *boundary)
 {
   const char *s;
 
-  if (!*value)
-    return gpg_error (GPG_ERR_INV_VALUE);  /* Empty string.  */
-
-  for (s=value + strlen (value) - 1;
-       (s >= value
-        && (*s == ' ' || *s == '\t' || *s == '\n'));
-       s--)
-    ;
-  if (!(s >= value))
-    return gpg_error (GPG_ERR_INV_VALUE);  /* Only spaces.  */
-
-  /* Fixme: We should use a dedicated header write functions which
-   * properly wraps the header.  */
-  es_fprintf (ctx->outfp, "Content-Type: %s%s\n\tboundary=\"%s\"\n",
-              value,
-              (*s == ';')? "":";",
-              boundary);
-  return 0;
+  es_fprintf (ctx->outfp, "%s: ", name);
+
+  /* Note that add_header made sure that VALUE does not end with a LF.
+   * Thus we can assume that a LF is followed by non-whitespace.  */
+  for (s = value; *s; s++)
+    {
+      if (*s == '\n')
+        es_fputs ("\r\n\t", ctx->outfp);
+      else
+        es_fputc (*s, ctx->outfp);
+    }
+  if (boundary)
+    {
+      if (s > value && s[-1] != ';')
+        es_fputc (';', ctx->outfp);
+      es_fprintf (ctx->outfp, "\r\n\tboundary=\"%s\"", boundary);
+    }
+
+  es_fputs ("\r\n", ctx->outfp);
+
+  return es_ferror (ctx->outfp)? gpg_error_from_syserror () : 0;
+}
+
+
+static gpg_error_t
+write_gap (mime_maker_t ctx)
+{
+  es_fputs ("\r\n", ctx->outfp);
+  return es_ferror (ctx->outfp)? gpg_error_from_syserror () : 0;
+}
+
+
+static gpg_error_t
+write_boundary (mime_maker_t ctx, const char *boundary, int last)
+{
+  es_fprintf (ctx->outfp, "\r\n--%s%s\r\n", boundary, last?"--":"");
+  return es_ferror (ctx->outfp)? gpg_error_from_syserror () : 0;
+}
+
+
+/* Fixme: Apply required encoding.  */
+static gpg_error_t
+write_body (mime_maker_t ctx, const void *body, size_t bodylen)
+{
+  const char *s;
+
+  for (s = body; bodylen; s++, bodylen--)
+    {
+      if (*s == '\n' && !(s > (const char *)body && s[-1] == '\r'))
+        es_fputc ('\r', ctx->outfp);
+      es_fputc (*s, ctx->outfp);
+    }
+
+  return es_ferror (ctx->outfp)? gpg_error_from_syserror () : 0;
 }
 
 
@@ -572,33 +638,39 @@ write_tree (mime_maker_t ctx, part_t parent, part_t part)
       for (hdr = part->headers; hdr; hdr = hdr->next)
         {
           if (part->child && !strcmp (hdr->name, "Content-Type"))
-            write_ct_with_boundary (ctx, hdr->value, part->boundary);
+            err = write_header (ctx, hdr->name, hdr->value, part->boundary);
           else
-            es_fprintf (ctx->outfp, "%s: %s\n", hdr->name, hdr->value);
+            err = write_header (ctx, hdr->name, hdr->value, NULL);
+          if (err)
+            return err;
         }
-      es_fputc ('\n', ctx->outfp);
+      err = write_gap (ctx);
+      if (err)
+        return err;
       if (part->body)
         {
-          if (es_write (ctx->outfp, part->body, part->bodylen, NULL))
-            return gpg_error_from_syserror ();
+          err = write_body (ctx, part->body, part->bodylen);
+          if (err)
+            return err;
         }
       if (part->child)
         {
           log_assert (part->boundary);
-          if (es_fprintf (ctx->outfp, "\n--%s\n", part->boundary) < 0)
-            return gpg_error_from_syserror ();
-          err = write_tree (ctx, part, part->child);
+          err = write_boundary (ctx, part->boundary, 0);
+          if (!err)
+            err = write_tree (ctx, part, part->child);
+          if (!err)
+            err = write_boundary (ctx, part->boundary, 1);
           if (err)
             return err;
-          if (es_fprintf (ctx->outfp, "\n--%s--\n", part->boundary) < 0)
-            return gpg_error_from_syserror ();
         }
 
       if (part->next)
         {
           log_assert (parent && parent->boundary);
-          if (es_fprintf (ctx->outfp, "\n--%s\n", parent->boundary) < 0)
-            return gpg_error_from_syserror ();
+          err = write_boundary (ctx, parent->boundary, 0);
+          if (err)
+            return err;
         }
     }
   return 0;
index 39752db..2fac9c3 100644 (file)
@@ -36,6 +36,7 @@ gpg_error_t mime_maker_add_body (mime_maker_t ctx, const char *string);
 gpg_error_t mime_maker_add_stream (mime_maker_t ctx, estream_t *stream_addr);
 gpg_error_t mime_maker_add_container (mime_maker_t ctx);
 gpg_error_t mime_maker_end_container (mime_maker_t ctx);
+unsigned int mime_maker_get_partid (mime_maker_t ctx);
 
 gpg_error_t mime_maker_make (mime_maker_t ctx, estream_t fp);