static char rcsid[] = "@(#)$Id: mime_param.c,v 1.18 2006/05/07 08:35:31 hurtta Exp $"; /****************************************************************************** * The Elm (ME+) Mail System - $Revision: 1.18 $ $State: Exp $ * * Author: Kari Hurtta * or Kari Hurtta *****************************************************************************/ #include "def_melib.h" #include "s_me.h" DEBUG_VAR(Debug,__FILE__,"mime"); static void free_parsed P_((struct mime_param *P)); static void free_parsed(P) struct mime_param *P; { if (P->ascii_params) { int i; for (i = 0; i < P->ascii_param_count; i++) { if (P->ascii_params[i].param) { free(P->ascii_params[i].param); P->ascii_params[i].param = NULL; } if (P->ascii_params[i].value) { free(P->ascii_params[i].value); P->ascii_params[i].value = NULL; } } free(P->ascii_params); P->ascii_params = NULL; } P->ascii_param_count =0; if (P->string_params) { int i; for (i = 0; i < P->string_param_count; i++) { if (P->string_params[i].param) { free(P->string_params[i].param); P->string_params[i].param = NULL; } if (P->string_params[i].value) { free_string(& (P->string_params[i].value)); } } free(P->string_params); P->string_params = NULL; } P->string_param_count =0; } static void free_raw P_((struct mime_param *P)); static void free_raw(P) struct mime_param *P; { if (P->params) { int i; for (i = 0; i < P->param_count; i++) { if (P->params[i].raw_param) { free(P->params[i].raw_param); P->params[i].raw_param = NULL; } if (P->params[i].raw_value) { free(P->params[i].raw_value); P->params[i].raw_value = NULL; } } free(P->params); P->params = NULL; } P->param_count =0; } void free_mime_param(x) struct mime_param **x; { struct mime_param *P = *x; if (MIME_PARAM_magic != P->magic) mime_panic(__FILE__,__LINE__,"free_mime_param", "Bad magic number"); free_raw(P); free_parsed(P); free(P); P = NULL; *x = P; } static struct mime_param * alloc_mime_param P_((void)); static struct mime_param * alloc_mime_param() { struct mime_param *P = safe_malloc(sizeof (*P)); bzero((void *)P, sizeof (*P)); P->magic = MIME_PARAM_magic; P->params = NULL; P->param_count = 0; P->def = NULL; P->ascii_params = NULL; P->ascii_param_count = 0; P->string_params = NULL; P->string_param_count = 0; return P; } struct mime_param * copy_mime_param(src) struct mime_param *src; { struct mime_param *P = alloc_mime_param(); P->def = src->def; if (src->params) { int i; P->params = safe_malloc(src->param_count * sizeof (P->params[0])); for (i = 0; i < src->param_count; i++) { if (src->params[i].raw_param) P->params[i].raw_param = safe_strdup(src->params[i].raw_param); if (src->params[i].raw_value) P->params[i].raw_value = safe_strdup(src->params[i].raw_value); } P->param_count = src->param_count; } /* These params are rebuild from raw params */ P->ascii_params = NULL; P->ascii_param_count = 0; P->string_params = NULL; P->string_param_count = 0; return P; } static struct ascii_pair * add_ascii_pair_1 P_((struct ascii_pair **list, int *len)); static struct ascii_pair * add_ascii_pair_1(list,len) struct ascii_pair **list; int *len; { struct ascii_pair * ascii_params = *list; int ascii_param_count = *len; struct ascii_pair * ret = NULL; ascii_params = safe_realloc(ascii_params, (ascii_param_count+1) * sizeof(ascii_params[0])); bzero((void *)& (ascii_params[ascii_param_count]), sizeof ((ascii_params[ascii_param_count]))); ascii_params[ascii_param_count].is_compat = 0; ascii_params[ascii_param_count].param = NULL; ascii_params[ascii_param_count].value = NULL; ret = (&(ascii_params[ascii_param_count++])); *list = ascii_params; *len = ascii_param_count; return ret; } static struct ascii_pair * add_ascii_pair P_((struct mime_param *P)); static struct ascii_pair * add_ascii_pair(P) struct mime_param *P; { return add_ascii_pair_1( & (P->ascii_params), & (P->ascii_param_count) ); } static struct string_pair * add_string_pair_1 P_((struct string_pair **list, int *len)); static struct string_pair * add_string_pair_1(list,len) struct string_pair **list; int *len; { struct string_pair *ret; struct string_pair * string_params = *list; int string_param_count = *len; string_params = safe_realloc(string_params, (string_param_count +1) * sizeof(string_params[0])); bzero((void *)& (string_params[string_param_count]), sizeof ((string_params[string_param_count]))); string_params[string_param_count].param = NULL; string_params[string_param_count].value = NULL; ret = (&(string_params[string_param_count++])); *list = string_params; *len = string_param_count; return ret; } static struct string_pair * add_string_pair P_((struct mime_param *P)); static struct string_pair * add_string_pair(P) struct mime_param *P; { return add_string_pair_1 (& (P->string_params), & (P->string_param_count)); } static struct mp_pair * add_mp_pair_1 P_((struct mp_pair **list, int *len)); static struct mp_pair * add_mp_pair_1(list,len) struct mp_pair **list; int *len; { struct mp_pair *params = *list; int param_count = *len; struct mp_pair *ret = NULL; params = safe_realloc(params, (param_count +1) * sizeof (params[0])); bzero ((void *) &(params[param_count]), sizeof (params[param_count])); params[param_count].raw_param = NULL; params[param_count].raw_value = NULL; ret = (& (params[ param_count++])); *list = params; *len = param_count; return ret; } static unsigned char * us_str P_((char *str)); static unsigned char * us_str(str) char *str; { return (unsigned char *)str; } static struct mp_pair * add_mp_pair P_((struct mime_param *P)); static struct mp_pair * add_mp_pair(P) struct mime_param *P; { return add_mp_pair_1(& (P->params), &(P->param_count)); } static int append_encoded P_((struct string *buffer,char *value)); static int append_encoded(buffer,value) struct string *buffer; char *value; { unsigned char *p; for (p = us_str(value); *p; p++) { if ('\'' == *p || '"' == *p) { DPRINT(Debug,9,(&Debug, "append_encoded: bad character %c, value=%s\n", *p,value)); return 0; } if ('%' == *p) { int v1, v2; unsigned char v; p++; if (! *p) { DPRINT(Debug,9,(&Debug, "append encoded: short %% sequence, value=%s\n", value)); return 0; } v1 = hex(*p); p++; if (! *p) { DPRINT(Debug,9,(&Debug, "append encoded: short %% sequence, value=%s\n", value)); return 0; } v2 = hex(*p); if (v1 < 0 || v2 < 0) { DPRINT(Debug,9,(&Debug, "append encoded: bad %% sequence, value=%s\n", value)); return 0; } v = ( v1 << 4) | v2; if (! add_streambyte_to_string(buffer,v)) { DPRINT(Debug,9,(&Debug, "append encoded: bad character %%%02x, value=%s\n", v,value)); return 0; } } else { if (! add_streambyte_to_string(buffer,(unsigned char)*p)) { DPRINT(Debug,9,(&Debug, "append encoded: bad character %c, value=%s\n", *p,value)); return 0; } } } return 1; } static struct string * parse_encoded_first P_((char *value)); static struct string * parse_encoded_first(value) char *value; { struct string *ret = NULL; int l = strlen(value); char * buffer = safe_malloc(l+1); char * lang = NULL; char * cs = NULL; charset_t cs1 = NULL; int count = 0; char *p1,*p2; /* character set */ cs = buffer; for (p1 = value, p2 =buffer; *p1 && '\'' != *p1 && p2 <= buffer + l; p1++) { *p2++ = *p1; } *p2 = '\0'; if ('\'' != *p1) { DPRINT(Debug,9,(&Debug, "parse_encoded_first: Missing ': %s\n",value)); goto fail; } p1++; if (p2 == cs) cs = NULL; /* language */ p2++; lang = p2; for (; *p1 && '\'' != *p1 && p2 <= buffer + l; p1++) { *p2++ = *p1; } *p2 = '\0'; if ('\'' != *p1) { DPRINT(Debug,9,(&Debug, "parse_encoded_first: Missing second ': %s\n", value)); goto fail; } p1++; if (p2 == lang) lang = NULL; if (cs) { DPRINT(Debug,9,(&Debug, "parse_encoded_first: cs=%s\n",cs)); cs1 = MIME_name_to_charset(cs,CHARSET_create); } else cs1 = RAW_BUFFER; /* charset is not relevant to parameter value! */ if (lang) { DPRINT(Debug,9,(&Debug, "parse_encoded_first: lang=%s\n",lang)); ret = new_langstring(cs1,lang); } else ret = new_string(cs1); if (!append_encoded(ret,p1)) { DPRINT(Debug,9,(&Debug, "parse_encoded_first: parse error .. ignoring result\n")); free_string(&ret); } fail: free(buffer); return ret; } #define MAX_POSITIONAL 100 static void parse_raw_params P_((struct mime_param *P)); static void parse_raw_params(P) struct mime_param *P; { struct positional_params { char *name; int bad; struct loc_pos { int is_new; int loc; } *locations ; int location_count; } * positional = NULL; int positional_count = 0; free_parsed(P); /* Pass 1 look item 0 on parameters */ if (P->params) { int i; for (i = 0; i < P->param_count; i++) { char * name = P->params[i].raw_param; char * p; if (!name) continue; if (!name[0]) { DPRINT(Debug,9,(&Debug, "parse_raw_params: %d: parameter without name\n", i)); continue; } for (p = name; *p; p++) { if ('*' == *p) break; } if ('\0' == *p) { char *x; /* compat parameter */ /* Check for 8-bit */ if (! P->params[i].raw_value) { DPRINT(Debug,9,(&Debug, "parse_raw_params: %d: %s: compat parameter without value\n", i,name)); continue; } for (x = P->params[i].raw_value; *x; x++) { if (0 != (*x & 0x80)) break; } if ('\0' == *x) { /* Ascii value -- really compat */ struct ascii_pair * A = add_ascii_pair(P); int l = x - P->params[i].raw_value; DPRINT(Debug,9,(&Debug, "parse_raw_params: *** %d: %s=%s\n", i,name,P->params[i].raw_value)); A->is_compat = 1; A->param = safe_strdup(name); A->value = dequote_opt(P->params[i].raw_value,l); DPRINT(Debug,9,(&Debug, "parse_raw_params: [compat] %s=%s\n", A->param, A->value)); } else { struct string_pair * S = add_string_pair(P); char * val; int l; DPRINT(Debug,9,(&Debug, "parse_raw_params: %d: %s: 8-bit data on compat parameter\n", i,name)); DPRINT(Debug,9,(&Debug, "parse_raw_params: *** %d: %s=%s\n", i,name,P->params[i].raw_value)); if (! P->def ) { DPRINT(Debug,9,(&Debug, "parse_raw_params: NO DEFAULT CHARSET -- parameter ignored\n")); continue; } S->param = safe_strdup(name); while (*x) x++; l = x - P->params[i].raw_value; val = dequote_opt(P->params[i].raw_value,l); S->value = new_string2(P->def,us_str(val)); free(val); DPRINT(Debug,9,(&Debug, "parse_raw_params: [8-bit] %s=%S\n", S->param, S->value)); } } else { int is_new = 0; int is_partial = 0; int index = 0; int l = p - name; char * name1 = safe_malloc(l+1); strncpy(name1,name,l); name1[l] = '\0'; p++; if ('\0' == *p ) { /* '*' was last character on name */ is_new = 1; } else if (*p >= '0' && *p <= '9') { char *end = NULL; long x; is_partial = 1; x= strtol(p,&end,10); if (x > MAX_POSITIONAL) { DPRINT(Debug,9,(&Debug, "parse_raw_params: %d: %s: On paramater %s postion value %ld is too big (max %d)\n", i,name,name1,x, MAX_POSITIONAL)); goto fail1; } index = x; if ('\0' != *end && '*' != *end) { DPRINT(Debug,9,(&Debug, "parse_raw_params: %d: %s: On paramater %s after position value %ld there is garbage: %s\n", i,name,name1,x,end)); goto fail1; } p = end; } if ('*' == *p) { is_new = 1; p++; } if (*p) { DPRINT(Debug,9,(&Debug, "parse_raw_params: %d: %s: On paramater %s there is garbage after '*': %s\n", i,name,name1,p)); goto fail1; } if (is_partial) { int x; for (x = 0; x < positional_count; x++) { if (0 == istrcmp(name1,positional[x].name)) break; } if (x >= positional_count) { positional = safe_realloc(positional, (x+1) * sizeof(positional[0])); positional[x].name = name1; name1 = NULL; positional[x].bad = 0; positional[x].locations = NULL; positional[x].location_count = 0; positional_count = x+1; } if (index >= positional[x].location_count) { int y; positional[x].locations = safe_realloc(positional[x].locations, (index+1) * sizeof(positional[x].locations[0])); for (y = positional[x].location_count; y <= index; y++) { positional[x].locations[y].is_new = 0; positional[x].locations[y].loc = -1; } positional[x].location_count = index+1; } if (index < 0) { DPRINT(Debug,9,(&Debug, "parse_raw_params: %d: %s: On paramater %s position value %ld is negative\n", i,name,positional[x].name,index)); positional[x].bad = 1; goto fail1; } if (positional[x].locations[index].loc != -1) { DPRINT(Debug,9,(&Debug, "parse_raw_params: %d: %s: On paramater %s position value %ld is duplicate\n", i,name,positional[x].name,index)); positional[x].bad = 1; goto fail1; } positional[x].locations[index].loc = i; positional[x].locations[index].is_new = is_new; } else { /* Not a partial value */ char *value = P->params[i].raw_value; struct string * X; struct string_pair * S; if (!is_new) mime_panic(__FILE__,__LINE__,"parse_raw_params", "compat parameters should be handled already"); if (! value) { DPRINT(Debug,9,(&Debug, "parse_raw_params: %d: %s: parameter %s without value\n", i,name,name1)); goto fail1; } DPRINT(Debug,9,(&Debug, "parse_raw_params: *** %d: %s=%s\n", i,name,P->params[i].raw_value)); X = parse_encoded_first(value); if (!X) { goto fail1; } S = add_string_pair(P); S->param = name1; name1 = NULL; S->value = X; DPRINT(Debug,9,(&Debug, "parse_raw_params: [string] %s=%S\n", S->param, S->value)); } fail1: if (name1) free(name1); } } } /* Pass 2 look positional arguments */ if (positional) { int x; for (x = 0; x < positional_count; x++) { int y; int new_seen = 0; for (y = 0; y < positional[x].location_count; y++) { if (positional[x].locations[y].loc == -1) { DPRINT(Debug,9,(&Debug, "parse_raw_params: On paramater %s position value %d is missing\n", positional[x].name,y)); positional[x].bad = 1; break; } if (positional[x].locations[y].loc < 0 || positional[x].locations[y].loc >= P->param_count) mime_panic(__FILE__,__LINE__,"parse_raw_params", "Bad location index"); new_seen |= positional[x].locations[0].is_new; } if (positional[x].location_count < 1) mime_panic(__FILE__,__LINE__,"parse_raw_params", "Inbossible location count"); if (positional[x].bad) continue; if (new_seen) { struct string_pair * S = NULL; /* Handle first parameter */ if (positional[x].locations[0].is_new) { int i = positional[x].locations[0].loc; char *value = P->params[i].raw_value; struct string * X; DPRINT(Debug,9,(&Debug, "parse_raw_params: *** %d: %s=%s\n", i, P->params[i].raw_param, P->params[i].raw_value)); X = parse_encoded_first(value); if (!X) { goto fail2; } S = add_string_pair(P); S->param = safe_strdup(positional[x].name); S->value = X; } else { int i = positional[x].locations[0].loc; char *value = P->params[i].raw_value; int l = strlen(value); char *val; DPRINT(Debug,9,(&Debug, "parse_raw_params: *** %d: %s=%s\n", i, P->params[i].raw_param, P->params[i].raw_value)); val = dequote_opt(value,l); S = add_string_pair(P); S->param = safe_strdup(positional[x].name); /* FIXME ??? Is this correct ??? */ S->value = new_string2(RAW_BUFFER,us_str(val)); free(val); } /* Handle rest of parameters */ for (y = 1; y < positional[x].location_count; y++) { int i = positional[x].locations[y].loc; char *value = P->params[i].raw_value; DPRINT(Debug,9,(&Debug, "parse_raw_params: *** %d: %s=%s\n", i, P->params[i].raw_param, P->params[i].raw_value)); if (positional[x].locations[x].is_new) { if (!append_encoded(S->value,value)) goto failX; } else { char * val; int l1; int errors = 0; int l = strlen(value); val = dequote_opt(value,l); l1 = strlen(val); if (!add_streambytes_to_string(S->value,l1,us_str(val),&errors)) { free(val); goto failX; } free(val); DPRINT(Debug,9,(&Debug, "parse_raw_params: [added %d] %s=%S\n", x, S->param, S->value)); } } DPRINT(Debug,9,(&Debug, "parse_raw_params: [splitted string] %s=%S\n", S->param, S->value)); if (0) { failX: DPRINT(Debug,9,(&Debug, "parse_raw_params: %s: Failed to parse value\n", S->param)); free_string(& (S->value)); } } else { struct ascii_pair * A = add_ascii_pair(P); char *p; A->is_compat = 0; A->param = safe_strdup(positional[x].name); A->value = NULL; for (y = 0; y < positional[x].location_count; y++) { int i = positional[x].locations[y].loc; char *value = P->params[i].raw_value; int l = strlen(value); char *val; DPRINT(Debug,9,(&Debug, "parse_raw_params: *** %d: %s=%s\n", i, P->params[i].raw_param, P->params[i].raw_value)); if (positional[x].locations[x].is_new) mime_panic(__FILE__,__LINE__,"parse_raw_params", "Inbossible encoding"); val = dequote_opt(value,l); A->value = strmcat(A->value,val); free(val); } DPRINT(Debug,9,(&Debug, "parse_raw_params: [splitted ascii] %s=%s\n", A->param, A->value)); for (p = A->value; *p; p++) { if (0 != (*p & 0x80)) break; } if (*p) { struct string_pair * S; DPRINT(Debug,9,(&Debug, "parse_raw_params: %s: 8-bit data on parameter\n", A->param)); if (! P->def ) { DPRINT(Debug,9,(&Debug, "parse_raw_params: NO DEFAULT CHARSET -- parameter ignored\n")); /* Destroy non-ascii value */ free(A->value); A->value = NULL; continue; } S = add_string_pair(P); S->param = safe_strdup(A->param); S->value = new_string2(P->def,us_str(A->value)); /* Destroy non-ascii value */ free(A->value); A->value = NULL; DPRINT(Debug,9,(&Debug, "parse_raw_params: [fixup string] %s=%S\n", S->param, S->value)); } } fail2: ; } } /* Cleanup */ if (positional) { int i; for (i = 0; i < positional_count; i++) { if (positional[i].locations) { free(positional[i].locations); positional[i].locations = NULL; } positional[i].location_count = 0; } free(positional); } } struct mime_param * parse_mime_param(headername,value,def,header_error) CONST char *headername; CONST char *value; charset_t def; struct header_errors **header_error; { CONST char *ptr; struct mime_param *P = alloc_mime_param(); DPRINT(Debug,9,(&Debug,"parse_mime_param: headername=%s value=%s\n", headername ? headername : "", value)); P->def = def; /* It is assumed that comments are already removed with rfc822_reap_comments() */ for (ptr = value; *ptr; ) { CONST char * start; CONST char * last; int len; struct mp_pair * mp = NULL; while (*ptr && isascii(*ptr) && isspace(*ptr)) ptr++; if (!*ptr) break ; start = ptr; last = NULL; /* Get param name */ while (*ptr) { if (isascii(*ptr) && isspace(*ptr)) { if (!last) last = ptr; } else if ('=' == *ptr) { if (!last) last = ptr; break; } else if ('"' == *ptr) { if (!last) last = ptr; break; } else if (';' == *ptr) { if (!last) last = ptr; break; } else if (last) { break; } ptr++; } if (!last) last = ptr; while (*ptr && isascii(*ptr) && isspace(*ptr)) ptr++; if ('=' == *ptr) ptr++; else { if (headername) process_header_error(header_error, CATGETS(elm_msg_cat, MeSet, MeMissingEqual, "PARSE ERROR: Missing = on %s header"), headername); } if (last == start) { if (headername) process_header_error(header_error, CATGETS(elm_msg_cat, MeSet, MeMissingName, "PARSE ERROR: Missing name of param on %s header"), headername); } len = last-start; mp = add_mp_pair(P); mp->raw_param = safe_malloc(len+1); strncpy(mp->raw_param,start,len); mp->raw_param[len] = '\0'; DPRINT(Debug,9,(&Debug,"parse_mime_param: [%d] param=%s\n", P->param_count-1,mp->raw_param)); while (*ptr && isascii(*ptr) && isspace(*ptr)) ptr++; start = ptr; last = NULL; if ('"' == *ptr) { ptr++; while (*ptr && '"' != *ptr) { if ('\\' == *ptr) { ptr++; if (!*ptr) { if (headername) process_header_error(header_error, CATGETS(elm_msg_cat, MeSet, MeTrailingBackslashHeader, "PARSE ERROR: Trailing \\ on %s header"), headername); break; } } ptr++; } if ('"' != *ptr) { if (headername) process_header_error(header_error, CATGETS(elm_msg_cat, MeSet, MeMissingQuote, "PARSE ERROR: Missing ending \" on %s header"), headername); } else ptr++; } else { while (*ptr) { if (isascii(*ptr) && isspace(*ptr)) { if (!last) last = ptr; } else if ('=' == *ptr) { if (headername) process_header_error(header_error, CATGETS(elm_msg_cat, MeSet, MeEqualOnValue, "PARSE ERROR: Missing ; or = on param value on %s header"), headername); } else if ('"' == *ptr) { if (!last) last = ptr; break; } else if (';' == *ptr) { if (!last) last = ptr; break; } else if (last) { if (headername) process_header_error(header_error, CATGETS(elm_msg_cat, MeSet, MeSpaceOnValue, "PARSE ERROR: Missing ; or space on param value on %s header"), headername); last = NULL; } ptr++; } } if (!last) last = ptr; while (*ptr && isascii(*ptr) && isspace(*ptr)) ptr++; if (';' == *ptr) ptr++; else if (*ptr) { if (headername) process_header_error(header_error, CATGETS(elm_msg_cat, MeSet, MeMissingSemicolonHeader, "PARSE ERROR: Missing ; on %s header"), headername); } if (last == start) { if (headername) process_header_error(header_error, CATGETS(elm_msg_cat, MeSet, MeMissingValue, "PARSE ERROR: Missing value of param on %s header"), headername); } len = last-start; mp->raw_value = safe_malloc(len+1); strncpy(mp->raw_value,start,len); mp->raw_value[len] = '\0'; DPRINT(Debug,9,(&Debug,"parse_mime_param: [%d] value=%s\n", P->param_count-1,mp->raw_value)); } return P; } static CONST char *find_ascii_param P_((struct mime_param *P, char *name, int compat)); static CONST char *find_ascii_param(P,name,compat) struct mime_param *P; char *name; int compat; { int i; for (i = 0; i < P->ascii_param_count; i++) { if (P->ascii_params[i].is_compat == compat && P->ascii_params[i].param && 0 == istrcmp(P->ascii_params[i].param,name)) return P->ascii_params[i].value; } return NULL; } static CONST struct string *find_string_param P_((struct mime_param *P, char *name)); static CONST struct string *find_string_param(P,name) struct mime_param *P; char *name; { int i; for (i = 0; i < P->string_param_count; i++) { if (P->string_params[i].param && 0 == istrcmp(P->string_params[i].param,name)) return P->string_params[i].value; } return NULL; } CONST char * get_mime_param_compat(P,name) struct mime_param *P; char *name; { if (!P) return NULL; if (MIME_PARAM_magic != P->magic) mime_panic(__FILE__,__LINE__,"get_mime_param_compat", "Bad magic number"); if (! P->ascii_params && ! P->string_params) parse_raw_params(P); if (P->ascii_params) return find_ascii_param(P,name,1); DPRINT(Debug,9,(&Debug, "get_mime_param_compat: No parameters\n")); return NULL; } CONST char * get_mime_param_ascii(P,name) struct mime_param *P; char *name; { int i; if (!P) return NULL; if (MIME_PARAM_magic != P->magic) mime_panic(__FILE__,__LINE__,"get_mime_param_ascii", "Bad magic number"); if (! P->ascii_params && ! P->string_params) parse_raw_params(P); if (P->ascii_params) { CONST char *found = find_ascii_param(P,name,0); if (found) return found; return find_ascii_param(P,name,1); } DPRINT(Debug,9,(&Debug, "get_mime_param_ascii: No parameters\n")); return NULL; } static CONST unsigned char * cus_str P_((const char *str)); static CONST unsigned char * cus_str(str) CONST char *str; { return (CONST unsigned char *)str; } CONST struct string * get_mime_param(P,name) struct mime_param *P; char *name; { if (!P) return NULL; if (MIME_PARAM_magic != P->magic) mime_panic(__FILE__,__LINE__,"get_mime_param", "Bad magic number"); if (! P->ascii_params && ! P->string_params) parse_raw_params(P); if (P->string_params) { CONST struct string *found = find_string_param(P,name); if (found) return found; } if (P->ascii_params) { charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0); CONST char *found = find_ascii_param(P,name,0); struct string_pair *S; if (!found) found = find_ascii_param(P,name,1); if (!found) return NULL; if (!ascii_ptr) mime_panic(__FILE__,__LINE__,"get_mime_param", "US-ASCII not found"); S = add_string_pair(P); S->param = safe_strdup(name); S->value = new_string2(ascii_ptr,cus_str(found)); return S->value; } DPRINT(Debug,9,(&Debug, "get_mime_param: No parameters\n")); return NULL; } static int ascii_need_quote P_((char *value)); static int ascii_need_quote(value) char *value; { char *p; if (! value[0]) return 1; for (p = value; *p; p++) { if (*p <= ' ') return 1; if (*p >= '"' && *p <= '/') return 1; if (*p >= ':' && *p <= '?') return 1; if (*p == '\\') return 1; } return 0; } static int string_is_ascii_control P_((struct string * value)); static int string_is_ascii_control(value) struct string * value; { int len = string_len(value); int i; for (i = 0; i < len; i++) { uint16 code = give_unicode_from_string(value,i); if (code <= 0x0020 /* space */ ) return 1; if (code >= 0x007F /* DEL */) return 1; } return 0; } static int string_need_quote P_((struct string * value)); static int string_need_quote(value) struct string * value; { int len = string_len(value); int i; if (0 == len) return 1; for (i = 0; i < len; i++) { uint16 code = give_unicode_from_string(value,i); if (code <= 0x0020 /* space */ ) return 1; if (code >= 0x0022 /* " */ && code <= 0x002F /* / */) return 1; if (code >= 0x003A /* : */ && code >= 0x003F /* ? */) return 1; switch(code) { case 0x005C /* \ */: return 1; } } return 0; } static void append_quoted_to_string P_((struct string ** ret, struct string * value)); static void append_quoted_to_string(ret,value) struct string ** ret; struct string * value; { charset_t cs = value->string_type; int len = string_len(value); int i; struct string * tmp = new_string(cs); add_ascii_to_string(tmp,us_str("\"")); for (i = 0; i < len; i++) { uint16 code = give_unicode_from_string(value,i); if (0x0022 /* " */ == code && 0x005C /* \ */ == code) add_ascii_to_string(tmp,us_str("\\")); add_unicode_to_string(tmp,1,&code); } add_ascii_to_string(tmp,us_str("\"")); append_string(ret,tmp); free_string(&tmp); } struct string * unquote_string P_((struct string * value)); struct string * unquote_string(value) struct string * value; { charset_t cs = value->string_type; int len = string_len(value); int i; int q = 0; struct string * ret = new_string(cs); for (i = 0; i < len; i++) { uint16 code = give_unicode_from_string(value,i); if (0x0022 /* " */ == code) { q = !q; continue; } /* Recognize \ escape only in quoted strings */ if (0x005C /* \ */ == code && q) { i++; if (i < len) code = give_unicode_from_string(value,i); else { lib_error(CATGETS(elm_msg_cat, MeSet, MeTrailingBackslashOnString, "Trailing Backslash (\\) on %S"), value); break; } } add_unicode_to_string(ret,1,&code); } if (q) lib_error(CATGETS(elm_msg_cat, MeSet, MeMissingEndQuote, "Missing ending quote (\") on %S"), value); return ret; } static void add_param_string P_((struct string ** ret, char *param, struct string * value)); static void add_param_string(ret,param,value) struct string ** ret; char *param; struct string * value; { if (*ret) add_ascii_to_string(*ret,us_str("; ")); if (!*ret) *ret = new_string(display_charset); add_ascii_to_string(*ret,us_str(param)); add_ascii_to_string(*ret,us_str("=")); if (string_need_quote(value)) append_quoted_to_string(ret,value); else append_string(ret,value); } static void add_param_ascii P_((struct string ** ret, char *param, char * value)); static void add_param_ascii(ret,param,value) struct string ** ret; char *param; char * value; { struct string * tmp ; charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0); if (!ascii_ptr) mime_panic(__FILE__,__LINE__,"add_param_ascii", "US-ASCII not found"); tmp = new_string2(ascii_ptr,us_str(value)); add_param_string(ret,param,tmp); free_string(&tmp); } struct string * show_mime_params(P) struct mime_param *P; { struct string * ret = NULL; if (MIME_PARAM_magic != P->magic) mime_panic(__FILE__,__LINE__,"show_mime_params", "Bad magic number"); if (! P->ascii_params && ! P->string_params) parse_raw_params(P); if (P->string_params) { int i; for (i = 0; i < P->string_param_count; i++) { if (P->string_params[i].param && P->string_params[i].value) add_param_string(&ret,P->string_params[i].param, P->string_params[i].value); } } if (P->ascii_params) { int i; for (i = 0; i < P->ascii_param_count; i++) { if (!P->ascii_params[i].param) continue; if (find_string_param(P,P->ascii_params[i].param)) continue; if (P->ascii_params[i].value) add_param_ascii(&ret,P->ascii_params[i].param, P->ascii_params[i].value); } } return ret; } char * encode_mime_params(P) struct mime_param *P; { char * ret = NULL; if (MIME_PARAM_magic != P->magic) mime_panic(__FILE__,__LINE__,"encode_mime_params", "Bad magic number"); if (P->params) { int i; for (i = 0; i < P->param_count; i++) { if (P->params[i].raw_param && P->params[i].raw_value) { if (ret) ret = strmcat(ret,"; "); ret = strmcat(ret,P->params[i].raw_param); ret = strmcat(ret,"="); ret = strmcat(ret,P->params[i].raw_value); } } } return ret; } char ** encode_mime_params_v(P) struct mime_param *P; { char ** ret = NULL; if (MIME_PARAM_magic != P->magic) mime_panic(__FILE__,__LINE__,"encode_mime_params", "Bad magic number"); if (P->params) { int i; int X = 0; ret = safe_malloc((P->param_count +1) * sizeof (ret[0])); for (i = 0; i < P->param_count; i++) { if (P->params[i].raw_param && P->params[i].raw_value) { ret[X] = safe_strdup(P->params[i].raw_param); ret[X] = strmcat(ret[X],"="); ret[X] = strmcat(ret[X],P->params[i].raw_value); X++; } } ret[X] = NULL; } return ret; } static void gen_splitted_ascii_param P_((struct mime_param *P, char *name, char *value)); static void gen_splitted_ascii_param(P,name,value) struct mime_param *P; char *name; char *value; { char * p, *p1; int counter = 0; for (p = value; *p; p = p1) { struct mp_pair * mp = add_mp_pair(P); char * tmp; int len; for (p1 = p; *p && p1-p < 70; p++) /* nothing */; len = p1-p; mp->raw_param = elm_message(FRM("%s*%d"),name,counter++); tmp = safe_malloc(len+1); strncpy(tmp,p,len); tmp[len] = '\0'; if (ascii_need_quote(tmp)) { mp->raw_value = elm_message(FRM("%Q"),tmp); free(tmp); } else { mp->raw_value = tmp; } } if (counter == 0) { struct mp_pair * mp = add_mp_pair(P); mp->raw_param = elm_message(FRM("%s*%d"),name,counter++); mp->raw_value = safe_strdup("\"\""); } } static char * get_encoded P_((struct string *value)); static char * get_encoded(value) struct string *value; { char * ret = NULL; int alloclen = 0; int len; char * tmp; int ok = charset_ok_p(value->string_type); int i; int count = 0; CONST char *y = get_string_MIME_name(value); if (y && (0 == istrcmp(y,"UTF-8") || 0 == istrcmp(y,"UTF-7"))) ok = 1; /* Allow producing \0 values also ... */ bytestream_from_string(value,&tmp,&len); alloclen = len+10; if (!ok) alloclen = len*3+2; ret = safe_malloc(alloclen); #define ADD(x) { if (count >= alloclen) { \ alloclen += 20; ret = safe_realloc(ret,alloclen); } \ ret[count++] = (x); } for (i = 0; i string_type == RAW_BUFFER) /* charset is not relevant to parameter value! */ cs = ""; else { cs = get_string_MIME_name(value); if (!cs || NULL != strpbrk(cs," \t\r\n()\"?*'=")) cs = "UNKNOWN-8BIT"; } lang = get_string_lang(value); if (!lang || NULL != strpbrk(lang," \t\r\n()\"?*'=")) lang = ""; tmp = get_encoded(value); ret = elm_message(FRM("%s'%s'%s"),cs,lang,tmp); free(tmp); return ret; } static void gen_string_param P_((struct mime_param *P, char *name, struct string *value)); static void gen_string_param(P,name,value) struct mime_param *P; char *name; struct string * value; { struct mp_pair * mp = add_mp_pair(P); mp->raw_param = elm_message(FRM("%s*"),name); mp->raw_value = get_first_encoded(value); DPRINT(Debug,9,(&Debug, "gen_string_param: *** %s=%s\n", mp->raw_param,mp->raw_value)); } #define STRING_SPLIT_LEN 15 static void gen_splitted_string_param P_((struct mime_param *P, char *name, struct string *value)); static void gen_splitted_string_param(P,name,value) struct mime_param *P; char *name; struct string * value; { int len = string_len(value); int pos = 0; struct string *buffer; int counter = 0; struct mp_pair * mp = add_mp_pair(P); buffer = clip_from_string(value,&pos,STRING_SPLIT_LEN); mp->raw_param = elm_message(FRM("%s*%d*"),name,counter++); mp->raw_value = get_first_encoded(buffer); DPRINT(Debug,9,(&Debug, "gen_splitted_string_param: *** %s=%s\n", mp->raw_param,mp->raw_value)); free_string(&buffer); while (pos < len) { buffer = clip_from_string(value,&pos,STRING_SPLIT_LEN); mp = add_mp_pair(P); mp->raw_param = elm_message(FRM("%s*%d*"),name,counter++); mp->raw_value = get_encoded(buffer); DPRINT(Debug,9,(&Debug, "gen_splitted_string_param: *** %s=%s\n", mp->raw_param,mp->raw_value)); free_string(&buffer); } } static char *us2s P_((unsigned char *str)); static char *us2s(str) unsigned char *str; { return (char *)str; } static void parse_rest P_((struct mime_param *P, struct string_token *X, int gen_compat_len, int plain_mode)); static void parse_rest(P,X, gen_compat_len,plain_mode) struct mime_param *P; struct string_token *X; int gen_compat_len; int plain_mode; { charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0); int i = 0; struct string_pair * tmp_list = NULL; int tmp_list_len = 0; struct ascii_pair * a_list = NULL; int a_list_len = 0; struct mp_pair * mp_list = NULL; int mp_list_len = 0; if (!ascii_ptr) mime_panic(__FILE__,__LINE__,"parse_rest", "US-ASCII not found"); while (X[i].token) { struct string * name = NULL; struct string *Y = NULL; char * ascname = NULL; CONST char *lang = NULL; while (X[i].token && ( 0x0020 /* SPACE */ == X[i].special || 0x0028 /* ( */ == X[i].special ) ) { i++; } if (! X[i].token) break; if (0 == X[i].special) name = X[i].token; else { lib_error(CATGETS(elm_msg_cat, MeSet, MeParseErrorNameExpected, "Parse error on %S -- name expected"), X[i].token); goto recover; } if (!can_ascii_string(X[i].token)) lib_error(CATGETS(elm_msg_cat, MeSet, MeAsciiRequiredTok, "Ascii required for token %S"), X[i].token); i++; while (X[i].token && ( 0x0020 /* SPACE */ == X[i].special || 0x0028 /* ( */ == X[i].special ) ) { i++; } if (! X[i].token) { lib_error(CATGETS(elm_msg_cat, MeSet, MeParseErrorEqualExpectedEOS, "Parse error on end of string -- = expected")); break; } if (0x003D /* = */ != X[i].special) { lib_error(CATGETS(elm_msg_cat, MeSet, MeParseErrorEqualExpected, "Parse error on %S -- = expected"), X[i].token); goto recover; } i++; while (X[i].token && ( 0x0020 /* SPACE */ == X[i].special || 0x0028 /* ( */ == X[i].special ) ) { i++; } if (! X[i].token) { lib_error(CATGETS(elm_msg_cat, MeSet, MeParseErrorValueExpectedEOS, "Parse error on end of string -- value of param expected")); break; } if (0x003B /* ';' */ == X[i].special) { lib_error(CATGETS(elm_msg_cat, MeSet, MeParseErrorValueExpected, "Parse error on %S -- value of param expected"), X[i].token); continue; } Y = convert_string(ascii_ptr,name,0); ascname = us2s(stream_from_string(Y,0,NULL)); free_string(&Y); if (NULL != strchr(ascname,'*')) { struct mp_pair *mp = add_mp_pair_1(&mp_list,&mp_list_len); DPRINT(Debug,9,(&Debug, "parse_rest: %s: String as raw parameter: %S\n", ascname,X[i].token)); if (!can_ascii_string(X[i].token)) lib_error(CATGETS(elm_msg_cat, MeSet, MeAsciiRequiredTok, "Ascii required for token %S"), X[i].token); Y = convert_string(ascii_ptr,X[i].token,0); mp->raw_param = ascname; mp->raw_value = us2s(stream_from_string(Y,0,NULL)); free_string(&Y); } else { struct string * value1 = NULL; struct string * value0 = NULL; if (0x0022 /* " */ == X[i].special) value1 = unquote_string(X[i].token); else value1 = dup_string(X[i].token); value0 = value1; /* synonym */ if (plain_mode) { struct ascii_pair *A = NULL; struct string *Y; if (!can_ascii_string(value1)) lib_error(CATGETS(elm_msg_cat, MeSet, MeAsciiRequiredTok, "Ascii required for token %S"), value1); Y = convert_string(ascii_ptr,value1,0); A = add_ascii_pair_1(&a_list,&a_list_len); A->param = ascname; A->value = us2s(stream_from_string(Y,0,NULL)); A->is_compat = 1; free_string(& Y); DPRINT(Debug,9,(&Debug, "parse_rest: [compat] %s=%s\n", A->param, A->value)); } else { /* Ascii version ... */ lang = get_string_lang(value1); if (plain_mode || !lang && can_ascii_string(value1) && !string_is_ascii_control(value1)) { struct ascii_pair *A = NULL; struct string *Y = convert_string(ascii_ptr,value1,0); /* Is really ascii */ A = add_ascii_pair_1(&a_list,&a_list_len); A->param = safe_strdup(ascname); A->value = us2s(stream_from_string(Y,0,NULL)); A->is_compat = string_len(Y) < gen_compat_len; DPRINT(Debug,9,(&Debug, "parse_rest: [%s] %s=%s\n", A->is_compat ? "compat" : "ascii", A->param, A->value)); if (!A->is_compat) goto gencompat; } else { CONST struct string * Y1; CONST char * Y2; { struct string_pair *S = NULL; S = add_string_pair_1(&tmp_list,&tmp_list_len); S->param = ascname; S->value = value1; value1 = NULL; DPRINT(Debug,9,(&Debug, "parse_rest: [string] %s=%S\n", S->param, S->value)); } gencompat: Y1 = get_mime_param(P,ascname); Y2 = find_ascii_param(P,ascname,1); if (Y1 && 0 == string_cmp(Y1,value0,999) && Y2) { /* Preserve existing compat parameter */ struct ascii_pair *A = NULL; DPRINT(Debug,9,(&Debug, "parse_rest: %s: Preserving existing compat parameter: %s\n", ascname,Y2)); A = add_ascii_pair_1(&a_list,&a_list_len); A->param = safe_strdup(ascname); A->value = safe_strdup(Y2); A->is_compat = 1; DPRINT(Debug,9,(&Debug, "parse_rest: [compat] %s=%s\n", A->param, A->value)); } else { if (Y1) { DPRINT(Debug,9,(&Debug, "parse_rest: old string param was: %S\n", Y1)); } if (Y2) { DPRINT(Debug,9,(&Debug, "parse_rest: old compat param was: %s\n", Y2)); } if (gen_compat_len) { struct ascii_pair *A = NULL; struct string *Y = convert_string(ascii_ptr,value0,0); int pos = 0; A = add_ascii_pair_1(&a_list,&a_list_len); A->param = safe_strdup(ascname); A->value = us2s(streamclip_from_string(Y,&pos,gen_compat_len,NULL,NULL)); A->is_compat = 1; DPRINT(Debug,9,(&Debug, "parse_rest: [compat] %s=%s\n", A->param, A->value)); } } } if (value1) free_string(& value1); } } i++; while (X[i].token && ( 0x0020 /* SPACE */ == X[i].special || 0x0028 /* ( */ == X[i].special ) ) { i++; } if (X[i].token && 0x003B /* ';' */ != X[i].special) { lib_error(CATGETS(elm_msg_cat, MeSet, MeParseErrorSemicolonExpected, "Parse error on %S -- ; or end of string expected"), X[i].token); /* syncronize */ recover: while (X[i].token && 0x003B /* ';' */ != X[i].special ) i++; } if (X[i].token && 0x003B /* ';' */ == X[i].special) i++; } if (X[i].token) lib_error(CATGETS(elm_msg_cat, MeSet, MeParseErrorFailed, "Parse error on %S -- failed to parse parameters"), X[i].token); free_parsed(P); P->string_params = tmp_list; P->string_param_count = tmp_list_len; P->ascii_params = a_list; P->ascii_param_count = a_list_len; free_raw(P); P->params = mp_list; P->param_count = mp_list_len; for (i = 0; i < a_list_len; i++) { if (a_list[i].is_compat) { struct mp_pair * mp = add_mp_pair(P); mp->raw_param = safe_strdup(a_list[i].param); if (ascii_need_quote(a_list[i].value)) mp->raw_value = elm_message(FRM("%Q"),a_list[i].value); else mp->raw_value = safe_strdup(a_list[i].value); DPRINT(Debug,9,(&Debug, "parse_rest: *** %s=%s\n", mp->raw_param,mp->raw_value)); } else { gen_splitted_ascii_param(P,a_list[i].param,a_list[i].value); } } for (i = 0; i < tmp_list_len; i++) { /* If there exists ascii -- non-compat version of same parameter, do not add this ... */ if (find_ascii_param(P,tmp_list[i].param,0)) continue; if (string_len(tmp_list[i].value) > STRING_SPLIT_LEN) gen_splitted_string_param(P,tmp_list[i].param,tmp_list[i].value); else gen_string_param(P,tmp_list[i].param,tmp_list[i].value); } } struct mime_param * parse_mime_param_string(value, gen_compat_len, plain_mode) struct string * value; int gen_compat_len; int plain_mode; { struct mime_param *P = alloc_mime_param(); struct string_token *X = string_tokenize(value,TOK_mime); P->def = value->string_type; if (X) { parse_rest(P,X,gen_compat_len, plain_mode); free_string_tokenized(&X); } return P; } char ** split_mime_params(P,in_buffer,gen_compat_len,plain_mode) struct mime_param **P; struct string *in_buffer; int gen_compat_len; int plain_mode; { char **ret = NULL; struct string_token *X; int i,count = 0; if (!*P) *P = alloc_mime_param(); if (MIME_PARAM_magic != (*P)->magic) mime_panic(__FILE__,__LINE__,"split_mime_params", "Bad magic number"); X = string_tokenize(in_buffer,TOK_mime); if (!X) return NULL; for (i=0; X[i].token; i++) { if (0x003B /* ';' */ == X[i].special) break; count++; } if (count) { int j; int k = 0; charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0); if (!ascii_ptr) mime_panic(__FILE__,__LINE__,"split_mime_params", "US-ASCII not found"); ret = safe_malloc((count+1) * sizeof(ret[0])); for (j = 0; j < count; j++) { struct string *Y; if (0x0020 /* SPACE */ == X[j].special) continue; /* Skips comment */ if (0x0028 /* ( */ == X[j].special) continue; if (!can_ascii_string(X[j].token)) lib_error(CATGETS(elm_msg_cat, MeSet, MeAsciiRequiredTok, "Ascii required for token %S"), X[j].token); Y = convert_string(ascii_ptr,X[j].token,0); ret[k] = us2s(stream_from_string(Y,0,NULL)); DPRINT(Debug,9,(&Debug, "split_mime_params: ret[%d]=%s\n", k,ret[k])); k++; free_string(&Y); } ret[k] = NULL; } if (0x003B /* ';' */ == X[i].special) { i++; parse_rest(*P,X+i,gen_compat_len, plain_mode); } else { DPRINT(Debug,9,(&Debug, "split_mime_params: Removing all parameters\n")); free_raw(*P); free_parsed(*P); } free_string_tokenized(&X); return ret; } void mime_params_add_compat(P,name,value) struct mime_param **P; char *name; char *value; { struct ascii_pair *A = NULL; struct mp_pair * mp = NULL; if (!*P) *P = alloc_mime_param(); if (MIME_PARAM_magic != (*P)->magic) mime_panic(__FILE__,__LINE__,"mime_params_add_compat", "Bad magic number"); A = add_ascii_pair(*P); A->param = safe_strdup(name); A->value = safe_strdup(value); A->is_compat = 1; DPRINT(Debug,9,(&Debug, "mime_params_add_compat: [compat] %s=%s\n", A->param,A->value)); mp = add_mp_pair(*P); mp->raw_param = safe_strdup(name); if (ascii_need_quote(value)) mp->raw_value = elm_message(FRM("%Q"),value); else mp->raw_value = safe_strdup(value); DPRINT(Debug,9,(&Debug, "mime_params_add_compat: *** %s=%s\n", mp->raw_param,mp->raw_value)); } void mime_params_add(P,name,value) struct mime_param **P; char *name; struct string *value; { struct string_pair *S = NULL; CONST char *lang; charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0); if (!*P) *P = alloc_mime_param(); if (MIME_PARAM_magic != (*P)->magic) mime_panic(__FILE__,__LINE__,"mime_params_add", "Bad magic number"); if (!ascii_ptr) mime_panic(__FILE__,__LINE__,"mime_params_add", "US-ASCII not found"); S = add_string_pair(*P); S->param = safe_strdup(name); S->value = dup_string(value); DPRINT(Debug,9,(&Debug, "mime_params_add: [string] %s=%S\n", S->param,S->value)); /* Compat parameter is not generated -- it is asusmed that caller calls mime_params_add_compat instead ... */ lang = get_string_lang(value); if (!lang && can_ascii_string(value) && !string_is_ascii_control(value)) { struct ascii_pair *A = NULL; struct string *Y = convert_string(ascii_ptr,value,0); /* Is really ascii */ A = add_ascii_pair(*P); A->param = safe_strdup(name); A->value = us2s(stream_from_string(Y,0,NULL)); A->is_compat = 0; DPRINT(Debug,9,(&Debug, "mime_params_add: [ascii] %s=%s\n", A->param,A->value)); gen_splitted_ascii_param(*P,A->param,A->value); } else { if (string_len(S->value) > STRING_SPLIT_LEN) gen_splitted_string_param(*P,S->param,S->value); else gen_string_param(*P,S->param,S->value); } } /* * Local Variables: * mode:c * c-basic-offset:4 * buffer-file-coding-system: iso-8859-1 * End: */