LCOV - code coverage report
Current view: top level - tools - mime-maker.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 323 0.0 %
Date: 2016-11-29 15:00:56 Functions: 0 28 0.0 %

          Line data    Source code
       1             : /* mime-maker.c - Create MIME structures
       2             :  * Copyright (C) 2016 g10 Code GmbH
       3             :  *
       4             :  * This file is part of GnuPG.
       5             :  *
       6             :  * GnuPG is free software; you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation; either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * GnuPG is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  * GNU General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License
      17             :  * along with this program; if not, see <https://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #include <config.h>
      21             : #include <stdio.h>
      22             : #include <stdlib.h>
      23             : #include <string.h>
      24             : 
      25             : #include "util.h"
      26             : #include "zb32.h"
      27             : #include "mime-maker.h"
      28             : 
      29             : 
      30             : /* All valid characters in a header name.  */
      31             : #define HEADER_NAME_CHARS  ("abcdefghijklmnopqrstuvwxyz" \
      32             :                             "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
      33             :                             "-01234567890")
      34             : 
      35             : /* An object to store an header.  Also used for a list of headers.  */
      36             : struct header_s
      37             : {
      38             :   struct header_s *next;
      39             :   char *value;   /* Malloced value.  */
      40             :   char name[1]; /* Name.  */
      41             : };
      42             : typedef struct header_s *header_t;
      43             : 
      44             : 
      45             : /* An object to store a MIME part.  A part is the header plus the
      46             :  * content (body). */
      47             : struct part_s
      48             : {
      49             :   struct part_s *next;  /* Next part in the current container.  */
      50             :   struct part_s *child; /* Child container.  */
      51             :   char *boundary;       /* Malloced boundary string.  */
      52             :   header_t headers;     /* List of headers.  */
      53             :   header_t *headers_tail;/* Address of last header in chain.  */
      54             :   size_t bodylen;       /* Length of BODY.   */
      55             :   char *body;           /* Malloced buffer with the body.  This is the
      56             :                          * non-encoded value.  */
      57             :   unsigned int partid;   /* The part ID.  */
      58             : };
      59             : typedef struct part_s *part_t;
      60             : 
      61             : 
      62             : 
      63             : /* Definition of the mime parser object.  */
      64             : struct mime_maker_context_s
      65             : {
      66             :   void *cookie;                /* Cookie passed to all callbacks.  */
      67             : 
      68             :   unsigned int verbose:1;      /* Enable verbose mode.  */
      69             :   unsigned int debug:1;        /* Enable debug mode.  */
      70             : 
      71             :   part_t mail;                 /* The MIME tree.  */
      72             :   part_t current_part;
      73             : 
      74             :   unsigned int partid_counter; /* Counter assign part ids.  */
      75             : 
      76             :   int boundary_counter;  /* Used to create easy to read boundaries.  */
      77             :   char *boundary_suffix; /* Random string used in the boundaries.  */
      78             : 
      79             :   struct b64state *b64state;     /* NULL or malloced Base64 decoder state.  */
      80             : 
      81             :   /* Helper to convey the output stream to recursive functions. */
      82             :   estream_t outfp;
      83             : };
      84             : 
      85             : 
      86             : /* Create a new mime make object.  COOKIE is a values woich will be
      87             :  * used as first argument for all callbacks registered with this
      88             :  * object.  */
      89             : gpg_error_t
      90           0 : mime_maker_new (mime_maker_t *r_maker, void *cookie)
      91             : {
      92             :   mime_maker_t ctx;
      93             : 
      94           0 :   *r_maker = NULL;
      95             : 
      96           0 :   ctx = xtrycalloc (1, sizeof *ctx);
      97           0 :   if (!ctx)
      98           0 :     return gpg_error_from_syserror ();
      99           0 :   ctx->cookie = cookie;
     100             : 
     101           0 :   *r_maker = ctx;
     102           0 :   return 0;
     103             : }
     104             : 
     105             : 
     106             : static void
     107           0 : release_parts (part_t part)
     108             : {
     109           0 :   while (part)
     110             :     {
     111           0 :       part_t partnext = part->next;
     112           0 :       while (part->headers)
     113             :         {
     114           0 :           header_t hdrnext = part->headers->next;
     115           0 :           xfree (part->headers);
     116           0 :           part->headers = hdrnext;
     117             :         }
     118           0 :       release_parts (part->child);
     119           0 :       xfree (part->boundary);
     120           0 :       xfree (part->body);
     121           0 :       xfree (part);
     122           0 :       part = partnext;
     123             :     }
     124           0 : }
     125             : 
     126             : 
     127             : /* Release a mime maker object.  */
     128             : void
     129           0 : mime_maker_release (mime_maker_t ctx)
     130             : {
     131           0 :   if (!ctx)
     132           0 :     return;
     133             : 
     134           0 :   release_parts (ctx->mail);
     135           0 :   xfree (ctx->boundary_suffix);
     136           0 :   xfree (ctx);
     137             : }
     138             : 
     139             : 
     140             : /* Set verbose and debug mode.  */
     141             : void
     142           0 : mime_maker_set_verbose (mime_maker_t ctx, int level)
     143             : {
     144           0 :   if (!level)
     145             :     {
     146           0 :       ctx->verbose = 0;
     147           0 :       ctx->debug = 0;
     148             :     }
     149             :   else
     150             :     {
     151           0 :       ctx->verbose = 1;
     152           0 :       if (level > 10)
     153           0 :         ctx->debug = 1;
     154             :     }
     155           0 : }
     156             : 
     157             : 
     158             : static void
     159           0 : dump_parts (part_t part, int level)
     160             : {
     161             :   header_t hdr;
     162             : 
     163           0 :   for (; part; part = part->next)
     164             :     {
     165           0 :       log_debug ("%*s[part %u]\n", level*2, "", part->partid);
     166           0 :       for (hdr = part->headers; hdr; hdr = hdr->next)
     167             :         {
     168           0 :           log_debug ("%*s%s: %s\n", level*2, "", hdr->name, hdr->value);
     169             :         }
     170           0 :       if (part->body)
     171           0 :         log_debug ("%*s[body %zu bytes]\n", level*2, "", part->bodylen);
     172           0 :       if (part->child)
     173             :         {
     174           0 :           log_debug ("%*s[container]\n", level*2, "");
     175           0 :           dump_parts (part->child, level+1);
     176             :         }
     177             :     }
     178           0 : }
     179             : 
     180             : 
     181             : /* Dump the mime tree for debugging.  */
     182             : void
     183           0 : mime_maker_dump_tree (mime_maker_t ctx)
     184             : {
     185           0 :   dump_parts (ctx->mail, 0);
     186           0 : }
     187             : 
     188             : 
     189             : /* Find the parent node for NEEDLE starting at ROOT.  */
     190             : static part_t
     191           0 : find_parent (part_t root, part_t needle)
     192             : {
     193             :   part_t node, n;
     194             : 
     195           0 :   for (node = root->child; node; node = node->next)
     196             :     {
     197           0 :       if (node == needle)
     198           0 :         return root;
     199           0 :       if ((n = find_parent (node, needle)))
     200           0 :         return n;
     201             :     }
     202           0 :   return NULL;
     203             : }
     204             : 
     205             : /* Find the part node from the PARTID.  */
     206             : static part_t
     207           0 : find_part (part_t root, unsigned int partid)
     208             : {
     209             :   part_t node, n;
     210             : 
     211           0 :   for (node = root->child; node; node = node->next)
     212             :     {
     213           0 :       if (node->partid == partid)
     214           0 :         return root;
     215           0 :       if ((n = find_part (node, partid)))
     216           0 :         return n;
     217             :     }
     218           0 :   return NULL;
     219             : }
     220             : 
     221             : 
     222             : /* Create a boundary string.  Outr codes is aware of the general
     223             :  * structure of that string (gebins with "=-=") so that
     224             :  * it can protect against accidentally-used boundaries within the
     225             :  * content.   */
     226             : static char *
     227           0 : generate_boundary (mime_maker_t ctx)
     228             : {
     229           0 :   if (!ctx->boundary_suffix)
     230             :     {
     231             :       char buffer[12];
     232             : 
     233           0 :       gcry_create_nonce (buffer, sizeof buffer);
     234           0 :       ctx->boundary_suffix = zb32_encode (buffer, 8 * sizeof buffer);
     235           0 :       if (!ctx->boundary_suffix)
     236           0 :         return NULL;
     237             :     }
     238             : 
     239           0 :   ctx->boundary_counter++;
     240           0 :   return es_bsprintf ("=-=%02d-%s=-=",
     241             :                       ctx->boundary_counter, ctx->boundary_suffix);
     242             : }
     243             : 
     244             : 
     245             : /* Ensure that the context has a MAIL and CURRENT_PART object and
     246             :  * return the parent object if available  */
     247             : static gpg_error_t
     248           0 : ensure_part (mime_maker_t ctx, part_t *r_parent)
     249             : {
     250           0 :   if (!ctx->mail)
     251             :     {
     252           0 :       ctx->mail = xtrycalloc (1, sizeof *ctx->mail);
     253           0 :       if (!ctx->mail)
     254           0 :         return gpg_error_from_syserror ();
     255           0 :       log_assert (!ctx->current_part);
     256           0 :       ctx->current_part = ctx->mail;
     257           0 :       ctx->current_part->headers_tail = &ctx->current_part->headers;
     258             :     }
     259           0 :   log_assert (ctx->current_part);
     260           0 :   if (r_parent)
     261           0 :     *r_parent = find_parent (ctx->mail, ctx->current_part);
     262             : 
     263           0 :   return 0;
     264             : }
     265             : 
     266             : 
     267             : /* Transform a header name into a standard capitalized format.
     268             :  * "Content-Type".  Conversion stops at the colon. */
     269             : static void
     270           0 : capitalize_header_name (char *name)
     271             : {
     272           0 :   unsigned char *p = name;
     273           0 :   int first = 1;
     274             : 
     275             :   /* Special cases first.  */
     276           0 :   if (!ascii_strcasecmp (name, "MIME-Version"))
     277             :     {
     278           0 :       strcpy (name, "MIME-Version");
     279           0 :       return;
     280             :     }
     281             : 
     282             :   /* Regular cases.  */
     283           0 :   for (; *p && *p != ':'; p++)
     284             :     {
     285           0 :       if (*p == '-')
     286           0 :         first = 1;
     287           0 :       else if (first)
     288             :         {
     289           0 :           if (*p >= 'a' && *p <= 'z')
     290           0 :             *p = *p - 'a' + 'A';
     291           0 :           first = 0;
     292             :         }
     293           0 :       else if (*p >= 'A' && *p <= 'Z')
     294           0 :         *p = *p - 'A' + 'a';
     295             :     }
     296             : }
     297             : 
     298             : 
     299             : /* Check whether a header with NAME has already been set into PART.
     300             :  * NAME must be in canonical capitalized format.  Return true or
     301             :  * false. */
     302             : static int
     303           0 : have_header (part_t part, const char *name)
     304             : {
     305             :   header_t hdr;
     306             : 
     307           0 :   for (hdr = part->headers; hdr; hdr = hdr->next)
     308           0 :     if (!strcmp (hdr->name, name))
     309           0 :       return 1;
     310           0 :   return 0;
     311             : }
     312             : 
     313             : 
     314             : /* Helper to add a header to a part.  */
     315             : static gpg_error_t
     316           0 : add_header (part_t part, const char *name, const char *value)
     317             : {
     318             :   gpg_error_t err;
     319             :   header_t hdr;
     320             :   size_t namelen;
     321             :   const char *s;
     322             :   char *p;
     323             : 
     324           0 :   if (!value)
     325             :     {
     326           0 :       s = strchr (name, '=');
     327           0 :       if (!s)
     328           0 :         return gpg_error (GPG_ERR_INV_ARG);
     329           0 :       namelen = s - name;
     330           0 :       value = s+1;
     331             :     }
     332             :   else
     333           0 :     namelen = strlen (name);
     334             : 
     335           0 :   hdr = xtrymalloc (sizeof *hdr + namelen);
     336           0 :   if (!hdr)
     337           0 :     return gpg_error_from_syserror ();
     338           0 :   hdr->next = NULL;
     339           0 :   memcpy (hdr->name, name, namelen);
     340           0 :   hdr->name[namelen] = 0;
     341             : 
     342             :   /* Check that the header name is valid.  We allow all lower and
     343             :    * uppercase letters and, except for the first character, digits and
     344             :    * the dash.  */
     345           0 :   if (strspn (hdr->name, HEADER_NAME_CHARS) != namelen
     346           0 :       || strchr ("-0123456789", *hdr->name))
     347             :     {
     348           0 :       xfree (hdr);
     349           0 :       return gpg_error (GPG_ERR_INV_NAME);
     350             :     }
     351             : 
     352           0 :   capitalize_header_name (hdr->name);
     353           0 :   hdr->value = xtrystrdup (value);
     354           0 :   if (!hdr->value)
     355             :     {
     356           0 :       err = gpg_error_from_syserror ();
     357           0 :       xfree (hdr);
     358           0 :       return err;
     359             :     }
     360             : 
     361           0 :   for (p = hdr->value + strlen (hdr->value) - 1;
     362           0 :        (p >= hdr->value
     363           0 :         && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'));
     364           0 :        p--)
     365           0 :     *p = 0;
     366           0 :   if (!(p >= hdr->value))
     367             :     {
     368           0 :       xfree (hdr->value);
     369           0 :       xfree (hdr);
     370           0 :       return gpg_error (GPG_ERR_INV_VALUE);  /* Only spaces.  */
     371             :     }
     372             : 
     373           0 :   if (part)
     374             :     {
     375           0 :       *part->headers_tail = hdr;
     376           0 :       part->headers_tail = &hdr->next;
     377             :     }
     378             :   else
     379           0 :     xfree (hdr);
     380             : 
     381           0 :   return 0;
     382             : }
     383             : 
     384             : 
     385             : /* Add a header with NAME and VALUE to the current mail.  A LF in the
     386             :  * VALUE will be handled automagically.  If NULL is used for VALUE it
     387             :  * is expected that the NAME has the format "NAME=VALUE" and VALUE is
     388             :  * taken from there.
     389             :  *
     390             :  * If no container has been added, the header will be used for the
     391             :  * regular mail headers and not for a MIME part.  If the current part
     392             :  * is in a container and a body has been added, we append a new part
     393             :  * to the current container.  Thus for a non-MIME mail the caller
     394             :  * needs to call this function followed by a call to add a body.  When
     395             :  * adding a Content-Type the boundary parameter must not be included.
     396             :  */
     397             : gpg_error_t
     398           0 : mime_maker_add_header (mime_maker_t ctx, const char *name, const char *value)
     399             : {
     400             :   gpg_error_t err;
     401             :   part_t part, parent;
     402             : 
     403             :   /* Hack to use this function for a syntax check of NAME and VALUE.  */
     404           0 :   if (!ctx)
     405           0 :     return add_header (NULL, name, value);
     406             : 
     407           0 :   err = ensure_part (ctx, &parent);
     408           0 :   if (err)
     409           0 :     return err;
     410           0 :   part = ctx->current_part;
     411             : 
     412           0 :   if ((part->body || part->child) && !parent)
     413             :     {
     414             :       /* We already have a body but no parent.  Adding another part is
     415             :        * thus not possible.  */
     416           0 :       return gpg_error (GPG_ERR_CONFLICT);
     417             :     }
     418           0 :   if (part->body || part->child)
     419             :     {
     420             :       /* We already have a body and there is a parent.  We now append
     421             :        * a new part to the current container.  */
     422           0 :       part = xtrycalloc (1, sizeof *part);
     423           0 :       if (!part)
     424           0 :         return gpg_error_from_syserror ();
     425           0 :       part->partid = ++ctx->partid_counter;
     426           0 :       part->headers_tail = &part->headers;
     427           0 :       log_assert (!ctx->current_part->next);
     428           0 :       ctx->current_part->next = part;
     429           0 :       ctx->current_part = part;
     430             :     }
     431             : 
     432             :   /* If no NAME and no VALUE has been given we do not add a header.
     433             :    * This can be used to create a new part without any header.  */
     434           0 :   if (!name && !value)
     435           0 :     return 0;
     436             : 
     437             :   /* If we add Content-Type, make sure that we have a MIME-version
     438             :    * header first; this simply looks better.  */
     439           0 :   if (!ascii_strcasecmp (name, "Content-Type")
     440           0 :       && !have_header (ctx->mail, "MIME-Version"))
     441             :     {
     442           0 :       err = add_header (ctx->mail, "MIME-Version", "1.0");
     443           0 :       if (err)
     444           0 :         return err;
     445             :     }
     446           0 :   return add_header (part, name, value);
     447             : }
     448             : 
     449             : 
     450             : /* Helper for mime_maker_add_{body,stream}.  */
     451             : static gpg_error_t
     452           0 : add_body (mime_maker_t ctx, const void *data, size_t datalen)
     453             : {
     454             :   gpg_error_t err;
     455             :   part_t part, parent;
     456             : 
     457           0 :   err = ensure_part (ctx, &parent);
     458           0 :   if (err)
     459           0 :     return err;
     460           0 :   part = ctx->current_part;
     461           0 :   if (part->body)
     462           0 :     return gpg_error (GPG_ERR_CONFLICT);
     463             : 
     464           0 :   part->body = xtrymalloc (datalen? datalen : 1);
     465           0 :   if (!part->body)
     466           0 :     return gpg_error_from_syserror ();
     467           0 :   part->bodylen = datalen;
     468           0 :   if (data)
     469           0 :     memcpy (part->body, data, datalen);
     470             : 
     471           0 :   return 0;
     472             : }
     473             : 
     474             : 
     475             : /* Add STRING as body to the mail or the current MIME container.  A
     476             :  * second call to this function is not allowed.
     477             :  *
     478             :  * FIXME: We may want to have an append_body to add more data to a body.
     479             :  */
     480             : gpg_error_t
     481           0 : mime_maker_add_body (mime_maker_t ctx, const char *string)
     482             : {
     483           0 :   return add_body (ctx, string, strlen (string));
     484             : }
     485             : 
     486             : 
     487             : /* This is the same as mime_maker_add_body but takes a stream as
     488             :  * argument.  As of now the stream is copied to the MIME object but
     489             :  * eventually we may delay that and read the stream only at the time
     490             :  * it is needed.  Note that the address of the stream object must be
     491             :  * passed and that the ownership of the stream is transferred to this
     492             :  * MIME object.  To indicate the latter the function will store NULL
     493             :  * at the ADDR_STREAM so that a caller can't use that object anymore
     494             :  * except for es_fclose which accepts a NULL pointer.  */
     495             : gpg_error_t
     496           0 : mime_maker_add_stream (mime_maker_t ctx, estream_t *stream_addr)
     497             : {
     498             :   void *data;
     499             :   size_t datalen;
     500             : 
     501           0 :   es_rewind (*stream_addr);
     502           0 :   if (es_fclose_snatch (*stream_addr, &data, &datalen))
     503           0 :     return gpg_error_from_syserror ();
     504           0 :   *stream_addr = NULL;
     505           0 :   return add_body (ctx, data, datalen);
     506             : }
     507             : 
     508             : 
     509             : /* Add a new MIME container.  A container can be used instead of a
     510             :  * body.  */
     511             : gpg_error_t
     512           0 : mime_maker_add_container (mime_maker_t ctx)
     513             : {
     514             :   gpg_error_t err;
     515             :   part_t part;
     516             : 
     517           0 :   err = ensure_part (ctx, NULL);
     518           0 :   if (err)
     519           0 :     return err;
     520           0 :   part = ctx->current_part;
     521             : 
     522           0 :   if (part->body)
     523           0 :     return gpg_error (GPG_ERR_CONFLICT); /* There is already a body. */
     524           0 :   if (part->child || part->boundary)
     525           0 :     return gpg_error (GPG_ERR_CONFLICT); /* There is already a container. */
     526             : 
     527             :   /* Create a child node.  */
     528           0 :   part->child = xtrycalloc (1, sizeof *part->child);
     529           0 :   if (!part->child)
     530           0 :     return gpg_error_from_syserror ();
     531           0 :   part->child->headers_tail = &part->child->headers;
     532             : 
     533           0 :   part->boundary = generate_boundary (ctx);
     534           0 :   if (!part->boundary)
     535             :     {
     536           0 :       err = gpg_error_from_syserror ();
     537           0 :       xfree (part->child);
     538           0 :       part->child = NULL;
     539           0 :       return err;
     540             :     }
     541             : 
     542           0 :   part = part->child;
     543           0 :   part->partid = ++ctx->partid_counter;
     544           0 :   ctx->current_part = part;
     545             : 
     546           0 :   return 0;
     547             : }
     548             : 
     549             : 
     550             : /* Finish the current container.  */
     551             : gpg_error_t
     552           0 : mime_maker_end_container (mime_maker_t ctx)
     553             : {
     554             :   gpg_error_t err;
     555             :   part_t parent;
     556             : 
     557           0 :   err = ensure_part (ctx, &parent);
     558           0 :   if (err)
     559           0 :     return err;
     560           0 :   if (!parent)
     561           0 :     return gpg_error (GPG_ERR_CONFLICT); /* No container.  */
     562           0 :   while (parent->next)
     563           0 :     parent = parent->next;
     564           0 :   ctx->current_part = parent;
     565           0 :   return 0;
     566             : }
     567             : 
     568             : 
     569             : /* Return the part-ID of the current part. */
     570             : unsigned int
     571           0 : mime_maker_get_partid (mime_maker_t ctx)
     572             : {
     573           0 :   if (ensure_part (ctx, NULL))
     574           0 :     return 0; /* Ooops.  */
     575           0 :   return ctx->current_part->partid;
     576             : }
     577             : 
     578             : 
     579             : /* Write a header and handle emdedded LFs.  If BOUNDARY is not NULL it
     580             :  * is appended to the value.  */
     581             : /* Fixme: Add automatic line wrapping.  */
     582             : static gpg_error_t
     583           0 : write_header (mime_maker_t ctx, const char *name, const char *value,
     584             :               const char *boundary)
     585             : {
     586             :   const char *s;
     587             : 
     588           0 :   es_fprintf (ctx->outfp, "%s: ", name);
     589             : 
     590             :   /* Note that add_header made sure that VALUE does not end with a LF.
     591             :    * Thus we can assume that a LF is followed by non-whitespace.  */
     592           0 :   for (s = value; *s; s++)
     593             :     {
     594           0 :       if (*s == '\n')
     595           0 :         es_fputs ("\r\n\t", ctx->outfp);
     596             :       else
     597           0 :         es_fputc (*s, ctx->outfp);
     598             :     }
     599           0 :   if (boundary)
     600             :     {
     601           0 :       if (s > value && s[-1] != ';')
     602           0 :         es_fputc (';', ctx->outfp);
     603           0 :       es_fprintf (ctx->outfp, "\r\n\tboundary=\"%s\"", boundary);
     604             :     }
     605             : 
     606           0 :   es_fputs ("\r\n", ctx->outfp);
     607             : 
     608           0 :   return es_ferror (ctx->outfp)? gpg_error_from_syserror () : 0;
     609             : }
     610             : 
     611             : 
     612             : static gpg_error_t
     613           0 : write_gap (mime_maker_t ctx)
     614             : {
     615           0 :   es_fputs ("\r\n", ctx->outfp);
     616           0 :   return es_ferror (ctx->outfp)? gpg_error_from_syserror () : 0;
     617             : }
     618             : 
     619             : 
     620             : static gpg_error_t
     621           0 : write_boundary (mime_maker_t ctx, const char *boundary, int last)
     622             : {
     623           0 :   es_fprintf (ctx->outfp, "\r\n--%s%s\r\n", boundary, last?"--":"");
     624           0 :   return es_ferror (ctx->outfp)? gpg_error_from_syserror () : 0;
     625             : }
     626             : 
     627             : 
     628             : /* Fixme: Apply required encoding.  */
     629             : static gpg_error_t
     630           0 : write_body (mime_maker_t ctx, const void *body, size_t bodylen)
     631             : {
     632             :   const char *s;
     633             : 
     634           0 :   for (s = body; bodylen; s++, bodylen--)
     635             :     {
     636           0 :       if (*s == '\n' && !(s > (const char *)body && s[-1] == '\r'))
     637           0 :         es_fputc ('\r', ctx->outfp);
     638           0 :       es_fputc (*s, ctx->outfp);
     639             :     }
     640             : 
     641           0 :   return es_ferror (ctx->outfp)? gpg_error_from_syserror () : 0;
     642             : }
     643             : 
     644             : 
     645             : /* Recursive worker for mime_maker_make.  */
     646             : static gpg_error_t
     647           0 : write_tree (mime_maker_t ctx, part_t parent, part_t part)
     648             : {
     649             :   gpg_error_t err;
     650             :   header_t hdr;
     651             : 
     652           0 :   for (; part; part = part->next)
     653             :     {
     654           0 :       for (hdr = part->headers; hdr; hdr = hdr->next)
     655             :         {
     656           0 :           if (part->child && !strcmp (hdr->name, "Content-Type"))
     657           0 :             err = write_header (ctx, hdr->name, hdr->value, part->boundary);
     658             :           else
     659           0 :             err = write_header (ctx, hdr->name, hdr->value, NULL);
     660           0 :           if (err)
     661           0 :             return err;
     662             :         }
     663           0 :       err = write_gap (ctx);
     664           0 :       if (err)
     665           0 :         return err;
     666           0 :       if (part->body)
     667             :         {
     668           0 :           err = write_body (ctx, part->body, part->bodylen);
     669           0 :           if (err)
     670           0 :             return err;
     671             :         }
     672           0 :       if (part->child)
     673             :         {
     674           0 :           log_assert (part->boundary);
     675           0 :           err = write_boundary (ctx, part->boundary, 0);
     676           0 :           if (!err)
     677           0 :             err = write_tree (ctx, part, part->child);
     678           0 :           if (!err)
     679           0 :             err = write_boundary (ctx, part->boundary, 1);
     680           0 :           if (err)
     681           0 :             return err;
     682             :         }
     683             : 
     684           0 :       if (part->next)
     685             :         {
     686           0 :           log_assert (parent && parent->boundary);
     687           0 :           err = write_boundary (ctx, parent->boundary, 0);
     688           0 :           if (err)
     689           0 :             return err;
     690             :         }
     691             :     }
     692           0 :   return 0;
     693             : }
     694             : 
     695             : 
     696             : /* Add headers we always require.  */
     697             : static gpg_error_t
     698           0 : add_missing_headers (mime_maker_t ctx)
     699             : {
     700             :   gpg_error_t err;
     701             : 
     702           0 :   if (!ctx->mail)
     703           0 :     return gpg_error (GPG_ERR_NO_DATA);
     704           0 :   if (!have_header (ctx->mail, "MIME-Version"))
     705             :     {
     706             :       /* Even if a Content-Type has never been set, we want to
     707             :        * announce that we do MIME.  */
     708           0 :       err = add_header (ctx->mail, "MIME-Version", "1.0");
     709           0 :       if (err)
     710           0 :         goto leave;
     711             :     }
     712             : 
     713           0 :   if (!have_header (ctx->mail, "Date"))
     714             :     {
     715           0 :       char *p = rfctimestamp (make_timestamp ());
     716           0 :       if (!p)
     717           0 :         err = gpg_error_from_syserror ();
     718             :       else
     719           0 :         err = add_header (ctx->mail, "Date", p);
     720           0 :       xfree (p);
     721           0 :       if (err)
     722           0 :         goto leave;
     723             :     }
     724             : 
     725             : 
     726             :  leave:
     727           0 :   return err;
     728             : }
     729             : 
     730             : 
     731             : /* Create message from the tree MIME and write it to FP.  Note that
     732             :  * the output uses only a LF and a later called sendmail(1) is
     733             :  * expected to convert them to network line endings.  */
     734             : gpg_error_t
     735           0 : mime_maker_make (mime_maker_t ctx, estream_t fp)
     736             : {
     737             :   gpg_error_t err;
     738             : 
     739           0 :   err = add_missing_headers (ctx);
     740           0 :   if (err)
     741           0 :     return err;
     742             : 
     743           0 :   ctx->outfp = fp;
     744           0 :   err = write_tree (ctx, NULL, ctx->mail);
     745             : 
     746           0 :   ctx->outfp = NULL;
     747           0 :   return err;
     748             : }
     749             : 
     750             : 
     751             : /* Create a stream object from the MIME part identified by PARTID and
     752             :  * store it at R_STREAM.  If PARTID identifies a container the entire
     753             :  * tree is returned. Using that function may read stream objects
     754             :  * which have been added as MIME bodies.  The caller must close the
     755             :  * stream object. */
     756             : gpg_error_t
     757           0 : mime_maker_get_part (mime_maker_t ctx, unsigned int partid, estream_t *r_stream)
     758             : {
     759             :   gpg_error_t err;
     760             :   part_t part;
     761             :   estream_t fp;
     762             : 
     763           0 :   *r_stream = NULL;
     764             : 
     765             :   /* When the entire tree is requested, we make sure that all missing
     766             :    * headers are applied.  We don't do that if only a part is
     767             :    * requested because the additional headers (like Date:) will only
     768             :    * be added to part 0 headers anyway. */
     769           0 :   if (!partid)
     770             :     {
     771           0 :        err = add_missing_headers (ctx);
     772           0 :        if (err)
     773           0 :          return err;
     774           0 :        part = ctx->mail;
     775             :     }
     776             :   else
     777           0 :     part = find_part (ctx->mail, partid);
     778             : 
     779             :   /* For now we use a memory stream object; however it would also be
     780             :    * possible to create an object created on the fly while the caller
     781             :    * is reading the returned stream.  */
     782           0 :   fp = es_fopenmem (0, "w+b");
     783           0 :   if (!fp)
     784           0 :     return gpg_error_from_syserror ();
     785             : 
     786           0 :   ctx->outfp = fp;
     787           0 :   err = write_tree (ctx, NULL, part);
     788           0 :   ctx->outfp = NULL;
     789             : 
     790           0 :   if (!err)
     791             :     {
     792           0 :       es_rewind (fp);
     793           0 :       *r_stream = fp;
     794             :     }
     795             :   else
     796           0 :     es_fclose (fp);
     797             : 
     798           0 :   return err;
     799             : }

Generated by: LCOV version 1.11