wkd: New command --print-wkd-hash for gpg-wks-client.
[gnupg.git] / tools / rfc822parse.c
1 /* rfc822parse.c - Simple mail and MIME parser
2  * Copyright (C) 1999, 2000 Werner Koch, Duesseldorf
3  * Copyright (C) 2003, 2004 g10 Code GmbH
4  *
5  * This file is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as
7  * published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This file is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, see <https://www.gnu.org/licenses/>.
17  */
18
19
20 /* According to RFC822 binary zeroes are allowed at many places. We do
21  * not handle this correct especially in the field parsing code.  It
22  * should be easy to fix and the API provides a interfaces which
23  * returns the length but in addition makes sure that returned strings
24  * are always ended by a \0.
25  *
26  * Furthermore, the case of field names is changed and thus it is not
27  * always a good idea to use these modified header
28  * lines (e.g. signatures may break).
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <stdarg.h>
40 #include <assert.h>
41
42 #include "rfc822parse.h"
43
44 /* All valid characters in a header name.  */
45 #define HEADER_NAME_CHARS  ("abcdefghijklmnopqrstuvwxyz" \
46                             "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
47                             "-01234567890")
48
49
50 enum token_type
51   {
52     tSPACE,
53     tATOM,
54     tQUOTED,
55     tDOMAINLIT,
56     tSPECIAL
57   };
58
59 /* For now we directly use our TOKEN as the parse context */
60 typedef struct rfc822parse_field_context *TOKEN;
61 struct rfc822parse_field_context
62 {
63   TOKEN next;
64   enum token_type type;
65   struct {
66     unsigned int cont:1;
67     unsigned int lowered:1;
68   } flags;
69   /*TOKEN owner_pantry; */
70   char data[1];
71 };
72
73 struct hdr_line
74 {
75   struct hdr_line *next;
76   int cont;     /* This is a continuation of the previous line. */
77   unsigned char line[1];
78 };
79
80 typedef struct hdr_line *HDR_LINE;
81
82
83 struct part
84 {
85   struct part *right;     /* The next part. */
86   struct part *down;      /* A contained part. */
87   HDR_LINE hdr_lines;       /* Header lines os that part. */
88   HDR_LINE *hdr_lines_tail; /* Helper for adding lines. */
89   char *boundary;           /* Only used in the first part. */
90 };
91 typedef struct part *part_t;
92
93 struct rfc822parse_context
94 {
95   rfc822parse_cb_t callback;
96   void *callback_value;
97   int callback_error;
98   int in_body;
99   int in_preamble;      /* Whether we are before the first boundary. */
100   part_t parts;         /* The tree of parts. */
101   part_t current_part;  /* Whom we are processing (points into parts). */
102   const char *boundary; /* Current boundary. */
103 };
104
105 static HDR_LINE find_header (rfc822parse_t msg, const char *name,
106                              int which, HDR_LINE * rprev);
107
108
109 static size_t
110 length_sans_trailing_ws (const unsigned char *line, size_t len)
111 {
112   const unsigned char *p, *mark;
113   size_t n;
114
115   for (mark=NULL, p=line, n=0; n < len; n++, p++)
116     {
117       if (strchr (" \t\r\n", *p ))
118         {
119           if( !mark )
120             mark = p;
121         }
122       else
123         mark = NULL;
124     }
125
126   if (mark)
127     return mark - line;
128   return len;
129 }
130
131
132 static void
133 lowercase_string (unsigned char *string)
134 {
135   for (; *string; string++)
136     if (*string >= 'A' && *string <= 'Z')
137       *string = *string - 'A' + 'a';
138 }
139
140
141 static int
142 my_toupper (int c)
143 {
144   if (c >= 'a' && c <= 'z')
145     c &= ~0x20;
146   return c;
147 }
148
149 /* This is the same as ascii_strcasecmp.  */
150 static int
151 my_strcasecmp (const char *a, const char *b)
152 {
153   if (a == b)
154     return 0;
155
156   for (; *a && *b; a++, b++)
157     {
158       if (*a != *b && my_toupper(*a) != my_toupper(*b))
159         break;
160     }
161   return *a == *b? 0 : (my_toupper (*a) - my_toupper (*b));
162 }
163
164
165 #ifndef HAVE_STPCPY
166 static char *
167 my_stpcpy (char *a,const char *b)
168 {
169   while (*b)
170     *a++ = *b++;
171   *a = 0;
172
173   return (char*)a;
174 }
175 #define stpcpy my_stpcpy
176 #endif
177
178
179 /* If a callback has been registered, call it for the event of type
180    EVENT. */
181 static int
182 do_callback (rfc822parse_t msg, rfc822parse_event_t event)
183 {
184   int rc;
185
186   if (!msg->callback || msg->callback_error)
187     return 0;
188   rc = msg->callback (msg->callback_value, event, msg);
189   if (rc)
190     msg->callback_error = rc;
191   return rc;
192 }
193
194 static part_t
195 new_part (void)
196 {
197   part_t part;
198
199   part = calloc (1, sizeof *part);
200   if (part)
201     {
202       part->hdr_lines_tail = &part->hdr_lines;
203     }
204   return part;
205 }
206
207
208 static void
209 release_part (part_t part)
210 {
211   part_t tmp;
212   HDR_LINE hdr, hdr2;
213
214   for (; part; part = tmp)
215     {
216       tmp = part->right;
217       if (part->down)
218         release_part (part->down);
219       for (hdr = part->hdr_lines; hdr; hdr = hdr2)
220         {
221           hdr2 = hdr->next;
222           free (hdr);
223         }
224       free (part->boundary);
225       free (part);
226     }
227 }
228
229
230 static void
231 release_handle_data (rfc822parse_t msg)
232 {
233   release_part (msg->parts);
234   msg->parts = NULL;
235   msg->current_part = NULL;
236   msg->boundary = NULL;
237 }
238
239
240 /* Check that the header name is valid.  We allow all lower and
241  * uppercase letters and, except for the first character, digits and
242  * the dash.  The check stops at the first colon or at string end.
243  * Returns true if the name is valid.  */
244 int
245 rfc822_valid_header_name_p (const char *name)
246 {
247   const char *s;
248   size_t namelen;
249
250   if ((s=strchr (name, ':')))
251     namelen = s - name;
252   else
253     namelen = strlen (name);
254
255   if (!namelen
256       || strspn (name, HEADER_NAME_CHARS) != namelen
257       || strchr ("-0123456789", *name))
258     return 0;
259   return 1;
260 }
261
262
263 /* Transform a header NAME into a standard capitalized format.
264  * Conversion stops at the colon. */
265 void
266 rfc822_capitalize_header_name (char *name)
267 {
268   unsigned char *p = name;
269   int first = 1;
270
271   /* Special cases first.  */
272   if (!my_strcasecmp (name, "MIME-Version"))
273     {
274       strcpy (name, "MIME-Version");
275       return;
276     }
277
278   /* Regular cases.  */
279   for (; *p && *p != ':'; p++)
280     {
281       if (*p == '-')
282         first = 1;
283       else if (first)
284         {
285           if (*p >= 'a' && *p <= 'z')
286             *p = *p - 'a' + 'A';
287           first = 0;
288         }
289       else if (*p >= 'A' && *p <= 'Z')
290         *p = *p - 'A' + 'a';
291     }
292 }
293
294
295
296 /* Create a new parsing context for an entire rfc822 message and
297    return it.  CB and CB_VALUE may be given to callback for certain
298    events.  NULL is returned on error with errno set appropriately. */
299 rfc822parse_t
300 rfc822parse_open (rfc822parse_cb_t cb, void *cb_value)
301 {
302   rfc822parse_t msg = calloc (1, sizeof *msg);
303   if (msg)
304     {
305       msg->parts = msg->current_part = new_part ();
306       if (!msg->parts)
307         {
308           free (msg);
309           msg = NULL;
310         }
311       else
312         {
313           msg->callback = cb;
314           msg->callback_value = cb_value;
315           if (do_callback (msg, RFC822PARSE_OPEN))
316             {
317               release_handle_data (msg);
318               free (msg);
319               msg = NULL;
320             }
321         }
322     }
323   return msg;
324 }
325
326
327 void
328 rfc822parse_cancel (rfc822parse_t msg)
329 {
330   if (msg)
331     {
332       do_callback (msg, RFC822PARSE_CANCEL);
333       release_handle_data (msg);
334       free (msg);
335     }
336 }
337
338
339 void
340 rfc822parse_close (rfc822parse_t msg)
341 {
342   if (msg)
343     {
344       do_callback (msg, RFC822PARSE_CLOSE);
345       release_handle_data (msg);
346       free (msg);
347     }
348 }
349
350 static part_t
351 find_parent (part_t tree, part_t target)
352 {
353   part_t part;
354
355   for (part = tree->down; part; part = part->right)
356     {
357       if (part == target)
358         return tree; /* Found. */
359       if (part->down)
360         {
361           part_t tmp = find_parent (part, target);
362           if (tmp)
363             return tmp;
364         }
365     }
366   return NULL;
367 }
368
369 static void
370 set_current_part_to_parent (rfc822parse_t msg)
371 {
372   part_t parent;
373
374   assert (msg->current_part);
375   parent = find_parent (msg->parts, msg->current_part);
376   if (!parent)
377     return; /* Already at the top. */
378
379 #ifndef NDEBUG
380   {
381     part_t part;
382     for (part = parent->down; part; part = part->right)
383       if (part == msg->current_part)
384         break;
385     assert (part);
386   }
387 #endif
388   msg->current_part = parent;
389
390   parent = find_parent (msg->parts, parent);
391   msg->boundary = parent? parent->boundary: NULL;
392 }
393
394
395
396 /****************
397  * We have read in all header lines and are about to receive the body
398  * part.  The delimiter line has already been processed.
399  *
400  * FIXME: we's better return an error in case of memory failures.
401  */
402 static int
403 transition_to_body (rfc822parse_t msg)
404 {
405   rfc822parse_field_t ctx;
406   int rc;
407
408   rc = do_callback (msg, RFC822PARSE_T2BODY);
409   if (!rc)
410     {
411       /* Store the boundary if we have multipart type. */
412       ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
413       if (ctx)
414         {
415           const char *s;
416
417           s = rfc822parse_query_media_type (ctx, NULL);
418           if (s && !strcmp (s,"multipart"))
419             {
420               s = rfc822parse_query_parameter (ctx, "boundary", 0);
421               if (s)
422                 {
423                   assert (!msg->current_part->boundary);
424                   msg->current_part->boundary = malloc (strlen (s) + 1);
425                   if (msg->current_part->boundary)
426                     {
427                       part_t part;
428
429                       strcpy (msg->current_part->boundary, s);
430                       msg->boundary = msg->current_part->boundary;
431                       part = new_part ();
432                       if (!part)
433                         {
434                           int save_errno = errno;
435                           rfc822parse_release_field (ctx);
436                           errno = save_errno;
437                           return -1;
438                         }
439                       rc = do_callback (msg, RFC822PARSE_LEVEL_DOWN);
440                       assert (!msg->current_part->down);
441                       msg->current_part->down = part;
442                       msg->current_part = part;
443                       msg->in_preamble = 1;
444                     }
445                 }
446             }
447           rfc822parse_release_field (ctx);
448         }
449     }
450
451   return rc;
452 }
453
454 /* We have just passed a MIME boundary and need to prepare for new part.
455    headers. */
456 static int
457 transition_to_header (rfc822parse_t msg)
458 {
459   part_t part;
460
461   assert (msg->current_part);
462   assert (!msg->current_part->right);
463
464   part = new_part ();
465   if (!part)
466     return -1;
467
468   msg->current_part->right = part;
469   msg->current_part = part;
470   return 0;
471 }
472
473
474 static int
475 insert_header (rfc822parse_t msg, const unsigned char *line, size_t length)
476 {
477   HDR_LINE hdr;
478
479   assert (msg->current_part);
480   if (!length)
481     {
482       msg->in_body = 1;
483       return transition_to_body (msg);
484     }
485
486   if (!msg->current_part->hdr_lines)
487     do_callback (msg, RFC822PARSE_BEGIN_HEADER);
488
489   length = length_sans_trailing_ws (line, length);
490   hdr = malloc (sizeof (*hdr) + length);
491   if (!hdr)
492     return -1;
493   hdr->next = NULL;
494   hdr->cont = (*line == ' ' || *line == '\t');
495   memcpy (hdr->line, line, length);
496   hdr->line[length] = 0; /* Make it a string. */
497
498   /* Transform a field name into canonical format. */
499   if (!hdr->cont && strchr (line, ':'))
500     rfc822_capitalize_header_name (hdr->line);
501
502   *msg->current_part->hdr_lines_tail = hdr;
503   msg->current_part->hdr_lines_tail = &hdr->next;
504
505   /* Lets help the caller to prevent mail loops and issue an event for
506    * every Received header. */
507   if (length >= 9 && !memcmp (line, "Received:", 9))
508      do_callback (msg, RFC822PARSE_RCVD_SEEN);
509   return 0;
510 }
511
512
513 /****************
514  * Note: We handle the body transparent to allow binary zeroes in it.
515  */
516 static int
517 insert_body (rfc822parse_t msg, const unsigned char *line, size_t length)
518 {
519   int rc = 0;
520
521   if (length > 2 && *line == '-' && line[1] == '-' && msg->boundary)
522     {
523       size_t blen = strlen (msg->boundary);
524
525       if (length == blen + 2
526           && !memcmp (line+2, msg->boundary, blen))
527         {
528           rc = do_callback (msg, RFC822PARSE_BOUNDARY);
529           msg->in_body = 0;
530           if (!rc && !msg->in_preamble)
531             rc = transition_to_header (msg);
532           msg->in_preamble = 0;
533         }
534       else if (length == blen + 4
535           && line[length-2] =='-' && line[length-1] == '-'
536           && !memcmp (line+2, msg->boundary, blen))
537         {
538           rc = do_callback (msg, RFC822PARSE_LAST_BOUNDARY);
539           msg->boundary = NULL; /* No current boundary anymore. */
540           set_current_part_to_parent (msg);
541
542           /* Fixme: The next should actually be send right before the
543              next boundary, so that we can mark the epilogue. */
544           if (!rc)
545             rc = do_callback (msg, RFC822PARSE_LEVEL_UP);
546         }
547     }
548   if (msg->in_preamble && !rc)
549     rc = do_callback (msg, RFC822PARSE_PREAMBLE);
550
551   return rc;
552 }
553
554 /* Insert the next line into the parser. Return 0 on success or true
555    on error with errno set appropriately. */
556 int
557 rfc822parse_insert (rfc822parse_t msg, const unsigned char *line, size_t length)
558 {
559   return (msg->in_body
560           ? insert_body (msg, line, length)
561           : insert_header (msg, line, length));
562 }
563
564
565 /* Tell the parser that we have finished the message. */
566 int
567 rfc822parse_finish (rfc822parse_t msg)
568 {
569   return do_callback (msg, RFC822PARSE_FINISH);
570 }
571
572
573
574 /****************
575  * Get a copy of a header line. The line is returned as one long
576  * string with LF to separate the continuation line. Caller must free
577  * the return buffer.  WHICH may be used to enumerate over all lines.
578  * Wildcards are allowed.  This function works on the current headers;
579  * i.e. the regular mail headers or the MIME headers of the current
580  * part.
581  *
582  * WHICH gives the mode:
583  *  -1 := Take the last occurrence
584  *   n := Take the n-th  one.
585  *
586  * Returns a newly allocated buffer or NULL on error.  errno is set in
587  * case of a memory failure or set to 0 if the requested field is not
588  * available.
589  *
590  * If VALUEOFF is not NULL it will receive the offset of the first non
591  * space character in the value part of the line (i.e. after the first
592  * colon).
593  */
594 char *
595 rfc822parse_get_field (rfc822parse_t msg, const char *name, int which,
596                        size_t *valueoff)
597 {
598   HDR_LINE h, h2;
599   char *buf, *p;
600   size_t n;
601
602   h = find_header (msg, name, which, NULL);
603   if (!h)
604     {
605       errno = 0;
606       return NULL; /* no such field */
607     }
608
609   n = strlen (h->line) + 1;
610   for (h2 = h->next; h2 && h2->cont; h2 = h2->next)
611     n += strlen (h2->line) + 1;
612
613   buf = p = malloc (n);
614   if (buf)
615     {
616       p = stpcpy (p, h->line);
617       *p++ = '\n';
618       for (h2 = h->next; h2 && h2->cont; h2 = h2->next)
619         {
620           p = stpcpy (p, h2->line);
621           *p++ = '\n';
622         }
623       p[-1] = 0;
624     }
625
626   if (valueoff)
627     {
628       p = strchr (buf, ':');
629       if (!p)
630         *valueoff = 0; /* Oops: should never happen. */
631       else
632         {
633           p++;
634           while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
635             p++;
636           *valueoff = p - buf;
637         }
638     }
639
640   return buf;
641 }
642
643
644 /****************
645  * Enumerate all header.  Caller has to provide the address of a pointer
646  * which has to be initialized to NULL, the caller should then never change this
647  * pointer until he has closed the enumeration by passing again the address
648  * of the pointer but with msg set to NULL.
649  * The function returns pointers to all the header lines or NULL when
650  * all lines have been enumerated or no headers are available.
651  */
652 const char *
653 rfc822parse_enum_header_lines (rfc822parse_t msg, void **context)
654 {
655   HDR_LINE l;
656
657   if (!msg) /* Close. */
658     return NULL;
659
660   if (*context == msg || !msg->current_part)
661     return NULL;
662
663   l = *context ? (HDR_LINE) *context : msg->current_part->hdr_lines;
664
665   if (l)
666     {
667       *context = l->next ? (void *) (l->next) : (void *) msg;
668       return l->line;
669     }
670   *context = msg; /* Mark end of list. */
671   return NULL;
672 }
673
674
675
676 /****************
677  * Find a header field.  If the Name does end in an asterisk this is meant
678  * to be a wildcard.
679  *
680  *  which  -1 : Retrieve the last field
681  *         >0 : Retrieve the n-th field
682
683  * RPREV may be used to return the predecessor of the returned field;
684  * which may be NULL for the very first one. It has to be initialized
685  * to either NULL in which case the search start at the first header line,
686  * or it may point to a headerline, where the search should start
687  */
688 static HDR_LINE
689 find_header (rfc822parse_t msg, const char *name, int which, HDR_LINE *rprev)
690 {
691   HDR_LINE hdr, prev = NULL, mark = NULL;
692   unsigned char *p;
693   size_t namelen, n;
694   int found = 0;
695   int glob = 0;
696
697   if (!msg->current_part)
698     return NULL;
699
700   namelen = strlen (name);
701   if (namelen && name[namelen - 1] == '*')
702     {
703       namelen--;
704       glob = 1;
705     }
706
707   hdr = msg->current_part->hdr_lines;
708   if (rprev && *rprev)
709     {
710       /* spool forward to the requested starting place.
711        * we cannot simply set this as we have to return
712        * the previous list element too */
713       for (; hdr && hdr != *rprev; prev = hdr, hdr = hdr->next)
714         ;
715     }
716
717   for (; hdr; prev = hdr, hdr = hdr->next)
718     {
719       if (hdr->cont)
720         continue;
721       if (!(p = strchr (hdr->line, ':')))
722         continue;               /* invalid header, just skip it. */
723       n = p - hdr->line;
724       if (!n)
725         continue;               /* invalid name */
726       if ((glob ? (namelen <= n) : (namelen == n))
727           && !memcmp (hdr->line, name, namelen))
728         {
729           found++;
730           if (which == -1)
731             mark = hdr;
732           else if (found == which)
733             {
734               if (rprev)
735                 *rprev = prev;
736               return hdr;
737             }
738         }
739     }
740   if (mark && rprev)
741     *rprev = prev;
742   return mark;
743 }
744
745
746
747 static const char *
748 skip_ws (const char *s)
749 {
750   while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
751     s++;
752   return s;
753 }
754
755
756 static void
757 release_token_list (TOKEN t)
758 {
759   while (t)
760     {
761       TOKEN t2 = t->next;
762       /* fixme: If we have owner_pantry, put the token back to
763        * this pantry so that it can be reused later */
764       free (t);
765       t = t2;
766     }
767 }
768
769
770 static TOKEN
771 new_token (enum token_type type, const char *buf, size_t length)
772 {
773   TOKEN t;
774
775   /* fixme: look through our pantries to find a suitable
776    * token for reuse */
777   t = malloc (sizeof *t + length);
778   if (t)
779     {
780       t->next = NULL;
781       t->type = type;
782       memset (&t->flags, 0, sizeof (t->flags));
783       t->data[0] = 0;
784       if (buf)
785         {
786           memcpy (t->data, buf, length);
787           t->data[length] = 0;  /* Make sure it is a C string. */
788         }
789       else
790         t->data[0] = 0;
791     }
792   return t;
793 }
794
795 static TOKEN
796 append_to_token (TOKEN old, const char *buf, size_t length)
797 {
798   size_t n = strlen (old->data);
799   TOKEN t;
800
801   t = malloc (sizeof *t + n + length);
802   if (t)
803     {
804       t->next = old->next;
805       t->type = old->type;
806       t->flags = old->flags;
807       memcpy (t->data, old->data, n);
808       memcpy (t->data + n, buf, length);
809       t->data[n + length] = 0;
810       old->next = NULL;
811       release_token_list (old);
812     }
813   return t;
814 }
815
816
817
818 /*
819    Parse a field into tokens as defined by rfc822.
820  */
821 static TOKEN
822 parse_field (HDR_LINE hdr)
823 {
824   static const char specials[] = "<>@.,;:\\[]\"()";
825   static const char specials2[] = "<>@.,;:";
826   static const char tspecials[] = "/?=<>@,;:\\[]\"()";
827   static const char tspecials2[] = "/?=<>@.,;:";  /* FIXME: really
828                                                      include '.'?*/
829   static struct
830   {
831     const unsigned char *name;
832     size_t namelen;
833   } tspecial_header[] = {
834     { "Content-Type", 12},
835     { "Content-Transfer-Encoding", 25},
836     { "Content-Disposition", 19},
837     { NULL, 0}
838   };
839   const char *delimiters;
840   const char *delimiters2;
841   const unsigned char *line, *s, *s2;
842   size_t n;
843   int i, invalid = 0;
844   TOKEN t, tok, *tok_tail;
845
846   errno = 0;
847   if (!hdr)
848     return NULL;
849
850   tok = NULL;
851   tok_tail = &tok;
852
853   line = hdr->line;
854   if (!(s = strchr (line, ':')))
855     return NULL; /* oops */
856
857   n = s - line;
858   if (!n)
859     return NULL; /* oops: invalid name */
860
861   delimiters = specials;
862   delimiters2 = specials2;
863   for (i = 0; tspecial_header[i].name; i++)
864     {
865       if (n == tspecial_header[i].namelen
866           && !memcmp (line, tspecial_header[i].name, n))
867         {
868           delimiters = tspecials;
869           delimiters2 = tspecials2;
870           break;
871         }
872     }
873
874   s++; /* Move over the colon. */
875   for (;;)
876     {
877       while (!*s)
878         {
879           if (!hdr->next || !hdr->next->cont)
880             return tok; /* Ready.  */
881
882           /* Next item is a header continuation line.  */
883           hdr = hdr->next;
884           s = hdr->line;
885         }
886
887       if (*s == '(')
888         {
889           int level = 1;
890           int in_quote = 0;
891
892           invalid = 0;
893           for (s++;; s++)
894             {
895               while (!*s)
896                 {
897                   if (!hdr->next || !hdr->next->cont)
898                     goto oparen_out;
899                   /* Next item is a header continuation line.  */
900                   hdr = hdr->next;
901                   s = hdr->line;
902                 }
903
904               if (in_quote)
905                 {
906                   if (*s == '\"')
907                     in_quote = 0;
908                   else if (*s == '\\' && s[1])  /* what about continuation? */
909                     s++;
910                 }
911               else if (*s == ')')
912                 {
913                   if (!--level)
914                     break;
915                 }
916               else if (*s == '(')
917                 level++;
918               else if (*s == '\"')
919                 in_quote = 1;
920             }
921         oparen_out:
922           if (!*s)
923             ; /* Actually this is an error, but we don't care about it. */
924           else
925             s++;
926         }
927       else if (*s == '\"' || *s == '[')
928         {
929           /* We do not check for non-allowed nesting of domainliterals */
930           int term = *s == '\"' ? '\"' : ']';
931           invalid = 0;
932           s++;
933           t = NULL;
934
935           for (;;)
936             {
937               for (s2 = s; *s2; s2++)
938                 {
939                   if (*s2 == term)
940                     break;
941                   else if (*s2 == '\\' && s2[1]) /* what about continuation? */
942                     s2++;
943                 }
944
945               t = (t
946                    ? append_to_token (t, s, s2 - s)
947                    : new_token (term == '\"'? tQUOTED : tDOMAINLIT, s, s2 - s));
948               if (!t)
949                 goto failure;
950
951               if (*s2 || !hdr->next || !hdr->next->cont)
952                 break;
953               /* Next item is a header continuation line.  */
954               hdr = hdr->next;
955               s = hdr->line;
956             }
957           *tok_tail = t;
958           tok_tail = &t->next;
959           s = s2;
960           if (*s)
961             s++; /* skip the delimiter */
962         }
963       else if ((s2 = strchr (delimiters2, *s)))
964         { /* Special characters which are not handled above. */
965           invalid = 0;
966           t = new_token (tSPECIAL, s, 1);
967           if (!t)
968             goto failure;
969           *tok_tail = t;
970           tok_tail = &t->next;
971           s++;
972         }
973       else if (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
974         {
975           invalid = 0;
976           s = skip_ws (s + 1);
977         }
978       else if (*s > 0x20 && !(*s & 128))
979         { /* Atom. */
980           invalid = 0;
981           for (s2 = s + 1; *s2 > 0x20
982                && !(*s2 & 128) && !strchr (delimiters, *s2); s2++)
983             ;
984           t = new_token (tATOM, s, s2 - s);
985           if (!t)
986             goto failure;
987           *tok_tail = t;
988           tok_tail = &t->next;
989           s = s2;
990         }
991       else
992         { /* Invalid character. */
993           if (!invalid)
994             { /* For parsing we assume only one space. */
995               t = new_token (tSPACE, NULL, 0);
996               if (!t)
997                 goto failure;
998               *tok_tail = t;
999               tok_tail = &t->next;
1000               invalid = 1;
1001             }
1002           s++;
1003         }
1004     }
1005   /*NOTREACHED*/
1006
1007  failure:
1008   {
1009     int save = errno;
1010     release_token_list (tok);
1011     errno = save;
1012   }
1013   return NULL;
1014 }
1015
1016
1017
1018
1019 /****************
1020  * Find and parse a header field.
1021  * WHICH indicates what to do if there are multiple instance of the same
1022  * field (like "Received"); the following value are defined:
1023  *  -1 := Take the last occurrence
1024  *   0 := Reserved
1025  *   n := Take the n-th one.
1026  * Returns a handle for further operations on the parse context of the field
1027  * or NULL if the field was not found.
1028  */
1029 rfc822parse_field_t
1030 rfc822parse_parse_field (rfc822parse_t msg, const char *name, int which)
1031 {
1032   HDR_LINE hdr;
1033
1034   if (!which)
1035     return NULL;
1036
1037   hdr = find_header (msg, name, which, NULL);
1038   if (!hdr)
1039     return NULL;
1040   return parse_field (hdr);
1041 }
1042
1043 void
1044 rfc822parse_release_field (rfc822parse_field_t ctx)
1045 {
1046   if (ctx)
1047     release_token_list (ctx);
1048 }
1049
1050
1051
1052 /****************
1053  * Check whether T points to a parameter.
1054  * A parameter starts with a semicolon and it is assumed that t
1055  * points to exactly this one.
1056  */
1057 static int
1058 is_parameter (TOKEN t)
1059 {
1060   t = t->next;
1061   if (!t || t->type != tATOM)
1062     return 0;
1063   t = t->next;
1064   if (!t || !(t->type == tSPECIAL && t->data[0] == '='))
1065     return 0;
1066   t = t->next;
1067   if (!t)
1068     return 1; /* We assume that an non existing value is an empty one. */
1069   return t->type == tQUOTED || t->type == tATOM;
1070 }
1071
1072 /*
1073    Some header (Content-type) have a special syntax where attribute=value
1074    pairs are used after a leading semicolon.  The parse_field code
1075    knows about these fields and changes the parsing to the one defined
1076    in RFC2045.
1077    Returns a pointer to the value which is valid as long as the
1078    parse context is valid; NULL is returned in case that attr is not
1079    defined in the header, a missing value is reppresented by an empty string.
1080
1081    With LOWER_VALUE set to true, a matching field value will be
1082    lowercased.
1083
1084    Note, that ATTR should be lowercase.
1085  */
1086 const char *
1087 rfc822parse_query_parameter (rfc822parse_field_t ctx, const char *attr,
1088                              int lower_value)
1089 {
1090   TOKEN t, a;
1091
1092   for (t = ctx; t; t = t->next)
1093     {
1094       /* skip to the next semicolon */
1095       for (; t && !(t->type == tSPECIAL && t->data[0] == ';'); t = t->next)
1096         ;
1097       if (!t)
1098         return NULL;
1099       if (is_parameter (t))
1100         { /* Look closer. */
1101           a = t->next; /* We know that this is an atom */
1102           if ( !a->flags.lowered )
1103             {
1104               lowercase_string (a->data);
1105               a->flags.lowered = 1;
1106             }
1107           if (!strcmp (a->data, attr))
1108             { /* found */
1109               t = a->next->next;
1110               /* Either T is now an atom, a quoted string or NULL in
1111                * which case we return an empty string. */
1112
1113               if ( lower_value && t && !t->flags.lowered )
1114                 {
1115                   lowercase_string (t->data);
1116                   t->flags.lowered = 1;
1117                 }
1118               return t ? t->data : "";
1119             }
1120         }
1121     }
1122   return NULL;
1123 }
1124
1125 /****************
1126  * This function may be used for the Content-Type header to figure out
1127  * the media type and subtype.  Note, that the returned strings are
1128  * guaranteed to be lowercase as required by MIME.
1129  *
1130  * Returns: a pointer to the media type and if subtype is not NULL,
1131  *          a pointer to the subtype.
1132  */
1133 const char *
1134 rfc822parse_query_media_type (rfc822parse_field_t ctx, const char **subtype)
1135 {
1136   TOKEN t = ctx;
1137   const char *type;
1138
1139   if (t->type != tATOM)
1140     return NULL;
1141   if (!t->flags.lowered)
1142     {
1143       lowercase_string (t->data);
1144       t->flags.lowered = 1;
1145     }
1146   type = t->data;
1147   t = t->next;
1148   if (!t || t->type != tSPECIAL || t->data[0] != '/')
1149     return NULL;
1150   t = t->next;
1151   if (!t || t->type != tATOM)
1152     return NULL;
1153
1154   if (subtype)
1155     {
1156       if (!t->flags.lowered)
1157         {
1158           lowercase_string (t->data);
1159           t->flags.lowered = 1;
1160         }
1161       *subtype = t->data;
1162     }
1163   return type;
1164 }
1165
1166
1167
1168
1169
1170 #ifdef TESTING
1171
1172 /* Internal debug function to print the structure of the message. */
1173 static void
1174 dump_structure (rfc822parse_t msg, part_t part, int indent)
1175 {
1176   if (!part)
1177     {
1178       printf ("*** Structure of this message:\n");
1179       part = msg->parts;
1180     }
1181
1182   for (; part; part = part->right)
1183     {
1184       rfc822parse_field_t ctx;
1185       part_t save_part; /* ugly hack - we should have a function to
1186                            get part information. */
1187       const char *s;
1188
1189       save_part = msg->current_part;
1190       msg->current_part = part;
1191       ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
1192       msg->current_part = save_part;
1193       if (ctx)
1194         {
1195           const char *s1, *s2;
1196           s1 = rfc822parse_query_media_type (ctx, &s2);
1197           if (s1)
1198             printf ("***   %*s %s/%s", indent*2, "", s1, s2);
1199           else
1200             printf ("***   %*s [not found]", indent*2, "");
1201
1202           s = rfc822parse_query_parameter (ctx, "boundary", 0);
1203           if (s)
1204             printf (" (boundary=\"%s\")", s);
1205           rfc822parse_release_field (ctx);
1206         }
1207       else
1208         printf ("***   %*s text/plain [assumed]", indent*2, "");
1209       putchar('\n');
1210
1211       if (part->down)
1212         dump_structure (msg, part->down, indent + 1);
1213     }
1214
1215 }
1216
1217
1218
1219 static void
1220 show_param (rfc822parse_field_t ctx, const char *name)
1221 {
1222   const char *s;
1223
1224   if (!ctx)
1225     return;
1226   s = rfc822parse_query_parameter (ctx, name, 0);
1227   if (s)
1228     printf ("***   %s: '%s'\n", name, s);
1229 }
1230
1231
1232
1233 static void
1234 show_event (rfc822parse_event_t event)
1235 {
1236   const char *s;
1237
1238   switch (event)
1239     {
1240     case RFC822PARSE_OPEN: s= "Open"; break;
1241     case RFC822PARSE_CLOSE: s= "Close"; break;
1242     case RFC822PARSE_CANCEL: s= "Cancel"; break;
1243     case RFC822PARSE_T2BODY: s= "T2Body"; break;
1244     case RFC822PARSE_FINISH: s= "Finish"; break;
1245     case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
1246     case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break;
1247     case RFC822PARSE_LEVEL_UP:   s= "Level_Up"; break;
1248     case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
1249     case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
1250     case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break;
1251     case RFC822PARSE_PREAMBLE: s= "Preamble"; break;
1252     case RFC822PARSE_EPILOGUE: s= "Epilogue"; break;
1253     default: s= "***invalid event***"; break;
1254     }
1255   printf ("*** got RFC822 event %s\n", s);
1256 }
1257
1258 static int
1259 msg_cb (void *dummy_arg, rfc822parse_event_t event, rfc822parse_t msg)
1260 {
1261   show_event (event);
1262   if (event == RFC822PARSE_T2BODY)
1263     {
1264       rfc822parse_field_t ctx;
1265       void *ectx;
1266       const char *line;
1267
1268       for (ectx=NULL; (line = rfc822parse_enum_header_lines (msg, &ectx)); )
1269         {
1270           printf ("*** HDR: %s\n", line);
1271         }
1272       rfc822parse_enum_header_lines (NULL, &ectx); /* Close enumerator. */
1273
1274       ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
1275       if (ctx)
1276         {
1277           const char *s1, *s2;
1278           s1 = rfc822parse_query_media_type (ctx, &s2);
1279           if (s1)
1280             printf ("***   media: '%s/%s'\n", s1, s2);
1281           else
1282             printf ("***   media: [not found]\n");
1283           show_param (ctx, "boundary");
1284           show_param (ctx, "protocol");
1285           rfc822parse_release_field (ctx);
1286         }
1287       else
1288         printf ("***   media: text/plain [assumed]\n");
1289
1290     }
1291
1292
1293   return 0;
1294 }
1295
1296
1297
1298 int
1299 main (int argc, char **argv)
1300 {
1301   char line[5000];
1302   size_t length;
1303   rfc822parse_t msg;
1304
1305   msg = rfc822parse_open (msg_cb, NULL);
1306   if (!msg)
1307     abort ();
1308
1309   while (fgets (line, sizeof (line), stdin))
1310     {
1311       length = strlen (line);
1312       if (length && line[length - 1] == '\n')
1313         line[--length] = 0;
1314       if (length && line[length - 1] == '\r')
1315         line[--length] = 0;
1316       if (rfc822parse_insert (msg, line, length))
1317         abort ();
1318     }
1319
1320   dump_structure (msg, NULL, 0);
1321
1322   rfc822parse_close (msg);
1323   return 0;
1324 }
1325 #endif
1326
1327 /*
1328 Local Variables:
1329 compile-command: "gcc -Wall -Wno-pointer-sign -g -DTESTING -o rfc822parse rfc822parse.c"
1330 End:
1331 */