kbx: Add missing header file.
[gnupg.git] / tools / mime-maker.c
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 <http://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 charachters 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 *mediatype;      /* Mediatype of the container (malloced). */
52   char *boundary;       /* Malloced boundary string.  */
53   header_t headers;     /* List of headers.  */
54   header_t *headers_tail;/* Address of last header in chain.  */
55   size_t bodylen;       /* Length of BODY.   */
56   char *body;           /* Malloced buffer with the body.  This is the
57                          * non-encoded value.  */
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   int boundary_counter;  /* Used to create easy to read boundaries.  */
75   char *boundary_suffix; /* Random string used in the boundaries.  */
76
77   struct b64state *b64state;     /* NULL or malloced Base64 decoder state.  */
78
79   /* Helper to convey the output stream to recursive functions. */
80   estream_t outfp;
81 };
82
83
84 /* Create a new mime make object.  COOKIE is a values woich will be
85  * used as first argument for all callbacks registered with this
86  * object.  */
87 gpg_error_t
88 mime_maker_new (mime_maker_t *r_maker, void *cookie)
89 {
90   mime_maker_t ctx;
91
92   *r_maker = NULL;
93
94   ctx = xtrycalloc (1, sizeof *ctx);
95   if (!ctx)
96     return gpg_error_from_syserror ();
97   ctx->cookie = cookie;
98
99   *r_maker = ctx;
100   return 0;
101 }
102
103
104 static void
105 release_parts (part_t part)
106 {
107   while (part)
108     {
109       part_t partnext = part->next;
110       while (part->headers)
111         {
112           header_t hdrnext = part->headers->next;
113           xfree (part->headers);
114           part->headers = hdrnext;
115         }
116       release_parts (part->child);
117       xfree (part->mediatype);
118       xfree (part->boundary);
119       xfree (part->body);
120       xfree (part);
121       part = partnext;
122     }
123 }
124
125
126 /* Release a mime maker object.  */
127 void
128 mime_maker_release (mime_maker_t ctx)
129 {
130   if (!ctx)
131     return;
132
133   release_parts (ctx->mail);
134   xfree (ctx->boundary_suffix);
135   xfree (ctx);
136 }
137
138
139 /* Set verbose and debug mode.  */
140 void
141 mime_maker_set_verbose (mime_maker_t ctx, int level)
142 {
143   if (!level)
144     {
145       ctx->verbose = 0;
146       ctx->debug = 0;
147     }
148   else
149     {
150       ctx->verbose = 1;
151       if (level > 10)
152         ctx->debug = 1;
153     }
154 }
155
156
157 static void
158 dump_parts (part_t part, int level)
159 {
160   header_t hdr;
161
162   for (; part; part = part->next)
163     {
164       log_debug ("%*s[part]\n", level*2, "");
165       for (hdr = part->headers; hdr; hdr = hdr->next)
166         {
167           log_debug ("%*s%s: %s\n", level*2, "", hdr->name, hdr->value);
168         }
169       log_debug ("%*s[body %zu bytes]\n", level*2, "", part->bodylen);
170       if (part->child)
171         {
172           log_debug ("%*s[container]\n", level*2, "");
173           dump_parts (part->child, level+1);
174         }
175     }
176 }
177
178
179 /* Dump the mime tree for debugging.  */
180 void
181 mime_maker_dump_tree (mime_maker_t ctx)
182 {
183   dump_parts (ctx->mail, 0);
184 }
185
186
187 /* Find the parent node for NEEDLE starting at ROOT.  */
188 static part_t
189 find_parent (part_t root, part_t needle)
190 {
191   part_t node, n;
192
193   for (node = root->child; node; node = node->next)
194     {
195       if (node == needle)
196         return root;
197       if ((n = find_parent (node, needle)))
198         return n;
199     }
200   return NULL;
201 }
202
203
204 /* Create a boundary string.  Outr codes is aware of the general
205  * structure of that string (gebins with "=-=") so that
206  * it can protect against accidently used boundaries within the
207  * content.   */
208 static char *
209 generate_boundary (mime_maker_t ctx)
210 {
211   if (!ctx->boundary_suffix)
212     {
213       char buffer[12];
214
215       gcry_create_nonce (buffer, sizeof buffer);
216       ctx->boundary_suffix = zb32_encode (buffer, 8 * sizeof buffer);
217       if (!ctx->boundary_suffix)
218         return NULL;
219     }
220
221   ctx->boundary_counter++;
222   return es_bsprintf ("=-=%02d-%s=-=",
223                       ctx->boundary_counter, ctx->boundary_suffix);
224 }
225
226
227 /* Ensure that the context has a MAIL and CURRENT_PART object and
228  * return the parent object if available  */
229 static gpg_error_t
230 ensure_part (mime_maker_t ctx, part_t *r_parent)
231 {
232   if (!ctx->mail)
233     {
234       ctx->mail = xtrycalloc (1, sizeof *ctx->mail);
235       if (!ctx->mail)
236         return gpg_error_from_syserror ();
237       log_assert (!ctx->current_part);
238       ctx->current_part = ctx->mail;
239       ctx->current_part->headers_tail = &ctx->current_part->headers;
240     }
241   log_assert (ctx->current_part);
242   if (r_parent)
243     *r_parent = find_parent (ctx->mail, ctx->current_part);
244
245   return 0;
246 }
247
248
249 /* Transform a header name into a standard capitalized format.
250  * "Content-Type".  Conversion stops at the colon. */
251 static void
252 capitalize_header_name (char *name)
253 {
254   unsigned char *p = name;
255   int first = 1;
256
257   /* Special cases first.  */
258   if (!ascii_strcasecmp (name, "MIME-Version"))
259     {
260       strcpy (name, "MIME-Version");
261       return;
262     }
263
264   /* Regular cases.  */
265   for (; *p && *p != ':'; p++)
266     {
267       if (*p == '-')
268         first = 1;
269       else if (first)
270         {
271           if (*p >= 'a' && *p <= 'z')
272             *p = *p - 'a' + 'A';
273           first = 0;
274         }
275       else if (*p >= 'A' && *p <= 'Z')
276         *p = *p - 'A' + 'a';
277     }
278 }
279
280
281 /* Check whether a header with NAME has already been set into PART.
282  * NAME must be in canonical capitalized format.  Return true or
283  * false. */
284 static int
285 have_header (part_t part, const char *name)
286 {
287   header_t hdr;
288
289   for (hdr = part->headers; hdr; hdr = hdr->next)
290     if (!strcmp (hdr->name, name))
291       return 1;
292   return 0;
293 }
294
295
296 /* Helper to add a header to a part.  */
297 static gpg_error_t
298 add_header (part_t part, const char *name, const char *value)
299 {
300   gpg_error_t err;
301   header_t hdr;
302   size_t namelen;
303   const char *s;
304
305   if (!value)
306     {
307       s = strchr (name, '=');
308       if (!s)
309         return gpg_error (GPG_ERR_INV_ARG);
310       namelen = s - name;
311       value = s+1;
312     }
313   else
314     namelen = strlen (name);
315
316   hdr = xtrymalloc (sizeof *hdr + namelen);
317   if (!hdr)
318     return gpg_error_from_syserror ();
319   hdr->next = NULL;
320   memcpy (hdr->name, name, namelen);
321   hdr->name[namelen] = 0;
322
323   /* Check that the header name is valid.  We allow all lower and
324    * uppercase letters and, except for the first character, digits and
325    * the dash.  */
326   if (strspn (hdr->name, HEADER_NAME_CHARS) != namelen
327       || strchr ("-0123456789", *hdr->name))
328     {
329       xfree (hdr);
330       return gpg_error (GPG_ERR_INV_NAME);
331     }
332
333   capitalize_header_name (hdr->name);
334   hdr->value = xtrystrdup (value);
335   if (!hdr->value)
336     {
337       err = gpg_error_from_syserror ();
338       xfree (hdr);
339       return err;
340     }
341
342   if (part)
343     {
344       *part->headers_tail = hdr;
345       part->headers_tail = &hdr->next;
346     }
347   else
348     xfree (hdr);
349
350   return 0;
351 }
352
353
354 /* Add a header with NAME and VALUE to the current mail.  A LF in the
355  * VALUE will be handled automagically.  If NULL is used for VALUE it
356  * is expected that the NAME has the format "NAME=VALUE" and VALUE is
357  * taken from there.
358  *
359  * If no container has been added, the header will be used for the
360  * regular mail headers and not for a MIME part.  If the current part
361  * is in a container and a body has been added, we append a new part
362  * to the current container.  Thus for a non-MIME mail the caller
363  * needs to call this function followed by a call to add a body.  When
364  * adding a Content-Type the boundary parameter must not be included.
365  */
366 gpg_error_t
367 mime_maker_add_header (mime_maker_t ctx, const char *name, const char *value)
368 {
369   gpg_error_t err;
370   part_t part, parent;
371
372   /* Hack to use this fucntion for a synacx check of NAME and VALUE.  */
373   if (!ctx)
374     return add_header (NULL, name, value);
375
376   err = ensure_part (ctx, &parent);
377   if (err)
378     return err;
379   part = ctx->current_part;
380
381   if (part->body && !parent)
382     {
383       /* We already have a body but no parent.  Adding another part is
384        * thus not possible.  */
385       return gpg_error (GPG_ERR_CONFLICT);
386     }
387   if (part->body)
388     {
389       /* We already have a body and there is a parent.  We now append
390        * a new part to the current container.  */
391       part = xtrycalloc (1, sizeof *part);
392       if (!part)
393         return gpg_error_from_syserror ();
394       part->headers_tail = &part->headers;
395       log_assert (!ctx->current_part->next);
396       ctx->current_part->next = part;
397       ctx->current_part = part;
398     }
399
400   /* If no NAME and no VALUE has been given we do not add a header.
401    * This can be used to create a new part without any header.  */
402   if (!name && !value)
403     return 0;
404
405   /* If we add Content-Type, make sure that we have a MIME-version
406    * header first; this simply looks better.  */
407   if (!ascii_strcasecmp (name, "Content-Type")
408       && !have_header (ctx->mail, "MIME-Version"))
409     {
410       err = add_header (ctx->mail, "MIME-Version", "1.0");
411       if (err)
412         return err;
413     }
414   return add_header (part, name, value);
415 }
416
417
418 /* Helper for mime_maker_add_{body,stream}.  */
419 static gpg_error_t
420 add_body (mime_maker_t ctx, const void *data, size_t datalen)
421 {
422   gpg_error_t err;
423   part_t part, parent;
424
425   err = ensure_part (ctx, &parent);
426   if (err)
427     return err;
428   part = ctx->current_part;
429   if (part->body)
430     return gpg_error (GPG_ERR_CONFLICT);
431
432   part->body = xtrymalloc (datalen? datalen : 1);
433   if (!part->body)
434     return gpg_error_from_syserror ();
435   part->bodylen = datalen;
436   if (data)
437     memcpy (part->body, data, datalen);
438
439   return 0;
440 }
441
442
443 /* Add STRING as body to the mail or the current MIME container.  A
444  * second call to this function is not allowed.
445  *
446  * FIXME: We may want to have an append_body to add more data to a body.
447  */
448 gpg_error_t
449 mime_maker_add_body (mime_maker_t ctx, const char *string)
450 {
451   return add_body (ctx, string, strlen (string));
452 }
453
454
455 /* This is the same as mime_maker_add_body but takes a stream as
456  * argument.  As of now the stream is copied to the MIME object but
457  * eventually we may delay that and read the stream only at the time
458  * it is needed.  Note that the address of the stream object must be
459  * passed and that the ownership of the stream is transferred to this
460  * MIME object.  To indicate the latter the function will store NULL
461  * at the ADDR_STREAM so that a caller can't use that object anymore
462  * except for es_fclose which accepts a NULL pointer.  */
463 gpg_error_t
464 mime_maker_add_stream (mime_maker_t ctx, estream_t *stream_addr)
465 {
466   void *data;
467   size_t datalen;
468
469   es_rewind (*stream_addr);
470   if (es_fclose_snatch (*stream_addr, &data, &datalen))
471     return gpg_error_from_syserror ();
472   *stream_addr = NULL;
473   return add_body (ctx, data, datalen);
474 }
475
476
477 /* Add a new MIME container.  The caller needs to provide the media
478  * and media-subtype in MEDIATYPE.  If MEDIATYPE is NULL
479  * "multipart/mixed" is assumed.  This function will then add a
480  * Content-Type header with that media type and an approriate boundary
481  * string to the parent part.  */
482 gpg_error_t
483 mime_maker_add_container (mime_maker_t ctx, const char *mediatype)
484 {
485   gpg_error_t err;
486   part_t part;
487
488   if (!mediatype)
489     mediatype = "multipart/mixed";
490
491   err = ensure_part (ctx, NULL);
492   if (err)
493     return err;
494   part = ctx->current_part;
495   if (part->body)
496     return gpg_error (GPG_ERR_CONFLICT); /* There is already a body. */
497   if (part->child || part->mediatype || part->boundary)
498     return gpg_error (GPG_ERR_CONFLICT); /* There is already a container. */
499
500   /* If a content type has not yet been set, do it now.  The boundary
501    * will be added while writing the headers.  */
502   if (!have_header (ctx->mail, "Content-Type"))
503     {
504       err = add_header (ctx->mail, "Content-Type", mediatype);
505       if (err)
506         return err;
507     }
508
509   /* Create a child node.  */
510   part->child = xtrycalloc (1, sizeof *part->child);
511   if (!part->child)
512     return gpg_error_from_syserror ();
513   part->child->headers_tail = &part->child->headers;
514
515   part->mediatype = xtrystrdup (mediatype);
516   if (!part->mediatype)
517     {
518       err = gpg_error_from_syserror ();
519       xfree (part->child);
520       part->child = NULL;
521       return err;
522     }
523
524   part->boundary = generate_boundary (ctx);
525   if (!part->boundary)
526     {
527       err = gpg_error_from_syserror ();
528       xfree (part->child);
529       part->child = NULL;
530       xfree (part->mediatype);
531       part->mediatype = NULL;
532       return err;
533     }
534
535   part = part->child;
536   ctx->current_part = part;
537
538   return 0;
539 }
540
541
542 /* Write the Content-Type header with the boundary value.  */
543 static gpg_error_t
544 write_ct_with_boundary (mime_maker_t ctx,
545                         const char *value, const char *boundary)
546 {
547   const char *s;
548
549   if (!*value)
550     return gpg_error (GPG_ERR_INV_VALUE);  /* Empty string.  */
551
552   for (s=value + strlen (value) - 1;
553        (s >= value
554         && (*s == ' ' || *s == '\t' || *s == '\n'));
555        s--)
556     ;
557   if (!(s >= value))
558     return gpg_error (GPG_ERR_INV_VALUE);  /* Only spaces.  */
559
560   /* Fixme: We should use a dedicated header write functions which
561    * properly wraps the header.  */
562   es_fprintf (ctx->outfp, "Content-Type: %s%s\n\tboundary=\"%s\"\n",
563               value,
564               (*s == ';')? "":";",
565               boundary);
566   return 0;
567 }
568
569
570 /* Recursive worker for mime_maker_make.  */
571 static gpg_error_t
572 write_tree (mime_maker_t ctx, part_t parent, part_t part)
573 {
574   gpg_error_t err;
575   header_t hdr;
576
577   for (; part; part = part->next)
578     {
579       for (hdr = part->headers; hdr; hdr = hdr->next)
580         {
581           if (part->child && !strcmp (hdr->name, "Content-Type"))
582             write_ct_with_boundary (ctx, hdr->value, part->boundary);
583           else
584             es_fprintf (ctx->outfp, "%s: %s\n", hdr->name, hdr->value);
585         }
586       es_fputc ('\n', ctx->outfp);
587       if (part->body)
588         {
589           if (es_write (ctx->outfp, part->body, part->bodylen, NULL))
590             return gpg_error_from_syserror ();
591         }
592       if (part->child)
593         {
594           log_assert (part->boundary);
595           if (es_fprintf (ctx->outfp, "\n--%s\n", part->boundary) < 0)
596             return gpg_error_from_syserror ();
597           err = write_tree (ctx, part, part->child);
598           if (err)
599             return err;
600           if (es_fprintf (ctx->outfp, "\n--%s--\n", part->boundary) < 0)
601             return gpg_error_from_syserror ();
602         }
603
604       if (part->next)
605         {
606           log_assert (parent && parent->boundary);
607           if (es_fprintf (ctx->outfp, "\n--%s\n", parent->boundary) < 0)
608             return gpg_error_from_syserror ();
609         }
610     }
611   return 0;
612 }
613
614
615 /* Add headers we always require.  */
616 static gpg_error_t
617 add_missing_headers (mime_maker_t ctx)
618 {
619   gpg_error_t err;
620
621   if (!ctx->mail)
622     return gpg_error (GPG_ERR_NO_DATA);
623   if (!have_header (ctx->mail, "MIME-Version"))
624     {
625       /* Even if a Content-Type has never been set, we want to
626        * announce that we do MIME.  */
627       err = add_header (ctx->mail, "MIME-Version", "1.0");
628       if (err)
629         goto leave;
630     }
631
632   if (!have_header (ctx->mail, "Date"))
633     {
634       char *p = rfctimestamp (make_timestamp ());
635       if (!p)
636         err = gpg_error_from_syserror ();
637       else
638         err = add_header (ctx->mail, "Date", p);
639       xfree (p);
640       if (err)
641         goto leave;
642     }
643
644
645  leave:
646   return err;
647 }
648
649
650 /* Create message from the tree MIME and write it to FP.  Noet that
651  * the output uses only a LF and a later called sendmail(1) is
652  * expected to convert them to network line endings.  */
653 gpg_error_t
654 mime_maker_make (mime_maker_t ctx, estream_t fp)
655 {
656   gpg_error_t err;
657
658   err = add_missing_headers (ctx);
659   if (err)
660     return err;
661
662   ctx->outfp = fp;
663   err = write_tree (ctx, NULL, ctx->mail);
664
665   ctx->outfp = NULL;
666   return err;
667 }