/*- * Copyright 2003 John-Mark Gurney. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: message.c,v 1.4 2003/09/15 23:37:19 jmg Exp $ * */ #include #include #include #include #include #include static struct mime_message * getmminit() { struct mime_message *ret; ret = malloc(sizeof *ret); ret->mm_headers = NULL; ret->mm_body = NULL; ret->mm_bodylen = 0; ret->mm_attachments = NULL; ret->mm_nattachments = 0; ret->mm_prolog = NULL; ret->mm_prologlen = 0; ret->mm_epilog = NULL; ret->mm_epiloglen = 0; return ret; } struct mime_message * mime_readmessage(const char *msg, size_t len, const char *crlfpair) { const char *bd; char *bdcm; const char *pos; char *bdlc; const char *ct; struct attrib *a; struct mime_message *ret; ret = getmminit(); if ((ret->mm_headers = mime_parseheader(msg, len, &pos, crlfpair)) == NULL) goto mrm; ct = mime_getvalue(ret->mm_headers, "content-type"); if (ct == NULL || ct_cmptype(ct, "multipart") != 0) { /* not a multipart of no content-type */ ret->mm_body = malloc(sizeof *ret->mm_body * (len - (pos - msg))); memcpy(ret->mm_body, pos, sizeof *ret->mm_body * (len - (pos - msg))); ret->mm_bodylen = len - (pos - msg); } else { /* multipart message */ a = mime_getattrib(ct); bd = attrib_get(a, "boundary", NULL); /* * XXX - need to be fixed to support "\r\n" */ bdcm = bd_makenorm(bd, crlfpair); bdlc = memmemory(pos, len - (pos - msg), bdcm, strlen(bdcm)); free(bdcm); if (bdlc != pos) { ret->mm_prolog = malloc(bdlc - pos); memcpy(ret->mm_prolog, pos, bdlc - pos); ret->mm_prologlen = pos - bdlc; } ret->mm_attachments = mime_parsemultipart(bdlc + 1, len - (bdlc + 1 - msg), bd, &ret->mm_nattachments, &pos, crlfpair); if (pos - msg < len) { ret->mm_epilog = malloc(len - (pos - msg)); memcpy(ret->mm_epilog, msg, len - (pos - msg)); ret->mm_epiloglen = len - (pos - msg); } } return ret; mrm: free(ret); return NULL; } struct mime_message ** mime_parsemultipart(const char *msg, size_t len, const char *bd, int *cnt, const char **endpos, const char *crlfpair) { char *next; char *bdtail; char *bdnorm; struct mime_message **ret; int bdnormlen; int bdtaillen; ret = NULL; *cnt = 0; bdnorm = bd_makenorm(bd, crlfpair); bdnormlen = strlen(bdnorm); bdtail = bd_maketail(bd, crlfpair); bdtaillen = strlen(bdtail); if (memcmp(msg, strstr(bdnorm, "--"), strlen(strstr(bdnorm, "--"))) != 0) return NULL; msg += strlen(strstr(bdnorm, "--")); len -= strlen(strstr(bdnorm, "--")); for (;;) { ret = realloc(ret, sizeof *ret * *cnt + 1); if ((next = memmemory(msg, len, bdnorm, bdnormlen)) == NULL) next = memmemory(msg, len, bdtail, bdtaillen); ret[(*cnt)++] = mime_readmessage(msg, next - msg, crlfpair); len -= next - msg; msg += next - msg; if (memcmp(msg, bdtail, strlen(bdtail)) == 0) break; msg += bdnormlen; len -= bdnormlen; } msg += bdnormlen; len -= bdnormlen; if (endpos != NULL) *endpos = msg; return ret; } struct mime_header * mime_getmsgheaders(struct mime_message *mm) { return mm->mm_headers; } static int hexdigit(char a) { if (a >= '0' && a <= '9') return a - '0'; if (a >= 'a' && a <= 'f') return a - 'a' + 10; if (a >= 'A' && a <= 'F') return a - 'A' + 10; abort(); } static int quoted_decode(char *in, int len, char *out, int crlfsize) { int inpos; int cnt; cnt = 0; for (inpos = 0; inpos < len; inpos++) { if (in[inpos] == '=') { /* special handling */ inpos++; if ((in[inpos] >= '0' && in[inpos] <= '9') || (in[inpos] >= 'A' && in[inpos] <= 'F') || (in[inpos] >= 'a' && in[inpos] <= 'f')) { /* encoding a special char */ *out++ = hexdigit(in[inpos]) << 4 | hexdigit(in[inpos + 1]); cnt++; } else inpos += crlfsize; /* crlf size */ } else { *out++ = in[inpos++]; cnt++; } } return cnt; } static int base64_decode(char *in, int len, char *out, int crlfsize) { int cnt; int inpos; int i; int bits; int eqcnt; cnt = 0; inpos = 0; eqcnt = 0; while (inpos < len && eqcnt == 0) { bits = 0; for (i = 0; inpos < len && i < 4; inpos++) { switch (in[inpos]) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': bits = (bits << 6) | (in[inpos] - 'A'); i++; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': bits = (bits << 6) | (in[inpos] - 'a' + 26); i++; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': bits = (bits << 6) | (in[inpos] - '0' + 52); i++; break; case '+': bits = (bits << 6) | 62; i++; break; case '/': bits = (bits << 6) | 63; i++; break; case '=': bits <<= 6; i++; eqcnt++; break; default: break; } } if (i == 0 && inpos >= len) continue; switch (eqcnt) { case 0: *out++ = (bits >> 16) & 0xff; *out++ = (bits >> 8) & 0xff; *out++ = bits & 0xff; cnt += 3; break; case 1: *out++ = (bits >> 16) & 0xff; *out++ = (bits >> 8) & 0xff; cnt += 2; break; case 2: *out++ = (bits >> 16) & 0xff; cnt += 1; break; } } return cnt; } static char *no_encode[] = { "7bit", "8bit", "binary" }; static struct { char *name; float multiple; int (*decode)(char *, int, char *, int); } encode[] = { { "quoted-printable", 1, quoted_decode }, { "base64", (float)3 / 4, base64_decode } }; int mime_estimaterawsize(struct mime_message *mm) { const char *cte; char *t; int len; int i; if (mm->mm_bodylen <= 0) return 0; if ((cte = mime_getvalue(mm->mm_headers, "content-transfer-encoding")) == NULL) return mm->mm_bodylen; while (isspace(*cte)) cte++; if ((t = strchr(cte, ';')) == NULL) len = strlen(cte); else len = t - cte; for (i = 0; i < sizeof no_encode / sizeof *no_encode; i++) if (len == strlen(no_encode[i]) && strncasecmp(cte, no_encode[i], len) == 0) return mm->mm_bodylen; for (i = 0; i < sizeof encode / sizeof *encode; i++) if (len == strlen(encode[i].name) && strncasecmp(cte, encode[i].name, len) == 0) return mm->mm_bodylen * encode[i].multiple; return -1; } int mime_getrawbody(struct mime_message *mm, char *out, int crlfsize) { const char *cte; char *t; int len; int i; if (mm->mm_bodylen <= 0) return 0; if ((cte = mime_getvalue(mm->mm_headers, "content-transfer-encoding")) == NULL) { memcpy(out, mm->mm_body, mm->mm_bodylen); return mm->mm_bodylen; } while (isspace(*cte)) cte++; if ((t = strchr(cte, ';')) == NULL) len = strlen(cte); else len = t - cte; for (i = 0; i < sizeof encode / sizeof *encode; i++) if (len == strlen(encode[i].name) && strncasecmp(cte, encode[i].name, len) == 0) return encode[i].decode(mm->mm_body, mm->mm_bodylen, out, crlfsize); return -1; } const char * mime_getmsgbody(struct mime_message *mm) { return mm->mm_body; } int mime_getmsgbodylen(struct mime_message *mm) { return mm->mm_bodylen; } struct mime_message * mime_getattachment(struct mime_message *mm, int pos) { if (pos >= mm->mm_nattachments || pos < 0) return NULL; return mm->mm_attachments[pos]; } int mime_nattachment(struct mime_message *mm) { return mm->mm_nattachments; } const char * mime_getmsgprolog(struct mime_message *mm) { return mm->mm_prolog; } int mime_getmsgprologlen(struct mime_message *mm) { return mm->mm_prologlen; } const char * mime_getmsgepilog(struct mime_message *mm) { return mm->mm_epilog; } int mime_getmsgepiloglen(struct mime_message *mm) { return mm->mm_epiloglen; }