/* $Id: attach.c,v 1.29 2007/08/31 14:16:01 nicm Exp $ */ /* * Copyright (c) 2006 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "fdm.h" char *attach_type(struct mail *, char *, const char *, char **); struct attach *attach_get(struct mail *, char **, size_t *, const char *, int *); struct attach * attach_visit(struct attach *atr, u_int *n) { struct attach *atn; if (atr == NULL) return (NULL); if (TAILQ_EMPTY(&atr->children)) { atn = TAILQ_NEXT(atr, entry); if (atn == NULL) { if (n != NULL) (*n)--; atr = atr->parent; if (atr != NULL) atr = TAILQ_NEXT(atr, entry); } else atr = atn; } else { if (n != NULL) (*n)++; atr = TAILQ_FIRST(&atr->children); } return (atr); } void printflike2 attach_log(struct attach *atr, const char *fmt, ...) { va_list ap; char *prefix; u_int n; va_start(ap, fmt); xvasprintf(&prefix, fmt, ap); va_end(ap); n = 0; while (atr != NULL) { if (TAILQ_EMPTY(&atr->children)) { if (atr->name == NULL) { log_debug3("%s:%*s%u, %s: offset %zu, size %zu," " body %zu", prefix, n + 1, " ", atr->idx, atr->type, atr->data, atr->size, atr->body); } else { log_debug3("%s:%*s%u, %s: offset %zu, size %zu," " body %zu: %s", prefix, n + 1, " ", atr->idx, atr->type, atr->data, atr->size, atr->body, atr->name); } } else { log_debug3("%s:%*s%u, %s", prefix, n + 1, " ", atr->idx, atr->type); } atr = attach_visit(atr, &n); } xfree(prefix); } void attach_free(struct attach *atr) { struct attach *at; while (!TAILQ_EMPTY(&atr->children)) { at = TAILQ_FIRST(&atr->children); TAILQ_REMOVE(&atr->children, at, entry); attach_free(at); } if (atr->type != NULL) xfree(atr->type); if (atr->name != NULL) xfree(atr->name); xfree(atr); } char * attach_type(struct mail *m, char *hdr, const char *name, char **value) { size_t len, llen; ssize_t namelen; char *ptr, *type = NULL; *value = NULL; len = m->size - (hdr - m->data); if (len < 13 || strncasecmp(hdr, "content-type:", 13) != 0) goto error; len -= 13; hdr += 13; /* Skip spaces. */ while (len > 0 && isspace((u_char) *hdr)) { len--; hdr++; } if (len == 0) goto error; /* Find end of line. */ ptr = memchr(hdr, '\n', len); if (ptr == NULL) llen = len; else llen = ptr - hdr; /* Find type. */ ptr = memchr(hdr, ';', llen); if (ptr == NULL) ptr = hdr + llen; type = xmalloc(ptr - hdr + 1); memcpy(type, hdr, ptr - hdr); type[ptr - hdr] = '\0'; len -= ptr - hdr; hdr = ptr; /* If this is now the end of the line, return the type. */ if (len == 0 || *ptr == '\n') return (type); /* Skip the semicolon. */ len--; hdr++; /* * Now follows a set of attributes of the form name=value, separated * by semicolons, possibly crossing multiple lines and possibly with * the value enclosed in quotes. */ namelen = strlen(name); for (;;) { /* Skip spaces and newlines. */ while (len > 0 && (isspace((u_char) *hdr) || *hdr == '\n')) { hdr++; len--; } if (len == 0) goto error; /* Find end of line. */ ptr = memchr(hdr, '\n', len); if (ptr == NULL) llen = len; else llen = ptr - hdr; /* Find the end of the attribute name. */ ptr = memchr(hdr, '=', llen); if (ptr == NULL) break; if (ptr - hdr == namelen && strncmp(hdr, name, namelen) == 0) { llen -= (ptr - hdr + 1); hdr = ptr + 1; ptr = memchr(hdr, ';', llen); if (ptr != NULL) llen = ptr - hdr; if (*hdr == '"') { if (llen < 2 || hdr[llen - 1] != '"') goto error; hdr++; llen -= 2; } *value = xmalloc(llen + 1); memcpy(*value, hdr, llen); (*value)[llen] = '\0'; break; } /* Skip to next semicolon. */ ptr = memchr(hdr, ';', llen); if (ptr == NULL) break; hdr = ptr + 1; len -= (ptr - hdr) + 1; } return (type); error: if (type != NULL) xfree(type); if (*value != NULL) { xfree(*value); *value = NULL; } return (NULL); } struct attach * attach_build(struct mail *m) { struct attach *atr = NULL, *at; char *hdr, *ptr, *b = NULL, *type; size_t len, bl; int last; u_int n; hdr = find_header(m, "content-type", &len, 0); if (hdr == NULL) return (NULL); type = attach_type(m, hdr, "boundary", &b); if (type == NULL || b == NULL) { if (type != NULL) xfree(type); goto error; } if (strncasecmp(type, "multipart/", 10) != 0) { xfree(type); goto error; } bl = strlen(b); atr = xmalloc(sizeof *atr); memset(atr, 0, sizeof *atr); TAILQ_INIT(&atr->children); atr->type = type; /* Find the first boundary. */ line_init(m, &ptr, &len); while (ptr != NULL) { if (ptr[0] == '-' && ptr[1] == '-') { if (len - 3 == bl && strncmp(ptr + 2, b, bl) == 0) break; } line_next(m, &ptr, &len); } if (ptr == NULL) goto error; /* Now iterate over the rest. */ last = 0; n = 0; while (ptr != NULL && !last) { if (ptr[0] == '-' && ptr[1] == '-') { if (len - 5 == bl && strncmp(ptr + 2, b, bl) == 0) break; } at = attach_get(m, &ptr, &len, b, &last); if (at == NULL) goto error; at->idx = n++; at->parent = atr; TAILQ_INSERT_TAIL(&atr->children, at, entry); } if (ptr == NULL) goto error; xfree(b); return (atr); error: if (atr != NULL) attach_free(atr); if (b != NULL) xfree(b); return (NULL); } struct attach * attach_get(struct mail *m, char **ptr, size_t *len, const char *b, int *last) { struct attach *atr, *at; char *name = NULL, *b2 = NULL; size_t bl, bl2; int last2; u_int n; bl = strlen(b); atr = xmalloc(sizeof *atr); memset(atr, 0, sizeof *atr); TAILQ_INIT(&atr->children); atr->data = *ptr - m->data; while (*ptr != NULL) { if (*len >= 13 && strncasecmp(*ptr, "content-type:", 13) == 0) break; line_next(m, ptr, len); } if (*ptr == NULL) goto error; atr->type = attach_type(m, *ptr, "name", &name); if (atr->type == NULL) { if (name != NULL) xfree(name); goto error; } atr->name = name; if (strncasecmp(atr->type, "multipart/", 10) != 0) { /* Skip the remaining headers. */ while (*ptr != NULL && *len > 1) line_next(m, ptr, len); if (*ptr == NULL) goto error; atr->body = *ptr - m->data; for (;;) { line_next(m, ptr, len); if (*ptr == NULL) break; if (*len < 3 || (*ptr)[0] != '-' || (*ptr)[1] != '-') continue; if (*len - 5 == bl && strncmp(*ptr + 2, b, bl) == 0 && strncmp(*ptr + bl + 2, "--", 2) == 0) { *last = 1; break; } if (*len - 3 == bl && strncmp(*ptr + 2, b, bl) == 0) break; } if (*ptr == NULL) goto error; atr->size = *ptr - m->data - atr->data; } else { /* XXX avoid doing this twice. */ xfree(atr->type); atr->type = attach_type(m, *ptr, "boundary", &b2); if (b2 == NULL) goto error; bl2 = strlen(b2); /* Find the first boundary. */ while (*ptr != NULL) { if ((*ptr)[0] == '-' && (*ptr)[1] == '-') { if (*len - 3 == bl2 && strncmp(*ptr + 2, b2, bl2) == 0) break; } line_next(m, ptr, len); } if (ptr == NULL) goto error; /* Now iterate over the rest. */ last2 = 0; n = 0; while (*ptr != NULL && !last2) { at = attach_get(m, ptr, len, b2, &last2); if (at == NULL) goto error; at->idx = n++; at->parent = atr; TAILQ_INSERT_TAIL(&atr->children, at, entry); } /* And skip on to the end of the multipart. */ while (*ptr != NULL) { if ((*ptr)[0] == '-' && (*ptr)[1] == '-') { if (*len - 5 == bl2 && strncmp(*ptr + 2, b2, bl2) == 0) break; } line_next(m, ptr, len); } if (*ptr == NULL) goto error; line_next(m, ptr, len); xfree(b2); } return (atr); error: if (b2 != NULL) xfree(b2); attach_free(atr); return (NULL); }