aboutsummaryrefslogtreecommitdiffstats
path: root/tools/rfc822parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/rfc822parse.c')
-rw-r--r--tools/rfc822parse.c1235
1 files changed, 0 insertions, 1235 deletions
diff --git a/tools/rfc822parse.c b/tools/rfc822parse.c
deleted file mode 100644
index be1cf4a47..000000000
--- a/tools/rfc822parse.c
+++ /dev/null
@@ -1,1235 +0,0 @@
-/* rfc822parse.c - Simple mail and MIME parser
- * Copyright (C) 1999, 2000 Werner Koch, Duesseldorf
- * Copyright (C) 2003, g10 Code GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-
-/* According to RFC822 binary 0 are allowed at many places. We
- * do not handle this correct especially in the field parsing code. It
- * should be easy to fix and the API provides a interfcaes which returns
- * the length but in addition makes sure that returned strings are always
- * ended by a \0.
- *
- * Furthermore, the case of field names is changed and thus it is not
- * always a good idea to use these modified header
- * lines (e.g. signatures may break).
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <assert.h>
-
-#include "rfc822parse.h"
-
-enum token_type
-{
- tSPACE,
- tATOM,
- tQUOTED,
- tDOMAINLIT,
- tSPECIAL
-};
-
-/* For now we directly use our TOKEN as the parse context */
-typedef struct rfc822parse_field_context *TOKEN;
-struct rfc822parse_field_context
-{
- TOKEN next;
- enum token_type type;
- struct {
- unsigned int cont:1;
- unsigned int lowered:1;
- } flags;
- /*TOKEN owner_pantry; */
- char data[1];
-};
-
-struct hdr_line
-{
- struct hdr_line *next;
- int cont; /* This is a continuation of the previous line. */
- unsigned char line[1];
-};
-
-typedef struct hdr_line *HDR_LINE;
-
-
-struct part
-{
- struct part *right; /* The next part. */
- struct part *down; /* A contained part. */
- HDR_LINE hdr_lines; /* Header lines os that part. */
- HDR_LINE *hdr_lines_tail; /* Helper for adding lines. */
- char *boundary; /* Only used in the first part. */
-};
-typedef struct part *part_t;
-
-struct rfc822parse_context
-{
- rfc822parse_cb_t callback;
- void *callback_value;
- int callback_error;
- int in_body;
- int in_preamble; /* Wether we are before the first boundary. */
- part_t parts; /* The tree of parts. */
- part_t current_part; /* Whom we are processing (points into parts). */
- const char *boundary; /* Current boundary. */
-};
-
-static HDR_LINE find_header (rfc822parse_t msg, const char *name,
- int which, HDR_LINE * rprev);
-
-
-static size_t
-length_sans_trailing_ws (const unsigned char *line, size_t len)
-{
- const unsigned char *p, *mark;
- size_t n;
-
- for (mark=NULL, p=line, n=0; n < len; n++, p++)
- {
- if (strchr (" \t\r\n", *p ))
- {
- if( !mark )
- mark = p;
- }
- else
- mark = NULL;
- }
-
- if (mark)
- return mark - line;
- return len;
-}
-
-
-static void
-lowercase_string (unsigned char *string)
-{
- for (; *string; string++)
- if (*string >= 'A' && *string <= 'Z')
- *string = *string - 'A' + 'a';
-}
-
-/* Transform a header name into a standard capitalized format; i.e
- "Content-Type". Conversion stops at the colon. As usual we don't
- use the localized versions of ctype.h.
- */
-static void
-capitalize_header_name (unsigned char *name)
-{
- int first = 1;
-
- for (; *name && *name != ':'; name++)
- if (*name == '-')
- first = 1;
- else if (first)
- {
- if (*name >= 'a' && *name <= 'z')
- *name = *name - 'a' + 'A';
- first = 0;
- }
- else if (*name >= 'A' && *name <= 'Z')
- *name = *name - 'A' + 'a';
-}
-
-
-static char *
-stpcpy (char *a,const char *b)
-{
- while (*b)
- *a++ = *b++;
- *a = 0;
-
- return (char*)a;
-}
-
-
-/* If a callback has been registerd, call it for the event of type
- EVENT. */
-static int
-do_callback (rfc822parse_t msg, rfc822parse_event_t event)
-{
- int rc;
-
- if (!msg->callback || msg->callback_error)
- return 0;
- rc = msg->callback (msg->callback_value, event, msg);
- if (rc)
- msg->callback_error = rc;
- return rc;
-}
-
-static part_t
-new_part (void)
-{
- part_t part;
-
- part = calloc (1, sizeof *part);
- if (part)
- {
- part->hdr_lines_tail = &part->hdr_lines;
- }
- return part;
-}
-
-
-static void
-release_part (part_t part)
-{
- part_t tmp;
- HDR_LINE hdr, hdr2;
-
- for (; part; part = tmp)
- {
- tmp = part->right;
- if (part->down)
- release_part (part->down);
- for (hdr = part->hdr_lines; hdr; hdr = hdr2)
- {
- hdr2 = hdr->next;
- free (hdr);
- }
- free (part->boundary);
- free (part);
- }
-}
-
-
-static void
-release_handle_data (rfc822parse_t msg)
-{
- release_part (msg->parts);
- msg->parts = NULL;
- msg->current_part = NULL;
- msg->boundary = NULL;
-}
-
-
-/* Create a new parsing context for an entire rfc822 message and
- return it. CB and CB_VALUE may be given to callback for certain
- events. NULL is returned on error with errno set appropriately. */
-rfc822parse_t
-rfc822parse_open (rfc822parse_cb_t cb, void *cb_value)
-{
- rfc822parse_t msg = calloc (1, sizeof *msg);
- if (msg)
- {
- msg->parts = msg->current_part = new_part ();
- if (!msg->parts)
- {
- free (msg);
- msg = NULL;
- }
- else
- {
- msg->callback = cb;
- msg->callback_value = cb_value;
- if (do_callback (msg, RFC822PARSE_OPEN))
- {
- release_handle_data (msg);
- free (msg);
- msg = NULL;
- }
- }
- }
- return msg;
-}
-
-
-void
-rfc822parse_cancel (rfc822parse_t msg)
-{
- if (msg)
- {
- do_callback (msg, RFC822PARSE_CANCEL);
- release_handle_data (msg);
- free (msg);
- }
-}
-
-
-void
-rfc822parse_close (rfc822parse_t msg)
-{
- if (msg)
- {
- do_callback (msg, RFC822PARSE_CLOSE);
- release_handle_data (msg);
- free (msg);
- }
-}
-
-static part_t
-find_parent (part_t tree, part_t target)
-{
- part_t part;
-
- for (part = tree->down; part; part = part->right)
- {
- if (part == target)
- return tree; /* Found. */
- if (part->down)
- {
- part_t tmp = find_parent (part, target);
- if (tmp)
- return tmp;
- }
- }
- return NULL;
-}
-
-static void
-set_current_part_to_parent (rfc822parse_t msg)
-{
- part_t parent;
-
- assert (msg->current_part);
- parent = find_parent (msg->parts, msg->current_part);
- if (!parent)
- return; /* Already at the top. */
-
-#ifndef NDEBUG
- {
- part_t part;
- for (part = parent->down; part; part = part->right)
- if (part == msg->current_part)
- break;
- assert (part);
- }
-#endif
- msg->current_part = parent;
-
- parent = find_parent (msg->parts, parent);
- msg->boundary = parent? parent->boundary: NULL;
-}
-
-
-
-/****************
- * We have read in all header lines and are about to receive the body
- * part. The delimiter line has already been processed.
- *
- * FIXME: we's better return an error in case of memory failures.
- */
-static int
-transition_to_body (rfc822parse_t msg)
-{
- rfc822parse_field_t ctx;
- int rc;
-
- rc = do_callback (msg, RFC822PARSE_T2BODY);
- if (!rc)
- {
- /* Store the boundary if we have multipart type. */
- ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
- if (ctx)
- {
- const char *s;
-
- s = rfc822parse_query_media_type (ctx, NULL);
- if (s && !strcmp (s,"multipart"))
- {
- s = rfc822parse_query_parameter (ctx, "boundary", 0);
- if (s)
- {
- assert (!msg->current_part->boundary);
- msg->current_part->boundary = malloc (strlen (s) + 1);
- if (msg->current_part->boundary)
- {
- part_t part;
-
- strcpy (msg->current_part->boundary, s);
- msg->boundary = msg->current_part->boundary;
- part = new_part ();
- if (!part)
- {
- int save_errno = errno;
- rfc822parse_release_field (ctx);
- errno = save_errno;
- return -1;
- }
- rc = do_callback (msg, RFC822PARSE_LEVEL_DOWN);
- assert (!msg->current_part->down);
- msg->current_part->down = part;
- msg->current_part = part;
- msg->in_preamble = 1;
- }
- }
- }
- rfc822parse_release_field (ctx);
- }
- }
-
- return rc;
-}
-
-/* We have just passed a MIME boundary and need to prepare for new part.
- headers. */
-static int
-transition_to_header (rfc822parse_t msg)
-{
- part_t part;
-
- assert (msg->current_part);
- assert (!msg->current_part->right);
-
- part = new_part ();
- if (!part)
- return -1;
-
- msg->current_part->right = part;
- msg->current_part = part;
- return 0;
-}
-
-
-static int
-insert_header (rfc822parse_t msg, const unsigned char *line, size_t length)
-{
- HDR_LINE hdr;
-
- assert (msg->current_part);
- if (!length)
- {
- msg->in_body = 1;
- return transition_to_body (msg);
- }
-
- if (!msg->current_part->hdr_lines)
- do_callback (msg, RFC822PARSE_BEGIN_HEADER);
-
- length = length_sans_trailing_ws (line, length);
- hdr = malloc (sizeof (*hdr) + length);
- if (!hdr)
- return -1;
- hdr->next = NULL;
- hdr->cont = (*line == ' ' || *line == '\t');
- memcpy (hdr->line, line, length);
- hdr->line[length] = 0; /* Make it a string. */
-
- /* Transform a field name into canonical format. */
- if (!hdr->cont && strchr (line, ':'))
- capitalize_header_name (hdr->line);
-
- *msg->current_part->hdr_lines_tail = hdr;
- msg->current_part->hdr_lines_tail = &hdr->next;
-
- /* Lets help the caller to prevent mail loops and issue an event for
- * every Received header. */
- if (length >= 9 && !memcmp (line, "Received:", 9))
- do_callback (msg, RFC822PARSE_RCVD_SEEN);
- return 0;
-}
-
-
-/****************
- * Note: We handle the body transparent to allow binary zeroes in it.
- */
-static int
-insert_body (rfc822parse_t msg, const unsigned char *line, size_t length)
-{
- int rc = 0;
-
- if (length > 2 && *line == '-' && line[1] == '-' && msg->boundary)
- {
- size_t blen = strlen (msg->boundary);
-
- if (length == blen + 2
- && !memcmp (line+2, msg->boundary, blen))
- {
- rc = do_callback (msg, RFC822PARSE_BOUNDARY);
- msg->in_body = 0;
- if (!rc && !msg->in_preamble)
- rc = transition_to_header (msg);
- msg->in_preamble = 0;
- }
- else if (length == blen + 4
- && line[length-2] =='-' && line[length-1] == '-'
- && !memcmp (line+2, msg->boundary, blen))
- {
- rc = do_callback (msg, RFC822PARSE_LAST_BOUNDARY);
- msg->boundary = NULL; /* No current boundary anymore. */
- set_current_part_to_parent (msg);
-
- /* Fixme: The next should acctually be sent right before the
- next boundary, so that we can mark the epilogue. */
- if (!rc)
- rc = do_callback (msg, RFC822PARSE_LEVEL_UP);
- }
- }
- if (msg->in_preamble && !rc)
- rc = do_callback (msg, RFC822PARSE_PREAMBLE);
-
- return rc;
-}
-
-/* Insert the next line into the parser. Return 0 on success or true
- on error with errno set appropriately. */
-int
-rfc822parse_insert (rfc822parse_t msg, const unsigned char *line, size_t length)
-{
- return (msg->in_body
- ? insert_body (msg, line, length)
- : insert_header (msg, line, length));
-}
-
-
-/* Tell the parser that we have finished the message. */
-int
-rfc822parse_finish (rfc822parse_t msg)
-{
- return do_callback (msg, RFC822PARSE_FINISH);
-}
-
-
-
-/****************
- * Get a copy of a header line. The line is returned as one long
- * string with LF to separate the continuation line. Caller must free
- * the return buffer. which may be used to enumerate over all lines.
- * Wildcards are allowed. This function works on the current headers;
- * i.e. the regular mail headers or the MIME headers of the current
- * part.
- *
- * WHICH gives the mode:
- * -1 := Take the last occurence
- * n := Take the n-th one.
- *
- * Returns a newly allocated buffer or NULL on error. errno is set in
- * case of a memory failure or set to 0 if the requested field is not
- * available.
- */
-char *
-rfc822parse_get_field (rfc822parse_t msg, const char *name, int which)
-{
- HDR_LINE h, h2;
- char *buf, *p;
- size_t n;
-
- h = find_header (msg, name, which, NULL);
- if (!h)
- {
- errno = 0;
- return NULL; /* no such field */
- }
-
- n = strlen (h->line) + 1;
- for (h2 = h->next; h2 && h2->cont; h2 = h2->next)
- n += strlen (h2->line) + 1;
-
- buf = p = malloc (n);
- if (buf)
- {
- p = stpcpy (p, h->line);
- *p++ = '\n';
- for (h2 = h->next; h2 && h2->cont; h2 = h2->next)
- {
- p = stpcpy (p, h2->line);
- *p++ = '\n';
- }
- p[-1] = 0;
- }
- return buf;
-}
-
-
-/****************
- * Enumerate all header. Caller has to provide the address of a pointer
- * which has to be initialzed to NULL, the caller should then never change this
- * pointer until he has closed the enumeration by passing again the address
- * of the pointer but with msg set to NULL.
- * The function returns pointers to all the header lines or NULL when
- * all lines have been enumerated or no headers are available.
- */
-const char *
-rfc822parse_enum_header_lines (rfc822parse_t msg, void **context)
-{
- HDR_LINE l;
-
- if (!msg) /* Close. */
- return NULL;
-
- if (*context == msg || !msg->current_part)
- return NULL;
-
- l = *context ? (HDR_LINE) *context : msg->current_part->hdr_lines;
-
- if (l)
- {
- *context = l->next ? (void *) (l->next) : (void *) msg;
- return l->line;
- }
- *context = msg; /* Mark end of list. */
- return NULL;
-}
-
-
-
-/****************
- * Find a header field. If the Name does end in an asterisk this is meant
- * to be a wildcard.
- *
- * which -1 : Retrieve the last field
- * >0 : Retrieve the n-th field
-
- * RPREV may be used to return the predecessor of the returned field;
- * which may be NULL for the very first one. It has to be initialzed
- * to either NULL in which case the search start at the first header line,
- * or it may point to a headerline, where the search should start
- */
-static HDR_LINE
-find_header (rfc822parse_t msg, const char *name, int which, HDR_LINE *rprev)
-{
- HDR_LINE hdr, prev = NULL, mark = NULL;
- unsigned char *p;
- size_t namelen, n;
- int found = 0;
- int glob = 0;
-
- if (!msg->current_part)
- return NULL;
-
- namelen = strlen (name);
- if (namelen && name[namelen - 1] == '*')
- {
- namelen--;
- glob = 1;
- }
-
- hdr = msg->current_part->hdr_lines;
- if (rprev && *rprev)
- {
- /* spool forward to the requested starting place.
- * we cannot simply set this as we have to return
- * the previous list element too */
- for (; hdr && hdr != *rprev; prev = hdr, hdr = hdr->next)
- ;
- }
-
- for (; hdr; prev = hdr, hdr = hdr->next)
- {
- if (hdr->cont)
- continue;
- if (!(p = strchr (hdr->line, ':')))
- continue; /* invalid header, just skip it. */
- n = p - hdr->line;
- if (!n)
- continue; /* invalid name */
- if ((glob ? (namelen <= n) : (namelen == n))
- && !memcmp (hdr->line, name, namelen))
- {
- found++;
- if (which == -1)
- mark = hdr;
- else if (found == which)
- {
- if (rprev)
- *rprev = prev;
- return hdr;
- }
- }
- }
- if (mark && rprev)
- *rprev = prev;
- return mark;
-}
-
-
-
-static const char *
-skip_ws (const char *s)
-{
- while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
- s++;
- return s;
-}
-
-
-static void
-release_token_list (TOKEN t)
-{
- while (t)
- {
- TOKEN t2 = t->next;
- /* fixme: If we have owner_pantry, put the token back to
- * this pantry so that it can be reused later */
- free (t);
- t = t2;
- }
-}
-
-
-static TOKEN
-new_token (enum token_type type, const char *buf, size_t length)
-{
- TOKEN t;
-
- /* fixme: look through our pantries to find a suitable
- * token for reuse */
- t = malloc (sizeof *t + length);
- if (t)
- {
- t->next = NULL;
- t->type = type;
- memset (&t->flags, 0, sizeof (t->flags));
- t->data[0] = 0;
- if (buf)
- {
- memcpy (t->data, buf, length);
- t->data[length] = 0; /* Make sure it is a C string. */
- }
- else
- t->data[0] = 0;
- }
- return t;
-}
-
-static TOKEN
-append_to_token (TOKEN old, const char *buf, size_t length)
-{
- size_t n = strlen (old->data);
- TOKEN t;
-
- t = malloc (sizeof *t + n + length);
- if (t)
- {
- t->next = old->next;
- t->type = old->type;
- t->flags = old->flags;
- memcpy (t->data, old->data, n);
- memcpy (t->data + n, buf, length);
- t->data[n + length] = 0;
- old->next = NULL;
- release_token_list (old);
- }
- return t;
-}
-
-
-
-/*
- Parse a field into tokens as defined by rfc822.
- */
-static TOKEN
-parse_field (HDR_LINE hdr)
-{
- static const char specials[] = "<>@.,;:\\[]\"()";
- static const char specials2[] = "<>@.,;:";
- static const char tspecials[] = "/?=<>@,;:\\[]\"()";
- static const char tspecials2[] = "/?=<>@.,;:";
- static struct
- {
- const unsigned char *name;
- size_t namelen;
- } tspecial_header[] = {
- { "Content-Type", 12},
- { "Content-Transfer-Encoding", 25},
- { NULL, 0}
- };
- const char *delimiters;
- const char *delimiters2;
- const unsigned char *line, *s, *s2;
- size_t n;
- int i, invalid = 0;
- TOKEN t, tok, *tok_tail;
-
- errno = 0;
- if (!hdr)
- return NULL;
-
- tok = NULL;
- tok_tail = &tok;
-
- line = hdr->line;
- if (!(s = strchr (line, ':')))
- return NULL; /* oops */
-
- n = s - line;
- if (!n)
- return NULL; /* oops: invalid name */
-
- delimiters = specials;
- delimiters2 = specials2;
- for (i = 0; tspecial_header[i].name; i++)
- {
- if (n == tspecial_header[i].namelen
- && !memcmp (line, tspecial_header[i].name, n))
- {
- delimiters = tspecials;
- delimiters2 = tspecials2;
- break;
- }
- }
-
- s++; /* Move over the colon. */
- for (;;)
- {
- if (!*s)
- {
- if (!hdr->next || !hdr->next->cont)
- break;
- hdr = hdr->next;
- s = hdr->line;
- }
-
- if (*s == '(')
- {
- int level = 1;
- int in_quote = 0;
-
- invalid = 0;
- for (s++;; s++)
- {
- if (!*s)
- {
- if (!hdr->next || !hdr->next->cont)
- break;
- hdr = hdr->next;
- s = hdr->line;
- }
-
- if (in_quote)
- {
- if (*s == '\"')
- in_quote = 0;
- else if (*s == '\\' && s[1]) /* what about continuation? */
- s++;
- }
- else if (*s == ')')
- {
- if (!--level)
- break;
- }
- else if (*s == '(')
- level++;
- else if (*s == '\"')
- in_quote = 1;
- }
- if (!*s)
- ; /* Actually this is an error, but we don't care about it. */
- else
- s++;
- }
- else if (*s == '\"' || *s == '[')
- {
- /* We do not check for non-allowed nesting of domainliterals */
- int term = *s == '\"' ? '\"' : ']';
- invalid = 0;
- s++;
- t = NULL;
-
- for (;;)
- {
- for (s2 = s; *s2; s2++)
- {
- if (*s2 == term)
- break;
- else if (*s2 == '\\' && s2[1]) /* what about continuation? */
- s2++;
- }
-
- t = (t
- ? append_to_token (t, s, s2 - s)
- : new_token (term == '\"'? tQUOTED : tDOMAINLIT, s, s2 - s));
- if (!t)
- goto failure;
-
- if (*s2 || !hdr->next || !hdr->next->cont)
- break;
- hdr = hdr->next;
- s = hdr->line;
- }
- *tok_tail = t;
- tok_tail = &t->next;
- s = s2;
- if (*s)
- s++; /* skip the delimiter */
- }
- else if ((s2 = strchr (delimiters2, *s)))
- { /* Special characters which are not handled above. */
- invalid = 0;
- t = new_token (tSPECIAL, s, 1);
- if (!t)
- goto failure;
- *tok_tail = t;
- tok_tail = &t->next;
- s++;
- }
- else if (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
- {
- invalid = 0;
- s = skip_ws (s + 1);
- }
- else if (*s > 0x20 && !(*s & 128))
- { /* Atom. */
- invalid = 0;
- for (s2 = s + 1; *s2 > 0x20
- && !(*s2 & 128) && !strchr (delimiters, *s2); s2++)
- ;
- t = new_token (tATOM, s, s2 - s);
- if (!t)
- goto failure;
- *tok_tail = t;
- tok_tail = &t->next;
- s = s2;
- }
- else
- { /* Invalid character. */
- if (!invalid)
- { /* For parsing we assume only one space. */
- t = new_token (tSPACE, NULL, 0);
- if (!t)
- goto failure;
- *tok_tail = t;
- tok_tail = &t->next;
- invalid = 1;
- }
- s++;
- }
- }
-
- return tok;
-
- failure:
- {
- int save = errno;
- release_token_list (tok);
- errno = save;
- }
- return NULL;
-}
-
-
-
-
-/****************
- * Find and parse a header field.
- * WHICH indicates what to do if there are multiple instance of the same
- * field (like "Received"); the following value are defined:
- * -1 := Take the last occurence
- * 0 := Reserved
- * n := Take the n-th one.
- * Returns a handle for further operations on the parse context of the field
- * or NULL if the field was not found.
- */
-rfc822parse_field_t
-rfc822parse_parse_field (rfc822parse_t msg, const char *name, int which)
-{
- HDR_LINE hdr;
-
- if (!which)
- return NULL;
-
- hdr = find_header (msg, name, which, NULL);
- if (!hdr)
- return NULL;
- return parse_field (hdr);
-}
-
-void
-rfc822parse_release_field (rfc822parse_field_t ctx)
-{
- if (ctx)
- release_token_list (ctx);
-}
-
-
-
-/****************
- * Check whether T points to a parameter.
- * A parameter starts with a semicolon and it is assumed that t
- * points to exactly this one.
- */
-static int
-is_parameter (TOKEN t)
-{
- t = t->next;
- if (!t || t->type != tATOM)
- return 0;
- t = t->next;
- if (!t || !(t->type == tSPECIAL && t->data[0] == '='))
- return 0;
- t = t->next;
- if (!t)
- return 1; /* We assume that an non existing value is an empty one. */
- return t->type == tQUOTED || t->type == tATOM;
-}
-
-/*
- Some header (Content-type) have a special syntax where attribute=value
- pairs are used after a leading semicolon. The parse_field code
- knows about these fields and changes the parsing to the one defined
- in RFC2045.
- Returns a pointer to the value which is valid as long as the
- parse context is valid; NULL is returned in case that attr is not
- defined in the header, a missing value is reppresented by an empty string.
-
- With LOWER_VALUE set to true, a matching field valuebe be
- lowercased.
-
- Note, that ATTR should be lowercase.
- */
-const char *
-rfc822parse_query_parameter (rfc822parse_field_t ctx, const char *attr,
- int lower_value)
-{
- TOKEN t, a;
-
- for (t = ctx; t; t = t->next)
- {
- /* skip to the next semicolon */
- for (; t && !(t->type == tSPECIAL && t->data[0] == ';'); t = t->next)
- ;
- if (!t)
- return NULL;
- if (is_parameter (t))
- { /* Look closer. */
- a = t->next; /* We know that this is an atom */
- if ( !a->flags.lowered )
- {
- lowercase_string (a->data);
- a->flags.lowered = 1;
- }
- if (!strcmp (a->data, attr))
- { /* found */
- t = a->next->next;
- /* Either T is now an atom, a quoted string or NULL in
- * which case we return an empty string. */
-
- if ( lower_value && t && !t->flags.lowered )
- {
- lowercase_string (t->data);
- t->flags.lowered = 1;
- }
- return t ? t->data : "";
- }
- }
- }
- return NULL;
-}
-
-/****************
- * This function may be used for the Content-Type header to figure out
- * the media type and subtype. Note, that the returned strings are
- * guaranteed to be lowercase as required by MIME.
- *
- * Returns: a pointer to the media type and if subtype is not NULL,
- * a pointer to the subtype.
- */
-const char *
-rfc822parse_query_media_type (rfc822parse_field_t ctx, const char **subtype)
-{
- TOKEN t = ctx;
- const char *type;
-
- if (t->type != tATOM)
- return NULL;
- if (!t->flags.lowered)
- {
- lowercase_string (t->data);
- t->flags.lowered = 1;
- }
- type = t->data;
- t = t->next;
- if (!t || t->type != tSPECIAL || t->data[0] != '/')
- return NULL;
- t = t->next;
- if (!t || t->type != tATOM)
- return NULL;
-
- if (subtype)
- {
- if (!t->flags.lowered)
- {
- lowercase_string (t->data);
- t->flags.lowered = 1;
- }
- *subtype = t->data;
- }
- return type;
-}
-
-
-
-
-
-#ifdef TESTING
-
-/* Internal debug function to print the structure of the message. */
-static void
-dump_structure (rfc822parse_t msg, part_t part, int indent)
-{
- if (!part)
- {
- printf ("*** Structure of this message:\n");
- part = msg->parts;
- }
-
- for (; part; part = part->right)
- {
- rfc822parse_field_t ctx;
- part_t save_part; /* ugly hack - we should have a function to
- get part inforation. */
- const char *s;
-
- save_part = msg->current_part;
- msg->current_part = part;
- ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
- msg->current_part = save_part;
- if (ctx)
- {
- const char *s1, *s2;
- s1 = rfc822parse_query_media_type (ctx, &s2);
- if (s1)
- printf ("*** %*s %s/%s", indent*2, "", s1, s2);
- else
- printf ("*** %*s [not found]", indent*2, "");
-
- s = rfc822parse_query_parameter (ctx, "boundary", 0);
- if (s)
- printf (" (boundary=\"%s\")", s);
- rfc822parse_release_field (ctx);
- }
- else
- printf ("*** %*s text/plain [assumed]", indent*2, "");
- putchar('\n');
-
- if (part->down)
- dump_structure (msg, part->down, indent + 1);
- }
-
-}
-
-
-
-static void
-show_param (rfc822parse_field_t ctx, const char *name)
-{
- const char *s;
-
- if (!ctx)
- return;
- s = rfc822parse_query_parameter (ctx, name, 0);
- if (s)
- printf ("*** %s: `%s'\n", name, s);
-}
-
-
-
-static void
-show_event (rfc822parse_event_t event)
-{
- const char *s;
-
- switch (event)
- {
- case RFC822PARSE_OPEN: s= "Open"; break;
- case RFC822PARSE_CLOSE: s= "Close"; break;
- case RFC822PARSE_CANCEL: s= "Cancel"; break;
- case RFC822PARSE_T2BODY: s= "T2Body"; break;
- case RFC822PARSE_FINISH: s= "Finish"; break;
- case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
- case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
- case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
- default: s= "***invalid event***"; break;
- }
- printf ("*** got RFC822 event %s\n", s);
-}
-
-static int
-msg_cb (void *dummy_arg, rfc822parse_event_t event, rfc822parse_t msg)
-{
- show_event (event);
- if (event == RFC822PARSE_T2BODY)
- {
- rfc822parse_field_t ctx;
- void *ectx;
- const char *line;
-
- for (ectx=NULL; (line = rfc822parse_enum_header_lines (msg, &ectx)); )
- {
- printf ("*** HDR: %s\n", line);
- }
- rfc822parse_enum_header_lines (NULL, &ectx); /* Close enumerator. */
-
- ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
- if (ctx)
- {
- const char *s1, *s2;
- s1 = rfc822parse_query_media_type (ctx, &s2);
- if (s1)
- printf ("*** media: `%s/%s'\n", s1, s2);
- else
- printf ("*** media: [not found]\n");
- show_param (ctx, "boundary");
- show_param (ctx, "protocol");
- rfc822parse_release_field (ctx);
- }
- else
- printf ("*** media: text/plain [assumed]\n");
-
- }
-
-
- return 0;
-}
-
-
-
-int
-main (int argc, char **argv)
-{
- char line[5000];
- size_t length;
- rfc822parse_t msg;
-
- msg = rfc822parse_open (msg_cb, NULL);
- if (!msg)
- abort ();
-
- while (fgets (line, sizeof (line), stdin))
- {
- length = strlen (line);
- if (length && line[length - 1] == '\n')
- line[--length] = 0;
- if (length && line[length - 1] == '\r')
- line[--length] = 0;
- if (rfc822parse_insert (msg, line, length))
- abort ();
- }
-
- dump_structure (msg, NULL, 0);
-
- rfc822parse_close (msg);
- return 0;
-}
-#endif
-
-/*
-Local Variables:
-compile-command: "gcc -Wall -g -DTESTING -o rfc822parse rfc822parse.c"
-End:
-*/