/* ** Copyright 1998 - 1999 Double Precision, Inc. ** See COPYING for distribution information. */ /* ** $Id$ */ #include #include #include #include #include "rfc822.h" static void tokenize(const char *p, struct rfc822token *tokp, int *toklen, void (*err_func)(const char *, int)) { const char *addr=p; int i=0; int inbracket=0; *toklen=0; while (*p) { if (isspace((int)(unsigned char)*p)) { p++; i++; continue; } switch (*p) { int level; case '(': if (tokp) { tokp->token='('; tokp->ptr=p; tokp->len=0; } level=0; for (;;) { if (!*p) { if (err_func) (*err_func)(addr, i); if (tokp) tokp->token='"'; ++*toklen; return; } if (*p == '(') ++level; if (*p == ')' && --level == 0) { p++; i++; if (tokp) tokp->len++; break; } if (*p == '\\' && p[1]) { p++; i++; if (tokp) tokp->len++; } i++; if (tokp) tokp->len++; p++; } if (tokp) ++tokp; ++*toklen; continue; case '"': p++; i++; if (tokp) { tokp->token='"'; tokp->ptr=p; } while (*p != '"') { if (!*p) { if (err_func) (*err_func)(addr, i); ++*toklen; return; } if (*p == '\\' && p[1]) { if (tokp) tokp->len++; p++; i++; } if (tokp) tokp->len++; p++; i++; } ++*toklen; if (tokp) ++tokp; p++; i++; continue; case '\\': case ')': if (err_func) (*err_func)(addr, i); ++p; ++i; continue; case '<': case '>': case '@': case ',': case ';': case ':': case '.': case '[': case ']': case '%': case '!': case '?': case '=': case '/': if ( (*p == '<' && inbracket) || (*p == '>' && !inbracket)) { if (err_func) (*err_func)(addr, i); ++p; ++i; continue; } if (*p == '<') inbracket=1; if (*p == '>') inbracket=0; if (tokp) { tokp->token= *p; tokp->ptr=p; tokp->len=1; ++tokp; } ++*toklen; if (*p == '<' && p[1] == '>') /* Fake a null address */ { if (tokp) { tokp->token=0; tokp->ptr=""; tokp->len=0; ++tokp; } ++*toklen; } ++p; ++i; continue; default: if (tokp) { tokp->token=0; tokp->ptr=p; tokp->len=0; } while (*p && !isspace((int)(unsigned char)*p) && strchr( "<>@,;:.[]()%!\"\\?=/", *p) == 0) { if (tokp) ++tokp->len; ++p; ++i; } if (i == 0) /* Idiot check */ { if (err_func) (*err_func)(addr, i); if (tokp) { tokp->token='"'; tokp->ptr=p; tokp->len=1; ++tokp; } ++*toklen; ++p; ++i; continue; } if (tokp) ++tokp; ++*toklen; } } } static void parseaddr(struct rfc822token *tokens, int ntokens, struct rfc822addr *addrs, int *naddrs) { int flag, j, k; struct rfc822token save_token; *naddrs=0; while (ntokens) { int i; /* atoms (token=0) or quoted strings, followed by a : token is a list name. */ for (i=0; itokens=0; addrs->name=i ? tokens:0; for (j=1; jname[j-1].next=addrs->name+j; if (i) addrs->name[i-1].next=0; addrs++; } ++*naddrs; tokens += i; ntokens -= i; continue; /* Group=phrase ":" */ } /* Spurious commas are skipped, ;s are recorded */ if (tokens->token == ',' || tokens->token == ';') { if (tokens->token == ';') { if (addrs) { addrs->tokens=0; addrs->name=tokens; addrs->name->next=0; addrs++; } ++*naddrs; } ++tokens; --ntokens; continue; } /* If we can find a '<' before the next comma or semicolon, we have new style RFC path address */ for (i=0; i'??? If it consists exclusively of atoms, leave them alone. Else, make them all a quoted string. */ for (j=0; jname= i ? tokens:0; for (k=1; kname[k-1].next=addrs->name+k; if (i) addrs->name[i-1].next=0; } } else /* Intentionally corrupt the original toks */ { if (addrs) { tokens->len= tokens[i-1].ptr + tokens[i-1].len - tokens->ptr; /* We know that all the ptrs point to parts of the same string. */ tokens->token='"'; /* Quoted string. */ addrs->name=tokens; addrs->name->next=0; } } /* Any comments in the name part are changed to quotes */ if (addrs) { struct rfc822token *t; for (t=addrs->name; t; t=t->next) if (t->token == '(') t->token='"'; } /* Now that's done and over with, see what can be done with the <...> part. */ ++i; tokens += i; ntokens -= i; for (i=0; itokens=i ? tokens:0; for (k=1; ktokens[k-1].next=addrs->tokens+k; if (i) addrs->tokens[i-1].next=0; ++addrs; } ++*naddrs; tokens += i; ntokens -= i; if (ntokens) /* Skip the '>' token */ { --ntokens; ++tokens; } continue; } /* Ok - old style address. Assume the worst */ /* Try to figure out where the address ends. It ends upon: a comma, semicolon, or two consecutive atoms. */ flag=0; for (i=0; iname=0; } /* Ok, now get rid of embedded comments in the address. Consider the last comment to be the real name */ if (addrs) { save_token.ptr=0; save_token.len=0; for (j=k=0; jname=tokens+i-1; addrs->name->next=0; } addrs->tokens=k ? tokens:NULL; for (j=1; jtokens[j-1].next=addrs->tokens+j; if (k) addrs->tokens[k-1].next=0; ++addrs; } ++*naddrs; tokens += i; ntokens -= i; } } static void print_token(const struct rfc822token *token, void (*print_func)(char, void *), void *ptr) { const char *p; int n; if (token->token == 0 || token->token == '(') { for (n=token->len, p=token->ptr; n; --n, ++p) (*print_func)(*p, ptr); return; } if (token->token != '"') { (*print_func)(token->token, ptr); return; } (*print_func)('"', ptr); n=token->len; p=token->ptr; while (n) { if (*p == '"' || (*p == '\\' && n == 1)) (*print_func)('\\', ptr); if (*p == '\\' && n > 1) { (*print_func)('\\', ptr); ++p; --n; } (*print_func)(*p++, ptr); --n; } (*print_func)('"', ptr); } void rfc822tok_print(const struct rfc822token *token, void (*print_func)(char, void *), void *ptr) { int prev_isatom=0; int isatom; while (token) { isatom=rfc822_is_atom(token->token); if (prev_isatom && isatom) (*print_func)(' ', ptr); print_token(token, print_func, ptr); prev_isatom=isatom; token=token->next; } } void rfc822_print(const struct rfc822a *rfcp, void (*print_func)(char, void *), void (*print_separator)(const char *s, void *), void *ptr) { rfc822_print_common(rfcp, 0, 0, print_func, print_separator, ptr); } void rfc822_print_common(const struct rfc822a *rfcp, char *(*decode_func)(const char *, const char *), const char *chset, void (*print_func)(char, void *), void (*print_separator)(const char *, void *), void *ptr) { const struct rfc822addr *addrs=rfcp->addrs; int naddrs=rfcp->naddrs; while (naddrs) { if (addrs->tokens == 0) { rfc822tok_print(addrs->name, print_func, ptr); ++addrs; --naddrs; if (addrs[-1].name && naddrs) { struct rfc822token *t; for (t=addrs[-1].name; t && t->next; t=t->next) ; if (t && (t->token == ':' || t->token == ';')) (*print_separator)(" ", ptr); } continue; } else if (addrs->name && addrs->name->token == '(') { /* old style */ char *p; rfc822tok_print(addrs->tokens, print_func, ptr); (*print_func)(' ', ptr); if (decode_func && (p=rfc822_gettok(addrs->name))!=0) { char *q= (*decode_func)(p, chset); char *r; for (r=q; r && *r; r++) (*print_func)( (int)(unsigned char)*r, ptr); if (q) free(q); free(p); } else rfc822tok_print(addrs->name, print_func, ptr); } else { int print_braces=0; char *p; if (addrs->name) { if (decode_func && (p=rfc822_gettok(addrs->name)) != 0) { char *q= (*decode_func)(p, chset); char *r; for (r=q; r && *r; r++) (*print_func)( (int)(unsigned char)*r, ptr); if (q) free(q); free(p); } else rfc822tok_print(addrs->name, print_func, ptr); (*print_func)(' ', ptr); print_braces=1; } else { struct rfc822token *p; for (p=addrs->tokens; p && p->next; p=p->next) if (rfc822_is_atom(p->token) && rfc822_is_atom(p->next->token)) print_braces=1; } if (print_braces) (*print_func)('<', ptr); rfc822tok_print(addrs->tokens, print_func, ptr); if (print_braces) { (*print_func)('>', ptr); } } ++addrs; --naddrs; if (naddrs) if (addrs->tokens || (addrs->name && rfc822_is_atom(addrs->name->token))) (*print_separator)(", ", ptr); } } void rfc822t_free(struct rfc822t *p) { if (p->tokens) free(p->tokens); free(p); } void rfc822a_free(struct rfc822a *p) { if (p->addrs) free(p->addrs); free(p); } void rfc822_deladdr(struct rfc822a *rfcp, int index) { int i; if (index < 0 || index >= rfcp->naddrs) return; for (i=index+1; inaddrs; i++) rfcp->addrs[i-1]=rfcp->addrs[i]; if (--rfcp->naddrs == 0) { free(rfcp->addrs); rfcp->addrs=0; } } struct rfc822t *rfc822t_alloc(const char *addr, void (*err_func)(const char *, int)) { struct rfc822t *p=(struct rfc822t *)malloc(sizeof(struct rfc822t)); if (!p) return (NULL); memset(p, 0, sizeof(*p)); tokenize(addr, NULL, &p->ntokens, err_func); p->tokens=p->ntokens ? (struct rfc822token *) calloc(p->ntokens, sizeof(struct rfc822token)):0; if (p->ntokens && !p->tokens) { rfc822t_free(p); return (NULL); } tokenize(addr, p->tokens, &p->ntokens, NULL); return (p); } struct rfc822a *rfc822a_alloc(struct rfc822t *t) { struct rfc822a *p=(struct rfc822a *)malloc(sizeof(struct rfc822a)); if (!p) return (NULL); memset(p, 0, sizeof(*p)); parseaddr(t->tokens, t->ntokens, NULL, &p->naddrs); p->addrs=p->naddrs ? (struct rfc822addr *) calloc(p->naddrs, sizeof(struct rfc822addr)):0; if (p->naddrs && !p->addrs) { rfc822a_free(p); return (NULL); } parseaddr(t->tokens, t->ntokens, p->addrs, &p->naddrs); return (p); } void rfc822_praddr(const struct rfc822a *rfcp, int index, void (*print_func)(char, void *), void *ptr) { const struct rfc822addr *addrs; if (index < 0 || index >= rfcp->naddrs) return; addrs=rfcp->addrs+index; if (addrs->tokens) { rfc822tok_print(addrs->tokens, print_func, ptr); (*print_func)('\n', ptr); } } void rfc822_addrlist(const struct rfc822a *rfcp, void (*print_func)(char, void *), void *ptr) { int i; for (i=0; inaddrs; i++) rfc822_praddr(rfcp, i, print_func, ptr); } void rfc822_prname(const struct rfc822a *rfcp, int index, void (*print_func)(char, void *), void *ptr) { const struct rfc822addr *addrs; if (index < 0 || index >= rfcp->naddrs) return; addrs=rfcp->addrs+index; if (!addrs->tokens) return; rfc822_prname_orlist(rfcp, index, print_func, ptr); } void rfc822_prname_orlist(const struct rfc822a *rfcp, int index, void (*print_func)(char, void *), void *ptr) { const struct rfc822addr *addrs; if (index < 0 || index >= rfcp->naddrs) return; addrs=rfcp->addrs+index; if (addrs->name) { struct rfc822token *i; int n; int prev_isatom=0; int isatom=0; for (i=addrs->name; i; i=i->next, prev_isatom=isatom) { isatom=rfc822_is_atom(i->token); if (isatom && prev_isatom) (*print_func)(' ', ptr); if (i->token != '(') { print_token(i, print_func, ptr); continue; } for (n=2; nlen; n++) (*print_func)(i->ptr[n-1], ptr); } } else rfc822tok_print(addrs->tokens, print_func, ptr); (*print_func)('\n', ptr); } void rfc822_namelist(const struct rfc822a *rfcp, void (*print_func)(char, void *), void *ptr) { int i; for (i=0; inaddrs; i++) rfc822_prname(rfcp, i, print_func, ptr); }