static char rcsid[] = "@(#)$Id: hdrdecode.c,v 1.22 2006/07/01 07:37:48 hurtta Exp $"; /****************************************************************************** * The Elm (ME+) Mail System - $Revision: 1.22 $ $State: Exp $ * * Author: Kari Hurtta (was hurtta+elm@ozone.FMI.FI) * * Partially based on mime_decode.c, which is initially * written by: Michael Elkins , 1995 *****************************************************************************/ #include "headers.h" #include "s_me.h" DEBUG_VAR(Debug,__FILE__,"mime"); int index_hex[128] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 }; int index_64[128] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 }; char * blstrpbrk (string, set) char *string; CONST char *set; { char *p; for (p = string; *p; p++) if ('\\' == *p) { p++; if ('\0' == *p) return NULL; } else if (NULL != strchr(set,*p)) return p; return NULL; } void append_string(res,s) struct string ** res; CONST struct string *s; { if (!*res) *res = dup_string(s); else { struct string *t = cat_strings(*res,s,1); free_string(res); *res = t; } } static unsigned char * us_str P_((char *str)); static unsigned char * us_str(str) char *str; { return (unsigned char *)str; } static char *us2s P_((unsigned char *str)); static char *us2s(str) unsigned char *str; { return (char *)str; } static struct string * hdr_qp_decode P_((charset_t set,char *buffer, char *lang)); static struct string * hdr_qp_decode(set,buffer,lang) charset_t set; char *buffer; char *lang; { struct string *ret = lang ? new_langstring(set,lang) : new_string(set); unsigned char *p; for (p = us_str(buffer); *p; p++) { switch(*p) { int a,b; case '_': add_streambyte_to_string(ret,' '); break; case '=': p++; if (!*p) goto fail; if ((a = hex(*p)) < 0) goto fail; p++; if (!*p) goto fail; if ((b = hex(*p)) < 0) goto fail; add_streambyte_to_string(ret,a*16+b); break; default: add_streambyte_to_string(ret,*p); break; } } return ret; fail: DPRINT(Debug,20,(&Debug, "hdr_qp_decode: FAIL: buffer=%s, next char=%c\n", buffer,*p)); free_string(&ret); return NULL; } static struct string * hdr_base64_decode P_((charset_t set,char *buffer, char *lang)); static struct string * hdr_base64_decode(set,buffer,lang) charset_t set; char *buffer; char *lang; { struct string *ret = lang ? new_langstring(set,lang) : new_string(set); int val = 0; int bits = 0; unsigned char *p; for (p = us_str(buffer); *p; p++) { int a; if ('=' == *p) break; if ((a=base64(*p)) < 0) goto fail; val = (val << 6) | a; bits += 6; if (bits >= 8) { int n; n = val >> (bits-8); val -= n << (bits-8); bits -= 8; add_streambyte_to_string(ret,n); } } return ret; fail: DPRINT(Debug,20,(&Debug, "hdr_base64_decode: FAIL: buffer=%s, next char=%c\n", buffer,*p)); free_string(&ret); return NULL; } static struct string * hdr_decode_word P_((char *buffer)); static struct string * hdr_decode_word(buffer) char *buffer; { char * temp = safe_strdup(buffer); char *p = temp; char *sn = NULL; char *lang = NULL; char E = '\0'; char *encoded = NULL; struct string *ret = NULL; charset_t set; char *front, *end; struct string *fstr, *estr; /* Pasear: front, end are used to solve buffer: abc""=?...?=" problem */ front = p; while (*p && '=' != *p) ++p; if (front != p && '=' == *p && '"' == *(p-1)) *(p-1) = '\0'; if ('=' != *p) goto fail; *p = '\0'; ++p; if ('?' != *p++) goto fail; sn = p; while (*p && '?' != *p && '*' != *p) p++; if (!*p) goto fail; if ('*' == *p) { *p++ = '\0'; lang = p; while (*p && '?' != *p && '*' != *p) p++; if ('?' != *p) goto fail; } *p++ = '\0'; E = *p++; if ('Q' != E && 'B' != E && 'q' != E && 'b' != E) goto fail; if ('?' != *p++) goto fail; encoded = p; while (*p && '?' != *p) p++; if (!*p) goto fail; *p = '\0'; p++; if ('=' != *p++) goto fail; if ('"' == *p) ++p; end = p; set = MIME_name_to_charset(sn,CHARSET_create); switch(E) { case 'Q': case 'q': ret = hdr_qp_decode(set,encoded,lang); break; case 'B': case 'b': ret = hdr_base64_decode(set,encoded,lang); break; } /* Pasear */ if (ret){ estr = ret; fstr = new_string2(system_charset,us_str(front)); fstr = ret = cat_strings(fstr, ret, 0); free_string(&estr); estr = new_string2(system_charset,us_str(end)); ret = cat_strings(ret, estr, 0); free_string(&estr); free_string(&fstr); } fail: if (!ret) { DPRINT(Debug,20,(&Debug, "hdr_decode_word: FAIL: buffer='%s' -- next char=%c\n", buffer,*p)); } free(temp); return ret; } static struct string * hdr_dequote P_((char *buffer, charset_t defcharset)); static struct string * hdr_dequote(buffer,defcharset) char *buffer; charset_t defcharset; { struct string * ret; char *p; char *t = buffer; for (p = buffer; *p; p++) { if ('\\' == *p) { p++; if ('\0' == *p) break; } else if ('"' == *p) continue; *t++ = *p; } *t = '\0'; ret = new_string2(defcharset,us_str(buffer)); return ret; } static struct string * hdr_comment P_((char *buffer, charset_t defcharset, int demime)); static struct string * hdr_comment(buffer,defcharset,demime) char *buffer; charset_t defcharset; int demime; { struct string * ret = new_string(defcharset); char * walk, *ptr; unsigned char last_char = 0; for (ptr = buffer; ptr && *ptr; ptr = walk) { unsigned char safe = 0; struct string * ok = NULL; int nostore = 0; walk = blstrpbrk(ptr," \t\r\n()"); if (walk) { safe = *walk; *walk = '\0'; walk++; } /* Try decode it (perhaps not encoded word at all) */ ok = hdr_decode_word(ptr); if (!ok) { /* now if it was not encoded, then we need add last space */ if (last_char) { unsigned char ascii[2]; ascii[0] = last_char; ascii[1] = '\0'; add_ascii_to_string(ret,ascii); } /* If decoding failed add word as is */ ok = new_string2(defcharset,us_str(ptr)); nostore = 1; } else if (last_char == ')' || last_char == '(') { /* Also we need add last char if not space */ add_streambyte_to_string(ret,last_char); } /* Now compine strings */ append_string(&ret,ok); free_string(&ok); if (nostore) { /* If last was not encoded we are not going to delete space between words */ if (safe) { unsigned char ascii[2]; ascii[0] = safe; ascii[1] = '\0'; add_ascii_to_string(ret,ascii); } last_char = 0; } else last_char = safe; } return ret; } static struct string * hdr_phrase P_((char *buffer, charset_t defcharset, int demime)); static struct string * hdr_phrase(buffer,defcharset,demime) char *buffer; charset_t defcharset; int demime; { struct string * ret = new_string(defcharset); char **tokenized = rfc822_tokenize(buffer); unsigned char * last_char = NULL; int i, encoded; char* p; for (i = 0; tokenized[i]; i++) { struct string * ok = NULL; int nostore = 0; /* Pasear: detect if it is a encoded string */ encoded = 0; if ('"' == tokenized[i][0]){ p = tokenized[i]; while (*p && *p != '=') ++p; if (*p && *p == '=' && *(p+1) && *(p+1) == '?' ) encoded = 1; } if ('(' == tokenized[i][0]) { /* we need add last space */ if (last_char) add_ascii_to_string(ret,last_char); ok = hdr_comment(tokenized[i],defcharset,demime); nostore = 1; } else if (!encoded && '"' == tokenized[i][0]) { /* we need add last space */ if (last_char) add_ascii_to_string(ret,last_char); ok = hdr_dequote(tokenized[i],defcharset); nostore = 1; } else { /* Try decode it (perhaps not encoded word at all) */ if (demime) ok = hdr_decode_word(tokenized[i]); if (!ok) { /* now if it was not encoded, then we need add last space */ if (last_char) add_ascii_to_string(ret,last_char); /* If decoding failed add word as is */ ok = new_string2(defcharset,us_str(tokenized[i])); nostore = 1; } } /* Now compine strings */ append_string(&ret,ok); free_string(&ok); last_char = 0; if (!nostore && tokenized[i+1] && (' ' == tokenized[i+1][0] || '\t' == tokenized[i+1][0] || '\r' == tokenized[i+1][0] || '\n' == tokenized[i+1][0])) { i++; last_char = us_str(tokenized[i]); } } free_rfc822tokenized(tokenized); return ret; } static struct string * hdr_text P_((char *buffer, charset_t defcharset, int demime)); static struct string * hdr_text(buffer,defcharset,demime) char *buffer; charset_t defcharset; int demime; { struct string * ret = NULL; char * walk, *ptr; unsigned char * last_space = NULL; for (ptr = buffer; ptr && *ptr; ptr = walk) { unsigned char safe = 0; struct string * ok = NULL; int nostore = 0; char * skipstore = NULL; walk = strpbrk(ptr," \t\r\n"); if (walk) { safe = *walk; *walk = '\0'; walk++; skipstore = walk; /* Jump to next word */ while (*walk && NULL != strchr(" \t\r\n", *walk)) walk++; } /* Try decode it (perhaps not encoded word at all) */ if (demime) ok = hdr_decode_word(ptr); if (!ok) { /* now if it was not encoded, then we need add last space */ if (last_space) { if (!ret) ret = new_string(defcharset); add_ascii_to_string(ret,last_space); } /* If decoding failed add word as is */ ok = new_string2(defcharset,us_str(ptr)); nostore = 1; } /* Now compine strings */ append_string(&ret,ok); free_string(&ok); if (last_space) free(last_space); last_space = NULL; if (safe) { int L; if (!skipstore) panic("STRING PANIC",__FILE__,__LINE__,"hdr_text", "skipstore not set",0); L = walk - skipstore; if (L < 0) panic("STRING PANIC",__FILE__,__LINE__,"hdr_text", "bad len",0); last_space = safe_malloc(L+2); last_space[0] = safe; if (L > 0) strncpy(us2s(last_space)+1,skipstore,L); last_space[L+1] = '\0'; } if (nostore) { /* If last was not encoded we are not going to delete space between words */ if (last_space) { if (!ret) ret = new_string(defcharset); add_ascii_to_string(ret,last_space); free(last_space); last_space = NULL; } } } return ret; } /* class is one of HDR_PHRASE, HDR_COMMENT, HDR_TEXT */ struct string * hdr_to_string(class,buffer,defcharset, demime) int class; CONST char *buffer; charset_t defcharset; int demime; { struct string * ret = NULL; char *temp = safe_strdup(buffer); switch(class) { case HDR_PHRASE: ret = hdr_phrase(temp,defcharset,demime); break; case HDR_COMMENT: ret = hdr_comment(temp,defcharset,demime); break; case HDR_TEXT: ret = hdr_text(temp,defcharset,demime); break; } free(temp); if (!ret) { ret = new_string(defcharset); DPRINT(Debug,30,(&Debug, "hdr_to_string: Returning empty header...\n")); } DPRINT(Debug,30,(&Debug, "hdr_to_string=%p (class=%d, buffer='%s', defcharset=%p '%s', demime=%d)\n", ret, class,buffer, defcharset, defcharset->MIME_name ? defcharset->MIME_name : "", demime)); return ret; } /* * Local Variables: * mode:c * c-basic-offset:4 * buffer-file-coding-system: iso-8859-1 * End: */