static char rcsid[] = "@(#)$Id: headers.c,v 1.37 2006/07/24 18:45:00 hurtta Exp $"; /****************************************************************************** * The Elm (ME+) Mail System - $Revision: 1.37 $ $State: Exp $ * * Author: Kari Hurtta (was hurtta+elm@ozone.FMI.FI) *****************************************************************************/ #include "headers.h" #include "hdr_imp.h" #include "s_me.h" DEBUG_VAR(Debug,__FILE__,"header"); static unsigned char * us_str P_((char *str)); static unsigned char * us_str(str) char *str; { return (unsigned char *)str; } /* MIME header decode routines ......................................... */ static struct string * hdr_decode_from_text P_((header_list_ptr hdr, int demime, charset_t defcharset)); static struct string * hdr_decode_from_text(hdr,demime,defcharset) header_list_ptr hdr; int demime; charset_t defcharset; { struct string * result = NULL; result = hdr_to_string(HDR_TEXT,hdr->body,defcharset,demime); return result; } static struct string * hdr_decode_from_raw P_((header_list_ptr hdr, int demime, charset_t defcharset)); static struct string * hdr_decode_from_raw(hdr,demime,defcharset) header_list_ptr hdr; int demime; charset_t defcharset; { struct string * result = NULL; result = new_string2(defcharset,us_str(hdr->body)); return result; } static struct string * decode_comment P_((char * token,int demime, charset_t defcharset)); static struct string * decode_comment(token,demime,defcharset) char * token; int demime; charset_t defcharset; { struct string *result = new_string(defcharset); struct string *newresult = NULL; struct string *R = NULL; int k,j=0; char buffer[1000]; for (k = 1; (token[k] != ')' || token[k+1]) && j < sizeof buffer -1; k++) buffer[j++] = token[k]; buffer[j] = '\0'; R = hdr_to_string(HDR_COMMENT,buffer,defcharset,demime); /* HACK */ add_streambyte_to_string(result,'('); add_streambyte_to_string(R,')'); /* Now compine strings */ newresult = cat_strings(result,R,1); free_string(&result); result = newresult; free_string(&R); return result; } static struct string * hdr_decode_from_comment P_((header_list_ptr hdr, int demime, charset_t defcharset)); static struct string * hdr_decode_from_comment(hdr,demime,defcharset) header_list_ptr hdr; int demime; charset_t defcharset; { struct string * result = new_string(defcharset); int i; char ** tokens = rfc822_tokenize(hdr->body); for (i = 0; tokens[i]; i++) { struct string * R = NULL, * newresult; if ('(' == tokens[i][0]) { R = decode_comment(tokens[i],demime,defcharset); } else { R = new_string2(defcharset,us_str(tokens[i])); } /* Now compine strings */ newresult = cat_strings(result,R,1); free_string(&result); result = newresult; free_string(&R); } free_rfc822tokenized(tokens); return result; } static struct string * hdr_decode_from_phrase P_((header_list_ptr hdr, int demime, charset_t defcharset)); static struct string * hdr_decode_from_phrase(hdr,demime,defcharset) header_list_ptr hdr; int demime; charset_t defcharset; { struct string * result = new_string(defcharset); int i; char ** tokens = rfc822_tokenize(hdr->body); for (i = 0; tokens[i]; i++) { struct string * R = NULL, * newresult; if ('\n' == tokens[i][0] && !tokens[i+1]) break; if (whitespace(tokens[i][0]) || '\n' == tokens[i][0]) { R = new_string(defcharset); add_ascii_to_string(R,us_str(tokens[i])); } else if ('(' == tokens[i][0]) { R = decode_comment(tokens[i],demime,defcharset); } else if ('<' == tokens[i][0]) { int j; /* Add mising space wetween phrase and < > */ if (i > 0) add_ascii_to_string(result,us_str(" ")); R = new_string(defcharset); add_ascii_to_string(R,us_str(tokens[i])); for (j = i+1; tokens[j] && tokens[j][0] != '>'; j++) { if ('(' == tokens[i][0]) { struct string * R1 = decode_comment(tokens[j], demime,defcharset); /* Now combine strings */ newresult = cat_strings(result,R1,1); free_string(&R); R = newresult; } else add_ascii_to_string(R,us_str(tokens[j])); i = j; } if (tokens[j]) { add_ascii_to_string(R,us_str(tokens[j])); i = j; } } else { char * buffer = NULL; int j; int ph = give_dt_enumerate_as_int(&phrase_display_mode); for (j = i; tokens[j]; j++) { if ('(' == tokens[j][0]) break; else if ('<' == tokens[j][0]) break; else if ('\n' == tokens[j][0] && !tokens[j+1]) break; i = j; buffer = strmcat(buffer,tokens[j]); } R = hdr_to_string(HDR_PHRASE,buffer,defcharset,demime); switch (ph) { case 0: break; default: /* We do not (backslash) quote displayed (only) text */ add_ascii_to_string(result,us_str("\"")); add_ascii_to_string(R,us_str("\"")); break; } free(buffer); } /* Now combine strings */ newresult = cat_strings(result,R,1); free_string(&result); result = newresult; free_string(&R); } free_rfc822tokenized(tokens); return result; } static struct string * hdr_decode_from_addr P_((header_list_ptr hdr, int demime, charset_t defcharset)); static struct string * hdr_decode_from_addr(hdr,demime,defcharset) header_list_ptr hdr; int demime; charset_t defcharset; { struct string * result = new_string(defcharset); char ** tokens = rfc822_tokenize(hdr->body); int tokcount = 0; int ptr = 0; int bracket = 0; int lastptr = 0; while(tokens[tokcount]) tokcount++; /* Remove LF from end of header */ if (tokcount > 0 && '\n' == tokens[tokcount-1][0]) tokcount--; /* Walk through addressess ... */ for (ptr = 0; ptr < tokcount; ptr = lastptr) { char **scanned = NULL; struct string *comments = NULL; struct string *newresult = NULL; char tok = '\0'; look_special_tokens(tokens,":<>,;",ptr,&lastptr,demime, defcharset,&comments,&scanned); if (lastptr < tokcount) { tok = tokens[lastptr][0]; DPRINT(Debug,25,(&Debug, "hdr_decode_from_addr: [%d] token=%c (%s)\n", lastptr,tok,tokens[lastptr])); } else { tok = '\0'; DPRINT(Debug,25,(&Debug, "hdr_decode_from_addr: [%d] token=EOS\n", lastptr)); } if (ptr < lastptr) { struct string *R = NULL; if (bracket <= 0 && ( ':' == tok || '<' == tok)) { int ph = give_dt_enumerate_as_int(&phrase_display_mode); DPRINT(Debug,25,(&Debug, "hdr_decode_from_addr: Adding phrase\n")); R = scanned_to_phrase(scanned, demime, defcharset); switch (ph) { case 0: break; default: /* We do not (backslash) quote displayed (only) text */ add_ascii_to_string(result,us_str("\"")); add_ascii_to_string(R,us_str("\"")); break; } } else { int z; DPRINT(Debug,25,(&Debug, "hdr_decode_from_addr: Adding address\n")); R = new_string(defcharset); for (z = 0; scanned[z]; z++) { if ('\n' == scanned[z][0] && !scanned[z+1]) { /* HACK: Ignore */ DPRINT(Debug,25,(&Debug, "hdr_decode_from_addr: Ignoring ending LF\n")); } else add_ascii_to_string(R,us_str(scanned[z])); } } /* Now compine strings */ newresult = cat_strings(result,R,1); free_string(&result); result = newresult; free_string(&R); } else { DPRINT(Debug,25,(&Debug, "hdr_decode_from_addr: (empty) \n")); } if (comments) { DPRINT(Debug,25,(&Debug, "hdr_decode_from_addr: Adding comments\n")); add_ascii_to_string(result,us_str(" (")); /* Now compine strings */ newresult = cat_strings(result,comments,1); free_string(&result); result = newresult; add_ascii_to_string(result,us_str(")")); } if (lastptr < tokcount) { switch (tok) { case '<': bracket++; if (ptr < lastptr) add_ascii_to_string(result,us_str(" ")); DPRINT(Debug,25,(&Debug, "hdr_decode_from_addr: bracket=%d\n", bracket)); break; case '>': bracket--; DPRINT(Debug,25,(&Debug, "hdr_decode_from_addr: bracket=%d\n", bracket)); break; } DPRINT(Debug,25,(&Debug, "hdr_decode_from_addr: Adding token %s\n", tokens[lastptr])); add_ascii_to_string(result,us_str(tokens[lastptr])); lastptr++; switch (tok) { case ':': case ',': if (bracket <= 0) add_ascii_to_string(result,us_str(" ")); break; } } if (comments) free_string(&comments); if (scanned) free_rfc822tokenized(scanned); } free_rfc822tokenized(tokens); return result; } #ifdef ANSI_C static hdr_add_to_mailing_hdr no_add_to_mail_hdr; #endif static int no_add_to_mail_hdr(headers,X,value,demime,defcharset,replace) struct mailing_headers * headers; header_ptr X; CONST char *value; int demime; charset_t defcharset; int replace; { return 0; } /* ------------------------------------------------------------------------ */ static struct header_info header_types[] = { /* From STD 11 (RFC 822): */ { "Subject", hdr_decode_from_text, add_subject_hdr }, { "Comments", hdr_decode_from_text, no_add_to_mail_hdr }, { "Return-path", hdr_decode_from_comment, add_env_from_hdr }, { "Received", hdr_decode_from_comment, no_add_to_mail_hdr }, { "Reply-To", hdr_decode_from_addr, add_replyto_hdr }, { "From", hdr_decode_from_addr, add_from_hdr }, { "Sender", hdr_decode_from_addr, no_add_to_mail_hdr }, { "Resent-Reply-To", hdr_decode_from_addr, no_add_to_mail_hdr }, { "Resent-From", hdr_decode_from_addr, no_add_to_mail_hdr }, { "Resent-Sender", hdr_decode_from_addr, no_add_to_mail_hdr }, { "Date", hdr_decode_from_comment, no_add_to_mail_hdr }, { "Resent-Date", hdr_decode_from_comment, no_add_to_mail_hdr }, { "To", hdr_decode_from_addr, add_to_hdr }, { "Resent-To", hdr_decode_from_addr, no_add_to_mail_hdr }, { "cc", hdr_decode_from_addr, add_cc_hdr }, { "Resent-cc", hdr_decode_from_addr, no_add_to_mail_hdr }, { "bcc", hdr_decode_from_addr, add_bcc_hdr }, { "Resent-bcc", hdr_decode_from_addr, no_add_to_mail_hdr }, { "Message-ID", hdr_decode_from_comment, no_add_to_mail_hdr }, { "Resent-Message-ID", hdr_decode_from_comment, no_add_to_mail_hdr }, { "In-Reply-To", hdr_decode_from_phrase, no_add_to_mail_hdr }, { "References", hdr_decode_from_phrase, no_add_to_mail_hdr }, { "Keywords", hdr_decode_from_phrase, no_add_to_mail_hdr }, { "Encrypted", hdr_decode_from_comment, no_add_to_mail_hdr }, /* From MIME (RFC 1521) */ { "MIME-Version", hdr_decode_from_comment, no_add_to_mail_hdr }, { "Content-Type", hdr_decode_from_comment, no_add_to_mail_hdr }, { "Content-Transfer-Encoding", hdr_decode_from_comment, no_add_to_mail_hdr }, { "Content-ID", hdr_decode_from_comment, no_add_to_mail_hdr }, { "Content-Description", hdr_decode_from_text, no_add_to_mail_hdr }, /* From RFC 1806 */ { "Content-Disposition", hdr_decode_from_comment, no_add_to_mail_hdr }, /* From RFC 1864 */ { "Content-MD5", hdr_decode_from_raw, no_add_to_mail_hdr }, /* From RFC 2919 */ { "List-Id", hdr_decode_from_phrase, no_add_to_mail_hdr }, /* From RFC 2369 */ { "List-Help", hdr_decode_from_comment, no_add_to_mail_hdr }, { "List-Subscribe", hdr_decode_from_comment, no_add_to_mail_hdr }, { "List-Unsubscribe", hdr_decode_from_comment, no_add_to_mail_hdr }, { "List-Post", hdr_decode_from_comment, no_add_to_mail_hdr }, { "List-Owner", hdr_decode_from_comment, no_add_to_mail_hdr }, { "List-Archive", hdr_decode_from_comment, no_add_to_mail_hdr }, /* mailbox format */ { "Content-Length", hdr_decode_from_raw, no_add_to_mail_hdr }, { "Status", hdr_decode_from_raw, no_add_to_mail_hdr }, /* Sendmail */ { "Full-Name", hdr_decode_from_text, no_add_to_mail_hdr }, { "Return-Receipt-To", hdr_decode_from_addr, no_add_to_mail_hdr }, { "Auto-Submitted", hdr_decode_from_comment, no_add_to_mail_hdr }, { "Precedence", hdr_decode_from_raw, no_add_to_mail_hdr }, /* IDA Sendmail */ { "X-Charset", hdr_decode_from_raw, no_add_to_mail_hdr }, { "X-Char-Esc", hdr_decode_from_raw, no_add_to_mail_hdr }, /* Unknown source */ { "Action", hdr_decode_from_raw, no_add_to_mail_hdr }, { "Priority", hdr_decode_from_comment, no_add_to_mail_hdr }, { "Expires", hdr_decode_from_comment, no_add_to_mail_hdr }, { "Importance", hdr_decode_from_comment, no_add_to_mail_hdr }, { "Sensitivity", hdr_decode_from_comment, no_add_to_mail_hdr }, /* Our non-standard headers */ { "X-ELM-OSV", hdr_decode_from_comment, no_add_to_mail_hdr }, { "X-Mailer", hdr_decode_from_text, no_add_to_mail_hdr }, { "Content-Name", hdr_decode_from_text, no_add_to_mail_hdr }, /* Tailer */ { NULL, hdr_decode_from_raw, no_add_to_mail_hdr } }; static struct header_info * malloc_header_info P_((const char *name)); static struct header_info * malloc_header_info(name) CONST char *name; { struct header_info *ret = safe_malloc(sizeof (*ret)); bzero((void *)ret, sizeof(*ret)); ret->header = safe_strdup(name); if (strincmp(name,"X-",2) == 0) { /* Default for user defined headers */ ret->hdr_decode_from_it = hdr_decode_from_text; ret->hdr_add_to_it = add_user_hdr; } else { /* We don't know it, right? */ ret->hdr_decode_from_it = hdr_decode_from_raw; ret->hdr_add_to_it = no_add_to_mail_hdr; } return ret; } #define HDR_SCAN_magic 0xFC06 static struct header_scan_list { unsigned short magic; /* HDR_SCAN_magic */ struct header_info * hdr; struct header_scan_list * smaller; struct header_scan_list * bigger; } * find_header_1 P_((struct header_scan_list **root, const char *name, int create_flag, struct header_info *init)); static struct header_scan_list * find_header_1(root,name,create_flag,init) struct header_scan_list **root; const char *name; int create_flag; struct header_info *init; { struct header_scan_list *ptr = *root; int r; if (!ptr) { if (!create_flag) return NULL; ptr = safe_malloc(sizeof(*ptr)); bzero((void *)ptr, sizeof(*ptr)); ptr->magic = HDR_SCAN_magic; if (init) ptr->hdr = init; else ptr->hdr = malloc_header_info(name); ptr->smaller = NULL; ptr->bigger = NULL; *root = ptr; return ptr; } if (ptr->magic != HDR_SCAN_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"find_header_1", "Bad magic number",0); r = istrcmp(name,ptr->hdr->header); if (0 == r) { return ptr; } else if (0 < r) { return find_header_1(& (ptr->bigger), name, create_flag, init); } else { return find_header_1(& (ptr->smaller), name, create_flag, init); } } header_ptr find_header(name, create_flag) CONST char *name; int create_flag; { static struct header_scan_list * root = NULL; struct header_scan_list * tmp; if (! root) { int i; for (i = 0; header_types[i].header; i++) { if (! find_header_1(&root, header_types[i].header, 1, & (header_types[i]))) panic("MBX VIEW PANIC",__FILE__,__LINE__,"find_header", "Init failure",0); } } tmp = find_header_1(&root, name, create_flag, NULL); if (tmp) { return tmp->hdr; } return NULL; } header_list_ptr locate_header(h,n) header_list_ptr h; header_ptr n; /* NOTE: n may be also NULL */ { header_list_ptr walk; for (walk = h; walk != NULL; walk = walk -> next_other_header) { if (walk -> magic != HEADER_magic) panic("HEADERS PANIC",__FILE__,__LINE__,"locate_header", "Bad magic number",0); if (n == walk -> header_name) return walk; } return NULL; } void update_header_list(list,last1, name, value) header_list_ptr *list; header_list_ptr *last1; CONST char * name; CONST char * value; { header_list_ptr result = *list, last = *last1; header_list_ptr item, last_this = NULL, walk; if (result && !last) { last = result; DPRINT(Debug,9,(&Debug, "update_header_list: Moving 'last' to end of list ... \n")); while (last -> next_header) last = last -> next_header; } item = (struct header_list *) safe_malloc(sizeof (struct header_list)); item -> header_name = find_header(name,1); item -> next_header = NULL; item -> next_this_header = NULL; item -> next_other_header = NULL; item -> body = safe_strdup(value); item -> magic = HEADER_magic; for (walk = result; walk != NULL; last_this = walk, walk = walk -> next_other_header) { if (walk -> magic != HEADER_magic) panic("HEADERS PANIC",__FILE__,__LINE__,"update_header_list", "Bad magic number",0); if (item -> header_name == walk -> header_name) break; } if (!walk) { DPRINT(Debug,9,(&Debug, "update_header_list: name='%s': (%p) Not in chain!\n", item -> header_name->header, item -> header_name)); } if (walk) { while (walk->next_this_header != NULL) { if (walk -> magic != HEADER_magic) panic("HEADERS PANIC",__FILE__,__LINE__,"update_header_list", "Bad magic number",0); walk = walk->next_this_header; } walk->next_this_header = item; DPRINT(Debug,9,(&Debug, "update_header_list: name='%s' -- append next_this_header (%s)\n", item->header_name->header, walk->header_name->header)); } else if (last_this) { last_this -> next_other_header = item; DPRINT(Debug,9,(&Debug, "update_header_list: name='%s' -- append next_other_header (%s)\n", item->header_name->header, last_this->header_name->header)); } if (last) { last -> next_header = item; DPRINT(Debug,9,(&Debug, "update_header_list: name='%s' -- append next_header (%s)\n", item->header_name->header, last->header_name->header)); } else { result = item; DPRINT(Debug,9,(&Debug, "update_header_list: name='%s' -- head of next_header\n", item->header_name->header)); } last = item; *list = result; *last1 = last; } void delete_headers(hdr) header_list_ptr *hdr; { header_list_ptr next = *hdr; while(next) { if (next -> magic != HEADER_magic) panic("HEADERS PANIC",__FILE__,__LINE__,"delete_headers", "Bad magic number",0); (*hdr) = next; next = next -> next_header; if((*hdr) -> body) { free((*hdr) -> body); (*hdr) -> body = NULL; } (*hdr) -> next_header = NULL; (*hdr) -> next_this_header = NULL; (*hdr) -> next_other_header = NULL; (*hdr) -> magic = 0; free((void *)(*hdr)); } (*hdr) = next; } /* Returns ponter to static storage */ CONST char * give_header_name(X) header_ptr X; { return X->header; } int is_mailing_header(X) header_ptr X; { return X->hdr_add_to_it != no_add_to_mail_hdr; } struct string * give_decoded_header(hdr,demime,defcharset) header_list_ptr hdr; int demime; charset_t defcharset; { struct string * result = NULL; if (hdr -> magic != HEADER_magic) panic("HEADERS PANIC",__FILE__,__LINE__,"give_decoded_header", "Bad magic number",0); result = hdr->header_name->hdr_decode_from_it(hdr,demime,defcharset); return result; } int add_to_mailing_header(headers,X,value,demime,defcharset,replace) struct mailing_headers * headers; header_ptr X; CONST char *value; int demime; charset_t defcharset; int replace; { int ret; if (headers->magic != MAIL_HDR_magic) panic("HEADERS PANIC",__FILE__,__LINE__,"add_to_mailing_header", "Bad magic number",0); ret = X->hdr_add_to_it(headers,X,value,demime,defcharset,replace); return ret; } int read_header_line (fp, buf, size,flag) FILE *fp; char *buf; int size; int flag; { in_state_t state; int result; in_state_clear(&state,STATE_in_file); set_in_state_file(fp,&state); result = state_read_hdr_line(&state,buf,size,flag); in_state_destroy(&state); return result; } int state_read_hdr_line (s, buf, size,flag) in_state_t *s; char *buf; int size; int flag; { /* Read and unfold header line -- stores maximum size-1 bytes to buffer * (plus \0). Also handle case when headers are eneded either CR LF or LF. * Returns number of bytes stored. Always _read_ end of header * (even when buffer fills). Returns 0 when reads empty line (only CR LF). * That indicates end of headers. * * If flag & 1 (RHL_MARK_FOLDING) then folding is marked with '\n' * (instead of ' ' and buffer ended with '\n' before '\0' * If flag & 2 (RHL_CHECK_HEADER) then check that this was header line... */ int len = 0,c; int col_seen = 0; long pos = -1; DPRINT(Debug,12,(&Debug, "state_read_hdr_line: size=%d, flag=%d\n",size,flag)); size--; /* Place for \0 */ if ( (!in_state_seekable(s) || (pos = in_state_ftell(s)) < 0) && (flag & RHL_CHECK_HEADER)) { DPRINT(Debug,5,(&Debug, "state_read_hdr_line=0; not seekable or ftell failed!")); buf[0] = '\0'; return 0; } #define PUTC(c) { if (len < size) buf[len++] = (c); } while (EOF != (c = state_getc(s))) { if ('\r' == c) { /* Is this CR LF sequence ? */ if (EOF == (c = state_getc(s))) break; if (c != '\n') PUTC('\r'); } if (c == '\n') { /* Read CR LF or LF, check folding */ if (!col_seen && len > 0 && (flag & RHL_CHECK_HEADER)) { bad_header_line: DPRINT(Debug,12,(&Debug, "state_read_hdr_line: Not ':' seen. Not a header line!\n")); if (0 != in_state_fseek(s,pos)) { DPRINT(Debug,5,(&Debug, "read_header_line: seek failed!\n")); } len = 0; } if (len == 0) break; /* End of headers ! */ if (EOF == (c = state_getc(s))) break; if (c != ' ' && c != '\t') { /* Not a continuation line */ state_ungetc(c,s); break; } /* CRLF LWSP sequence should be replaced with ' ' */ c = ' '; if (flag & RHL_MARK_FOLDING) c = '\n'; } /* Space before ':' ? */ if ((' ' == c || '\t' == c) && !col_seen && (flag & RHL_CHECK_HEADER)) { /* Skip to next ':' */ while (' ' == c || '\t' == c) c = state_getc(s); if (':' != c) goto bad_header_line; } if (':' == c) col_seen = 1; PUTC(c); } if (flag & RHL_MARK_FOLDING) { PUTC('\n'); } #undef PUTC buf[len] = 0; DPRINT(Debug,12,(&Debug, "state_read_hdr_line: len=%d, buf=%s\n",len,buf)); return len; } /* * Local Variables: * mode:c * c-basic-offset:4 * buffer-file-coding-system: iso-8859-1 * End: */