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); +} | 
