static char rcsid[] = "@(#)$Id: getaddr.c,v 1.29 2006/04/09 07:37:06 hurtta Exp $"; /****************************************************************************** * The Elm (ME+) Mail System - $Revision: 1.29 $ $State: Exp $ * * Author: Kari Hurtta (was hurtta+elm@ozone.FMI.FI) * * Content (originally) partially moved from melib/parse_util.c * (however rfc822_reap_comments is now on mparse.c) *****************************************************************************/ #include "headers.h" DEBUG_VAR(Debug,__FILE__,"addr"); char **rfc822_tokenize(line) CONST char *line; { CONST char * ptr; char **res; int len, count = 0,i; DPRINT(Debug,25,(&Debug, "rfc822_tokenize(line=\"%s\n\"):\n",line)); for (ptr = line; *ptr; ptr += len) { len = rfc822_toklen(ptr); count++; } DPRINT(Debug,25,(&Debug, "rfc822_tokenize: count=%d\n",count)); res = safe_malloc((count+1) * sizeof (char *)); for (i = 0, ptr = line; i < count; i++, ptr += len) { len = rfc822_toklen(ptr); res[i] = safe_malloc(len+1); strncpy(res[i],ptr,len); res[i][len] = '\0'; DPRINT(Debug,25,(&Debug, " : [%i]=\"%s\"\n",i,res[i])); } res[i] = NULL; DPRINT(Debug,25,(&Debug, " : [%i]=NULL\n",i)); return res; } void remove_space_tokenized(tokenized) char ** tokenized; { int ptr, ptr2 = 0; for (ptr = 0; tokenized[ptr]; ptr++) { if ('(' == tokenized[ptr][0] || whitespace(tokenized[ptr][0]) || '\n' == tokenized[ptr][0]) { DPRINT(Debug,25,(&Debug, "remove_space_tokenized: FREE \t<= [%d] = \"%s\"\n", ptr,tokenized[ptr])); free(tokenized[ptr]); tokenized[ptr] = NULL; } else { DPRINT(Debug,25,(&Debug, "remove_space_tokenized: [%d] \t<= [%d] = \"%s\"\n", ptr2,ptr,tokenized[ptr])); tokenized[ptr2] = tokenized[ptr]; if (ptr2 != ptr) tokenized[ptr] = NULL; ptr2++; } } /* Check: */ while (ptr2 <= ptr) { if (!tokenized[ptr2]) { DPRINT(Debug,25,(&Debug, "remove_space_tokenized: [%d] \t<= NULL\n", ptr2)); } else { DPRINT(Debug,25,(&Debug, "remove_space_tokenized: [%d] \t== \"%s\" ERROR!\n", ptr2,tokenized[ptr2])); } ptr2++; } } void free_rfc822tokenized(res) char **res; { int i; for (i = 0; res[i]; i++) { DPRINT(Debug,100,(&Debug, "free_rfc822tokenized: free(res[%d]=\"%s\")\n", i,res[i])); free(res[i]); } DPRINT(Debug,100,(&Debug, "free_rfc822tokenized: free(res)\n")); free(res); } static CONST unsigned char * csUs P_((const char *str)); static CONST unsigned char * csUs(str) CONST char *str; { return (CONST unsigned char *)str; } int look_special_tokens(tokenized,tok_chars, start,ended,demime,set, comments,scanned) char **tokenized; CONST char *tok_chars; int start; int *ended; int demime; charset_t set; struct string **comments; char ***scanned; { int end; DPRINT(Debug,25,(&Debug, "look_special_tokens: start=%d, tok_chars=%s,*comments=%p\n", start,tok_chars,*comments)); *scanned = NULL; DPRINT(Debug,25,(&Debug, "look_special_tokens: ")); for (end = start; tokenized[end]; end++) { if (NULL != strchr(tok_chars,tokenized[end][0]) && '\0' == tokenized[end][1]) break; DPRINT(Debug,25,(&Debug," [%d]=%s",end,tokenized[end])); } DPRINT(Debug,25,(&Debug,"\n")); if (!tokenized[end]) { DPRINT(Debug,25,(&Debug,"look_special_tokens: end=%d NO MATCH\n",end)); } else { DPRINT(Debug,25,(&Debug,"look_special_tokens: end=%d MATCH=%s\n", end,tokenized[end])); } if (end > start) { int count = end-start; int i, in_ptr,out_ptr; char **res; DPRINT(Debug,26,(&Debug,"look_special_tokens: count=%d\n", count)); res = safe_malloc((count+1) * sizeof (char *)); for (i = 0; i <= count; i++) res[i] = NULL; for (in_ptr=start, out_ptr=0; in_ptr < end && out_ptr < count; in_ptr++) { if (whitespace(tokenized[in_ptr][0])) { if (*comments && string_len(*comments) && in_ptr < end-1) add_ascii_to_string(*comments,csUs(tokenized[in_ptr])); } else if ('(' == tokenized[in_ptr][0]) { struct string * tmp; char buffer[1000]; int k, j = 0; for (k = 1; tokenized[in_ptr][k] && (tokenized[in_ptr][k] != ')' || tokenized[in_ptr][k+1]) && j < sizeof buffer -1; k++) buffer[j++] = tokenized[in_ptr][k]; buffer[j] = '\0'; tmp = hdr_to_string(HDR_COMMENT,buffer,set,demime); if (!*comments) *comments = tmp; else { struct string *new_str = cat_strings(*comments,tmp,1); free_string(comments); *comments = new_str; free_string(&tmp); } } else res[out_ptr++] = safe_strdup(tokenized[in_ptr]); } DPRINT(Debug,26,(&Debug,"look_special_tokens: real count=%d, scanned:", out_ptr)); for (i = 0; i < out_ptr; i++) { DPRINT(Debug,26,(&Debug," [%d]=%s",i,res[i])); } *scanned=res; DPRINT(Debug,26,(&Debug, "\nlook_special_tokens: *scanned=%p, *comments=%S\n", *scanned,*comments)); } if (ended) *ended = end; DPRINT(Debug,25,(&Debug, "look_special_tokens=%d\n",end-start)); return end-start; } struct string * scanned_to_phrase P_((char **scanned, int demime, charset_t set)); struct string *scanned_to_phrase(scanned,demime,set) char * *scanned; int demime; charset_t set; { struct string * res = NULL; char * buffer = NULL; int idx, max_idx; CONST char *A,*B; DPRINT(Debug,25,(&Debug, "scanned_to_phrase: demime=%d\n",demime)); DPRINT(Debug,25,(&Debug, "scanned_to_phrase:")); for (max_idx = 0; scanned[max_idx]; max_idx++) { DPRINT(Debug,25,(&Debug, " [%d]=%s",max_idx,scanned[max_idx])); } DPRINT(Debug,25,(&Debug, "\n")); for (idx = 0; idx < max_idx; idx++) { if (buffer) buffer = strmcat(buffer," "); buffer = strmcat(buffer,scanned[idx]); } if (buffer) { res = hdr_to_string(HDR_PHRASE,buffer,set,demime); free(buffer); } else res = new_string(set); DPRINT(Debug,25,(&Debug, "scanned_to_phrase=%S\n",res)); A = get_string_MIME_name(res); B = get_string_lang(res); DPRINT(Debug,25,(&Debug, " cs=%s lang=%s\n", A ? A : "", B ? B : "")); return res; } static char *scanned_to_str P_((char **scanned)); static char *scanned_to_str(scanned) char **scanned; { int idx; char * res = safe_strdup(""); DPRINT(Debug,25,(&Debug, "scanned_to_str:")); for (idx = 0; scanned[idx]; idx++) { DPRINT(Debug,25,(&Debug, " [%d]=%s",idx,scanned[idx])); res = strmcat(res,scanned[idx]); } DPRINT(Debug,25,(&Debug, "\nscanned_to_str=%p=%s\n",res,res)); return res; } struct addr_item * break_down_address (buffer, demime, defcharset) CONST char *buffer; int demime; charset_t defcharset; { int count=1; int i,res_idx = 0; struct addr_item *res; int outer, inner; int tok_end; struct { int in_group; int in_bracket; } state; char **tokenized = rfc822_tokenize(buffer); DPRINT(Debug,11,(&Debug, "break_down_address: buffer=%.100s\n", buffer)); if (!tokenized) return NULL; for (tok_end = 0; tokenized[tok_end]; tok_end++) if (tokenized[tok_end][0] == ',') count++; DPRINT(Debug,11,(&Debug, "break_down_address: count=%d, tok_end=%d\n", count,tok_end)); res = safe_malloc((count+1) * sizeof (struct addr_item)); for (i =0; i <= count; i++) { res[i].addr = NULL; res[i].fullname = NULL; res[i].comment = NULL; } state.in_group = 0; state.in_bracket = 0; for (outer = 0; outer < tok_end; outer = inner) { char **scanned = NULL; struct string *comments = NULL; char tok = '\0'; DPRINT(Debug,25,(&Debug, "break_down_address: [%d]=%.10s... state: in_group=%d in_bracket=%d\n", outer,tokenized[outer],state.in_group,state.in_bracket)); look_special_tokens(tokenized,":<>,;",outer,&inner,demime, defcharset,&comments,&scanned); if (inner < tok_end) { tok = tokenized[inner][0]; DPRINT(Debug,25,(&Debug, "break_down_address: [%d] token=%c (%s)\n",inner,tok,tokenized[inner])); inner++; } else { tok = '\0'; DPRINT(Debug,25,(&Debug, "break_down_address: [%d] token=EOS\n",inner)); } if (res_idx < count) { /* state engine */ if (!state.in_group) { if (!state.in_bracket) switch(tok) { case ':': DPRINT(Debug,25,(&Debug, "... skipping group phrase\n")); state.in_group = 1; break; case '<': if (scanned && scanned[0]) { struct string *str = scanned_to_phrase(scanned, demime, defcharset); DPRINT(Debug,25,(&Debug, "... storing address phrase\n")); if (res[res_idx].fullname) { struct string *new_str = cat_strings(res[res_idx].fullname, str,1); free_string(&res[res_idx].fullname); res[res_idx].fullname = new_str; free_string(&str); } else res[res_idx].fullname = str; } state.in_bracket = 1; break; case '>': DPRINT(Debug,25,(&Debug, "... Parse error, next token '>'\n")); break; case ';': DPRINT(Debug,25,(&Debug, "... Parse error, next token ';'\n")); break; case ',': default: /* \0 */ if (scanned && scanned[0]) { char *str = scanned_to_str(scanned); DPRINT(Debug,25,(&Debug, "... storing address: %s\n",str)); res[res_idx].addr = strmcat(res[res_idx].addr,str); free(str); if (comments && convert_comment && !res[res_idx].fullname) { DPRINT(Debug,25,(&Debug, "... storing comments (as fullname)\n")); res[res_idx].fullname = comments; comments = NULL; } else if (comments) { DPRINT(Debug,25,(&Debug, "... storing comments")); if (!res[res_idx].comment) res[res_idx].comment = comments; else { struct string *new_str = cat_strings(res[res_idx].comment, comments,1); free_string(&res[res_idx].comment); res[res_idx].comment = new_str; free_string(&comments); } comments = NULL; } } if (res[res_idx].addr || res[res_idx].fullname || res[res_idx].comment) { CONST char *A,*B; res[res_idx].addr = strmcat(res[res_idx].addr,""); if (!res[res_idx].fullname) res[res_idx].fullname = new_string(defcharset); if (!res[res_idx].comment) res[res_idx].comment = new_string(defcharset); DPRINT(Debug,11,(&Debug, "break_down_address: [%d].addr =%.100s\n", res_idx,res[res_idx].addr)); DPRINT(Debug,11,(&Debug, " .fullname=%.100S\n", res[res_idx].fullname)); A = get_string_MIME_name(res[res_idx].fullname); B = get_string_lang(res[res_idx].fullname); DPRINT(Debug,11,(&Debug, " cs=%s lang=%s\n", A ? A : "", B ? B : "")); DPRINT(Debug,11,(&Debug, " .comment =%.100S\n", res[res_idx].comment)); A = get_string_MIME_name(res[res_idx].comment); B = get_string_lang(res[res_idx].comment); DPRINT(Debug,11,(&Debug, " cs=%s lang=%s\n", A ? A : "", B ? B : "")); res_idx++; } break; } else switch(tok) { /* state.in_bracket */ case ':': DPRINT(Debug,25,(&Debug, "... skipping route\n")); break; case '<': DPRINT(Debug,25,(&Debug, "... Parse error, next token '<'\n")); break; case '>': store_addr1: if (scanned && scanned[0]) { char *str = scanned_to_str(scanned); DPRINT(Debug,25,(&Debug, "... storing address: %s\n",str)); res[res_idx].addr = strmcat(res[res_idx].addr,str); free(str); } if (res[res_idx].addr || res[res_idx].fullname || res[res_idx].comment) { CONST char *A, *B; res[res_idx].addr = strmcat(res[res_idx].addr,""); if (!res[res_idx].fullname) res[res_idx].fullname = new_string(defcharset); if (!res[res_idx].comment) res[res_idx].comment = new_string(defcharset); DPRINT(Debug,11,(&Debug, "break_down_address: [%d].addr =%.100s\n", res_idx,res[res_idx].addr)); DPRINT(Debug,11,(&Debug, " .fullname=%.100S\n", res[res_idx].fullname)); A = get_string_MIME_name(res[res_idx].fullname); B = get_string_lang(res[res_idx].fullname); DPRINT(Debug,11,(&Debug, " cs=%s lang=%s\n", A ? A : "", B ? B : "")); DPRINT(Debug,11,(&Debug, " .comment =%.100S\n", res[res_idx].comment)); A = get_string_MIME_name(res[res_idx].comment); B = get_string_lang(res[res_idx].comment); DPRINT(Debug,11,(&Debug, " cs=%s lang=%s\n", A ? A : "", B ? B : "")); res_idx++; } state.in_bracket = 0; break; case ',': DPRINT(Debug,25,(&Debug, "... skipping route\n")); break; case ';': DPRINT(Debug,25,(&Debug, "... Parse error, next token ';'\n")); break; default: /* \0 */ DPRINT(Debug,25,(&Debug, "... Parse error, missing '>'\n")); goto store_addr1; } } else { /* state.in_group */ if (!state.in_bracket) switch(tok) { case ':': DPRINT(Debug,25,(&Debug, "... Parse error, next token ':'\n")); break; case '<': if (scanned && scanned[0]) { struct string *str = scanned_to_phrase(scanned, demime, defcharset); DPRINT(Debug,25,(&Debug, "... storing address phrase\n")); if (res[res_idx].fullname) { struct string *new_str = cat_strings(res[res_idx].fullname, str,1); free_string(&res[res_idx].fullname); res[res_idx].fullname = new_str; free_string(&str); } else res[res_idx].fullname = str; } state.in_bracket = 1; break; case '>': DPRINT(Debug,25,(&Debug, "... Parse error, next token '>'\n")); break; case ';': store_addr2: state.in_group = 0; /* FALLTHRU */ case ',': if (scanned && scanned[0]) { char *str = scanned_to_str(scanned); DPRINT(Debug,25,(&Debug, "... storing address: %s\n",str)); res[res_idx].addr = strmcat(res[res_idx].addr,str); free(str); if (comments && convert_comment && !res[res_idx].fullname) { DPRINT(Debug,25,(&Debug, "... storing comments (as fullname)\n")); res[res_idx].fullname = comments; comments = NULL; } else if (comments) { DPRINT(Debug,25,(&Debug, "... storing comments\n")); if (!res[res_idx].comment) res[res_idx].comment = comments; else { struct string *new_str = cat_strings(res[res_idx].comment, comments,1); free_string(&res[res_idx].comment); res[res_idx].comment = new_str; free_string(&comments); } comments = NULL; } } if (res[res_idx].addr || res[res_idx].fullname || res[res_idx].comment) { CONST char *A,*B; res[res_idx].addr = strmcat(res[res_idx].addr,""); if (!res[res_idx].fullname) res[res_idx].fullname = new_string(defcharset); if (!res[res_idx].comment) res[res_idx].comment = new_string(defcharset); DPRINT(Debug,11,(&Debug, "break_down_address: [%d].addr =%.100s\n", res_idx,res[res_idx].addr)); DPRINT(Debug,11,(&Debug, " .fullname=%.100S\n", res[res_idx].fullname)); A = get_string_MIME_name(res[res_idx].fullname); B = get_string_lang(res[res_idx].fullname); DPRINT(Debug,11,(&Debug, " cs=%s lang=%s\n", A ? A : "", B ? B : "")); DPRINT(Debug,11,(&Debug, " .comment =%.100S\n", res[res_idx].comment)); A = get_string_MIME_name(res[res_idx].comment); B = get_string_lang(res[res_idx].comment); DPRINT(Debug,11,(&Debug, " cs=%s lang=%s\n", A ? A : "", B ? B : "")); res_idx++; } break; default: /* \0 */ DPRINT(Debug,25,(&Debug, "... Parse error, missing ';'\n")); goto store_addr2; } else switch(tok) { /* state.in_bracket */ case ':': DPRINT(Debug,25,(&Debug, "... skipping route\n")); break; case '<': DPRINT(Debug,25,(&Debug, "... Parse error, next token '<'\n")); break; case ';': DPRINT(Debug,25,(&Debug, "... Parse error, next token ';', missing '>'\n")); state.in_group = 0; /* FALLTHRU */ case '>': store_addr3: if (scanned && scanned[0]) { char *str = scanned_to_str(scanned); DPRINT(Debug,25,(&Debug, "... storing address: %s\n",str)); res[res_idx].addr = strmcat(res[res_idx].addr,str); free(str); } if (res[res_idx].addr || res[res_idx].fullname || res[res_idx].comment) { CONST char *A, *B; res[res_idx].addr = strmcat(res[res_idx].addr,""); if (!res[res_idx].fullname) res[res_idx].fullname = new_string(defcharset); if (!res[res_idx].comment) res[res_idx].comment = new_string(defcharset); DPRINT(Debug,11,(&Debug, "break_down_address: [%d].addr =%.100s\n", res_idx,res[res_idx].addr)); DPRINT(Debug,11,(&Debug, " .fullname=%.100S\n", res[res_idx].fullname)); A = get_string_MIME_name(res[res_idx].fullname); B = get_string_lang(res[res_idx].fullname); DPRINT(Debug,11,(&Debug, " cs=%s lang=%s\n", A ? A : "", B ? B : "")); DPRINT(Debug,11,(&Debug, " .comment =%.100S\n", res[res_idx].comment)); A = get_string_MIME_name(res[res_idx].comment); B = get_string_lang(res[res_idx].comment); DPRINT(Debug,11,(&Debug, " cs=%s lang=%s\n", A ? A : "", B ? B : "")); res_idx++; } state.in_bracket = 0; break; case ',': DPRINT(Debug,25,(&Debug, "... skipping route\n")); break; default: /* \0 */ DPRINT(Debug,25,(&Debug, "... Parse error, missing '>', missing ';'\n")); state.in_group = 0; goto store_addr3; } } } else { if (scanned && scanned[0]) { DPRINT(Debug,11,(&Debug, "break_down_address: OVERFLOW (already %d addresses)\n", count)); } } /* do not convert here comment to fullname even when convert_comment * is set! */ if (comments && res_idx > 0 && !string_len(res[res_idx-1].comment) && /* Don't store comment it is already stored as fullname * because of convert_comment is set! */ 0 != string_cmp(res[res_idx-1].fullname,comments, 999 /* == Values not comparable */ )) { struct string *new_str = cat_strings(res[res_idx-1].comment, comments,1); DPRINT(Debug,25,(&Debug, "... storing comments to previous address\n")); free_string(&res[res_idx-1].comment); res[res_idx-1].comment = new_str; } if (comments) free_string(&comments); if (scanned) free_rfc822tokenized(scanned); } free_rfc822tokenized(tokenized); DPRINT(Debug,11,(&Debug, "break_down_address=%08X (real count=%d)\n", res,res_idx)); return res; } void free_addr_items (list) struct addr_item *list; { struct addr_item *ptr; DPRINT(Debug,100,(&Debug, "free_addr_items(%08X)\n",list)); for (ptr=list; ptr->addr && ptr->fullname; ptr++) { free(ptr->addr); ptr->addr = NULL; free_string(& (ptr->fullname)); free_string (& (ptr->comment)); } free(list); } void append_addr_items (list, list1) struct addr_item **list, *list1; { int count = 0; int n = 0; struct addr_item *ptr; struct addr_item *ret = *list; if (ret) { for (ptr=ret; ptr->addr && ptr->fullname; ptr++) n++; count = n; } for (ptr=list1; ptr->addr && ptr->fullname; ptr++) count++; ret = safe_realloc(ret, (count + 1) * sizeof (*ret)); for (ptr=list1; ptr->addr && ptr->fullname && n < count; ptr++) { ret[n].addr = safe_strdup(ptr->addr); ret[n].fullname = dup_string (ptr->fullname); ret[n].comment = NULL; if (ptr->comment) ret[n].comment = dup_string(ptr->comment); n++; } ret[n].addr = NULL; ret[n].fullname = NULL; ret[n].comment = NULL; *list = ret; } /* * Local Variables: * mode:c * c-basic-offset:4 * buffer-file-coding-system: iso-8859-1 * End: */