diff options
Diffstat (limited to 'rfc822/rfc822.c')
| -rw-r--r-- | rfc822/rfc822.c | 826 |
1 files changed, 826 insertions, 0 deletions
diff --git a/rfc822/rfc822.c b/rfc822/rfc822.c new file mode 100644 index 0000000..c51460d --- /dev/null +++ b/rfc822/rfc822.c @@ -0,0 +1,826 @@ +/* +** Copyright 1998 - 2009 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +/* +*/ +#include "rfc822.h" +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +static void tokenize(const char *p, struct rfc822token *tokp, int *toklen, + void (*err_func)(const char *, int, void *), void *voidp) +{ +const char *addr=p; +int i=0; +int inbracket=0; + + *toklen=0; + while (*p) + { + if (isspace((int)(unsigned char)*p)) + { + p++; + i++; + continue; + } + +#define SPECIALS "<>@,;:.[]()%!\"\\?=/" + + 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, + voidp); + 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, + voidp); + ++*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, voidp); + ++p; + ++i; + continue; + + case '=': + + if (p[1] == '?') + { + int j; + + /* exception: =? ... ?= */ + + for (j=2; p[j]; j++) + { + if (p[j] == '?' && p[j+1] == '=') + break; + + if (p[j] == '?' || p[j] == '=') + continue; + + if (strchr(RFC822_SPECIALS, p[j]) || + isspace(p[j])) + break; + } + + if (p[j] == '?' && p[j+1] == '=') + { + j += 2; + if (tokp) + { + tokp->token=0; + tokp->ptr=p; + tokp->len=j; + ++tokp; + } + ++*toklen; + + p += j; + i += j; + continue; + } + } + /* FALLTHROUGH */ + + 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, voidp); + ++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( + SPECIALS, *p) == 0) + { + if (tokp) ++tokp->len; + ++p; + ++i; + } + if (i == 0) /* Idiot check */ + { + if (err_func) (*err_func)(addr, i, voidp); + 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; + + *naddrs=0; + + while (ntokens) + { + int i; + + /* atoms (token=0) or quoted strings, followed by a : token + is a list name. */ + + for (i=0; i<ntokens; i++) + if (tokens[i].token && tokens[i].token != '"') + break; + if (i < ntokens && tokens[i].token == ':') + { + ++i; + if (addrs) + { + addrs->tokens=0; + addrs->name=i ? tokens:0; + for (j=1; j<i; j++) + addrs->name[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<ntokens && tokens[i].token != ';' && + tokens[i].token != ',' && + tokens[i].token != '<'; i++) + ; + + if (i < ntokens && tokens[i].token == '<') + { + int j; + + /* Ok -- what to do with the stuff before '>'??? + If it consists exclusively of atoms, leave them alone. + Else, make them all a quoted string. */ + + for (j=0; j<i && (tokens[j].token == 0 || + tokens[j].token == '('); j++) + ; + + if (j == i) + { + if (addrs) + { + addrs->name= i ? tokens:0; + for (k=1; k<i; k++) + addrs->name[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; i<ntokens && tokens[i].token != '>'; i++) + ; + if (addrs) + { + addrs->tokens=i ? tokens:0; + for (k=1; k<i; k++) + addrs->tokens[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; i<ntokens && tokens[i].token != ',' && + tokens[i].token != ';'; i++) + { + if (tokens[i].token == '(') continue; + /* Ignore comments */ + if (tokens[i].token == 0 || tokens[i].token == '"') + /* Atom */ + { + if (flag) break; + flag=1; + } + else flag=0; + } + if (i == 0) /* Must be spurious comma, or something */ + { + ++tokens; + --ntokens; + continue; + } + + if (addrs) + { + addrs->name=0; + } + + /* Ok, now get rid of embedded comments in the address. + Consider the last comment to be the real name */ + + if (addrs) + { + struct rfc822token save_token; + + memset(&save_token, 0, sizeof(save_token)); + + for (j=k=0; j<i; j++) + { + if (tokens[j].token == '(') + { + save_token=tokens[j]; + continue; + } + tokens[k]=tokens[j]; + k++; + } + + if (save_token.ptr) + { + tokens[i-1]=save_token; + addrs->name=tokens+i-1; + addrs->name->next=0; + } + addrs->tokens=k ? tokens:NULL; + for (j=1; j<k; j++) + addrs->tokens[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; + } +} + +static void rfc822_prname_int(const struct rfc822addr *addrs, + void (*print_func)(char, void *), + void *ptr) + +{ + 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 == '"') + { + for (n=0; n<i->len; n++) + { + if (i->ptr[n] == '\\' && + n + 1 < i->len) + ++n; + (*print_func)(i->ptr[n], ptr); + } + continue; + } + + if (i->token != '(') + { + print_token(i, print_func, ptr); + continue; + } + + for (n=2; n<i->len; n++) + (*print_func)(i->ptr[n-1], ptr); + } +} + +static void rfc822_print_common_nameaddr_cntlen(char c, void *p) +{ + ++ *(size_t *)p; +} + +static void rfc822_print_common_nameaddr_saveaddr(char c, void *p) +{ + char **cp=(char **)p; + + *(*cp)++=c; +} + +static int rfc822_print_common_nameaddr(const struct rfc822addr *addrs, + char *(*decode_func)(const char *, + const char *, int), + const char *chset, + void (*print_func)(char, void *), + void *ptr) +{ + size_t n=1; + char *addrbuf, *namebuf; + char *p, *q; + int print_braces=0; + + if (addrs->tokens) + rfc822tok_print(addrs->tokens, + rfc822_print_common_nameaddr_cntlen, &n); + + + p=addrbuf=malloc(n); + + if (!addrbuf) + return -1; + + if (addrs->tokens) + rfc822tok_print(addrs->tokens, + rfc822_print_common_nameaddr_saveaddr, &p); + + *p=0; + + n=1; + + rfc822_prname_int(addrs, + rfc822_print_common_nameaddr_cntlen, &n); + + p=namebuf=malloc(n); + + if (!p) + { + free(addrbuf); + return -1; + } + + rfc822_prname_int(addrs, + rfc822_print_common_nameaddr_saveaddr, &p); + + *p=0; + + p=(*decode_func)(namebuf, chset, 0); + + free(namebuf); + if (!p) + { + free(addrbuf); + return -1; + } + + for (namebuf=p; *p; p++) + { + print_braces=1; + (*print_func)(*p, ptr); + } + free(namebuf); + + p=(*decode_func)(addrbuf, chset, 1); + free(addrbuf); + + if (!p) + return -1; + + if (print_braces) + (*print_func)(' ', ptr); + + for (q=p; *q; ++q) + if (*q != '.' && *q != '@' && strchr(RFC822_SPECIALS, *q)) + { + print_braces=1; + break; + } + + if (print_braces) + (*print_func)('<', ptr); + + for (addrbuf=p; *p; p++) + (*print_func)(*p, ptr); + + if (print_braces) + (*print_func)('>', ptr); + + free(addrbuf); + return (0); +} + +int rfc822_print(const struct rfc822a *rfcp, void (*print_func)(char, void *), + void (*print_separator)(const char *s, void *), void *ptr) +{ + return rfc822_print_common(rfcp, 0, 0, print_func, print_separator, ptr); +} + +int rfc822_print_common(const struct rfc822a *rfcp, + char *(*decode_func)(const char *, const char *, int), + 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 */ + + if (!decode_func) + { + rfc822tok_print(addrs->tokens, print_func, ptr); + (*print_func)(' ', ptr); + rfc822tok_print(addrs->name, print_func, ptr); + } + else + { + if (rfc822_print_common_nameaddr(addrs, + decode_func, + chset, + print_func, + ptr) < 0) + return -1; + } + } + else + { + if (!decode_func) + { + int print_braces=0; + + if (addrs->name) + { + rfc822tok_print(addrs->name, + print_func, ptr); + (*print_func)(' ', ptr); + print_braces=1; + } +#if 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; + } +#endif + + if (print_braces) + (*print_func)('<', ptr); + + rfc822tok_print(addrs->tokens, print_func, ptr); + + if (print_braces) + (*print_func)('>', ptr); + } + else + { + if (rfc822_print_common_nameaddr(addrs, + decode_func, + chset, + print_func, + ptr) < 0) + return -1; + } + } + ++addrs; + --naddrs; + if (naddrs) + if (addrs->tokens || (addrs->name && + rfc822_is_atom(addrs->name->token))) + (*print_separator)(", ", ptr); + } + return 0; +} + +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; i<rfcp->naddrs; i++) + rfcp->addrs[i-1]=rfcp->addrs[i]; + if (--rfcp->naddrs == 0) + { + free(rfcp->addrs); + rfcp->addrs=0; + } +} + +struct rfc822t *rfc822t_alloc_new(const char *addr, + void (*err_func)(const char *, int, void *), void *voidp) +{ +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, voidp); + 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, 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); +} |
