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