/* ** ** Copyright (C) 1993 Swedish University Network (SUNET) ** ** ** This program is developed by UDAC, Uppsala University by commission ** of the Swedish University Network (SUNET). ** ** 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 distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITTNESS 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., 675 Mass Ave, Cambridge, MA 02139, USA. ** ** ** Martin.Wendel@its.uu.se ** Torbjorn.Wictorin@its.uu.se ** ** ITS ** P.O. Box 887 ** S-751 08 Uppsala ** Sweden ** */ #include "emil.h" int do_sibling = FALSE; /* * int * parse_message(inbuf, message) * * Parse message and divide into a hierarchical structure of message parts. * */ int parse_message(struct message *m) { struct data *inbuf; /* Initialize inbuf */ inbuf = (struct data *)m->sd; /* Exit on empty input */ if (inbuf->size == 0 || (inbuf->end <= inbuf->offset)) { #ifdef DEBUG if (edebug) fprintf(stderr, "*** Empty input (failed).\n"); #endif logger(LOG_ERR, "parse_message: Empty input"); return(NOK); } /* Check header if possible */ if (!m->headerdone && (m->sd->format != RFC822 || m->level == 0)) if (check_header(m) != OK) return(NOK); switch(m->sd->format) { case MIME: return(parse_mime_message(m)); break; case MAILTOOL: return(parse_sun_message(m)); break; case RFC822: return(parse_rfc822_message(m)); break; default: break; } return(NOK); } int move_past_boundary(struct message *m, char *boundary) { int linelen; struct data *inbuf; inbuf = (struct data *)m->sd; /* Find start boundary */ while (is_bound(inbuf, boundary) != TRUE) { if ((linelen = getline(inbuf)) == 0) { #ifdef DEBUG if (edebug) fprintf(stderr, "* move_past_boundary failed, EOF.\n"); #endif return(NOK); } inbuf->offset += linelen; if (inbuf->end <= inbuf->offset) { #ifdef DEBUG if (edebug) fprintf(stderr, "move_past_boundary: cannot find boundary\n"); #endif sprintf(ebuf, "move_past_boundary: cannot find boundary"); logger(LOG_DEBUG, ebuf); return(NOK); } inbuf->loffset += 1; } /* Move past boundary */ inbuf->offset += getline(inbuf); inbuf->loffset += 1; #ifdef DEBUG if (edebug) fprintf(stderr, "+ Found boundary, moved to %lu\n", inbuf->offset); #endif return(OK); } int check_header(struct message *m) { m->headerdone = 1; if (load_header(m) == OK) /* Offset is updated with size of header! */ { /* Decode the loaded header */ decode_header(m); /* Set body lines, if available * (body length is not trusted) */ if (m->sd->bodylines != 0) m->sd->lineend = m->sd->loffset + m->sd->bodylines; } else { /* Load header failed */ sprintf(ebuf, "check_header: load header failed: %lu", m->sd->loffset); #ifdef DEBUG if (edebug) fprintf(stderr, "%s\n", ebuf); #endif logger(LOG_DEBUG, ebuf); return(NOK); } return(OK); } int check_encoding(struct message *m) { struct data *inbuf; int status; inbuf = m->sd; /* Check for encoding */ switch (inbuf->encoding) { case 0: inbuf->encoding = E7BIT; status = OK; break; case EBINHEX: if (decode_binhex(m) == NOK) { sprintf(ebuf, "WARNING: parse_message: BinHex decode failed :%lu", inbuf->loffset); #ifdef DEBUG if (edebug) fprintf(stderr, "%s\n", ebuf); #endif logger(LOG_WARNING, ebuf); status = NOK; } else status = OK; break; case EUUENCODE: if (decode_uuencode(m) == NOK) { sprintf(ebuf, "WARNING: parse_message: UUencode decode failed :%lu", inbuf->loffset); #ifdef DEBUG if (edebug) fprintf(stderr, "%s\n", ebuf); #endif logger(LOG_WARNING, ebuf); status = NOK; } else status = OK; break; case EBASE64: if (decode_base64(m) == NOK) { sprintf(ebuf, "WARNING: parse_message: Base64 decode failed :%lu", inbuf->loffset); #ifdef DEBUG if (edebug) fprintf(stderr, "%s\n", ebuf); #endif logger(LOG_WARNING, ebuf); status = NOK; } else status = OK; if (inbuf->bodyend == 0) inbuf->bodyend = inbuf->end; break; case EQP: if (decode_quoted_printable(m) == NOK) { sprintf(ebuf, "WARNING: parse_message: Quoted-Printable decode failed :%lu", inbuf->loffset); #ifdef DEBUG if (edebug) fprintf(stderr, "%s\n", ebuf); #endif logger(LOG_WARNING, ebuf); status = NOK; } else status = OK; if (inbuf->bodyend == 0) inbuf->bodyend = inbuf->end; break; default: status = OK; break; } return(status); } int boundary_check(struct message *m) { struct message *parent; int encoding; int check; encoding = m->sd->encoding; check = m->sd->check; parent = m->parent; #ifdef DEBUG if (edebug) fprintf(stderr, "checking part %d with check %d and encoding %d\nat offset %lu\n", m->sd->count, check, encoding, m->sd->offset); #endif /* Check a multipart, look for start boundary */ if (parent != NULL && parent->sd->startbound != NULL && (strlen(parent->sd->startbound)) != 0) { if (is_bound(m->sd, parent->sd->startbound)) { return(E7BIT); /* Just a precaution */ } } /* Check for uuencode */ if (check & EUUENCODE) { if (encoding & EUUENCODE) return(0); /* Check for binhex and uuencode */ if (strncmp((m->sd->contents + m->sd->offset), "begin ", 6) == 0) { return(EUUENCODE); } } /* Check for BinHex */ if (check & EBINHEX) { if (encoding & EBINHEX) return(0); /* Check for BinHex */ if (strncasecmp((m->sd->contents + m->sd->offset), "(This file ", 11) == 0) { return(EBINHEX); } } if (encoding == 0) return(E7BIT); return(0); } /* * */ int is_bound(struct data *d, char *boundary) { int blen; if (boundary != NULL && (blen = strlen(boundary)) != 0) { /* printf("is_bound: '%20.20s'\n '%20.20'\n", d->contents+d->offset, boundary); */ if (d->offset + blen < d->end && strncmp((d->contents + d->offset), boundary, blen) == 0) { return(TRUE); } } return(FALSE); } /* * */ struct message * copy_mstruct(struct message *m, int force) { struct data *cbuf, *inbuf; struct message *cm; #ifdef DEBUG if (edebug) fprintf(stderr, "+ Copying structure\n"); #endif inbuf = m->sd; if (force < 2) { #ifdef DEBUG if (edebug) fprintf(stderr, "+ Creating new data structure\n"); #endif /* Copy data structure */ cbuf = (struct data *) Yalloc(sizeof(struct data)); cbuf->contents = inbuf->contents; /* data */ cbuf->offset = inbuf->offset; /* and offset */ cbuf->check = inbuf->check; cbuf->size = inbuf->size; cbuf->count = inbuf->count + 1; cbuf->loffset = inbuf->loffset; cbuf->charset = inbuf->charset; cbuf->format = inbuf->format; } else cbuf = m->sd; /* Copy message structure */ if (force < 1 && m->sd->format == RFC822) { #ifdef DEBUG if (edebug) fprintf(stderr, "+ Living with old message structure\n"); #endif m->sd->next = cbuf; m->sd = m->sd->next; m->td = m->sd; #ifdef DEBUG if (edebug) fprintf(stderr, "Reached offset %lu\n", m->sd->offset); #endif return(m); } else { #ifdef DEBUG if (edebug) fprintf(stderr, "+ Copying message structure\n"); #endif cm = (struct message *) Yalloc(sizeof(struct message)); cm->sdl = cbuf; cm->sd = cm->sdl; cm->td = cbuf; return(cm); } } /* * int * getline(inbuf) * * Move pointer to next line of inbuf. Return line length. */ int getline(struct data *inbuf) { unsigned char *tmp; if (inbuf == NULL || inbuf->contents == NULL || inbuf->offset >= inbuf->end) return(0); tmp = index(inbuf->contents + inbuf->offset, '\n'); if (tmp == NULL || (tmp - inbuf->contents) >= inbuf->end) return(inbuf->end - inbuf->offset); else return(tmp - inbuf->contents + 1 - inbuf->offset); } int set_end_by_boundary(struct message *m, char *boundary) { int linelen, offset, loffset; struct data *inbuf; inbuf = (struct data *)m->sd; #ifdef DEBUG if (edebug) fprintf(stderr, "* set_end_by_boundary\n"); #endif offset = inbuf->offset; loffset = inbuf->loffset; while (is_bound(inbuf, boundary) != TRUE) { if ((linelen = getline(inbuf)) == 0) { if (m->parent) inbuf->bodyend = m->parent->sd->bodyend; else inbuf->bodyend = inbuf->offset; inbuf->lineend = inbuf->loffset; inbuf->offset = offset; inbuf->loffset = loffset; sprintf(ebuf, "set_end_by_boundary: cannot find boundary, setting end to %lu\n", inbuf->bodyend); logger(LOG_DEBUG, ebuf); #ifdef DEBUG if (edebug) fprintf(stderr, "set_end_by_boundary: cannot find boundary, setting end to %lu\n", inbuf->bodyend); #endif return(NOK); } inbuf->offset += linelen; inbuf->loffset += 1; } /* Set bodyend to just before the end boundary */ inbuf->bodyend = inbuf->offset; inbuf->lineend = inbuf->loffset; if (m->sd->format == MIME) inbuf->bodyend -= 1; inbuf->offset = offset; inbuf->loffset = loffset; #ifdef DEBUG if (edebug) fprintf(stderr, "+ Found boundary, setting end at %lu.\n", inbuf->bodyend); #endif return(OK); } int set_end_by_lines(struct message *m, int lines) { int linelen, offset, loffset, lineend; struct data *inbuf; inbuf = (struct data *)m->sd; #ifdef DEBUG if (edebug) fprintf(stderr, "* set_end_by_lines\n"); #endif offset = inbuf->offset; loffset = inbuf->loffset; lineend = loffset + lines; while ((linelen = getline(inbuf)) != 0 && lineend > inbuf->loffset) { inbuf->offset += linelen; inbuf->loffset += 1; } /* Set bodyend to just before the end boundary */ inbuf->bodyend = inbuf->offset; inbuf->lineend = inbuf->loffset; inbuf->offset = offset; inbuf->loffset = loffset; return(OK); }