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 <hurtta+elm@posti.FMI.FI> (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 : "<none>", B ? B : "<none>"));
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 : "<none>",
B ? B : "<none>"));
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 : "<none>",
B ? B : "<none>"));
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 : "<none>",
B ? B : "<none>"));
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 : "<none>",
B ? B : "<none>"));
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 : "<none>",
B ? B : "<none>"));
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 : "<none>",
B ? B : "<none>"));
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 : "<none>",
B ? B : "<none>"));
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 : "<none>",
B ? B : "<none>"));
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:
*/
syntax highlighted by Code2HTML, v. 0.9.1