/*-
* 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 <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <mime_priv.h>
#include <ct.h>
#include <util.h>
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;
}
syntax highlighted by Code2HTML, v. 0.9.1