DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T r

⟦ea92043cf⟧ TextFile

    Length: 27869 (0x6cdd)
    Types: TextFile
    Names: »rfc822norm.c«

Derivation

└─⟦2d1937cfd⟧ Bits:30007241 EUUGD22: P.P 5.0
    └─⟦dc59850a2⟧ »EurOpenD22/pp5.0/pp-5.tar.Z« 
        └─⟦e5a54fb17⟧ 
            └─⟦this⟧ »pp-5.0/Format/rfc822norm/rfc822norm.c« 

TextFile

/* rfc822norm: program to 822norm stdin to stdout */

# ifndef lint
static char Rcsid[] = "@(#)$Header: /cs/research/pp/hubris/pp-beta/Format/rfc822norm/RCS/rfc822norm.c,v 5.0 90/09/20 16:02:35 pp Exp Locker: pp $";
# endif

/*
 * $Header: /cs/research/pp/hubris/pp-beta/Format/rfc822norm/RCS/rfc822norm.c,v 5.0 90/09/20 16:02:35 pp Exp Locker: pp $
 *
 * $Log:	rfc822norm.c,v $
 * Revision 5.0  90/09/20  16:02:35  pp
 * rcsforce : 5.0 public release
 * 
 */



#include	"util.h"
#include	<isode/cmd_srch.h>
#include	"ap.h"
#include	"alias.h"
#include	"chan.h"
#include	"retcode.h"
#include	"adr.h"

#define	OPT_822		1
#define OPT_733		2	
#define	OPT_JNT		3
#define	OPT_BIGEND	4
#define OPT_LITTLEEND	5
#define	OPT_STRIPROUTES	6
#define OPT_STRIPDOMAIN	7
#define	OPT_STRIPTRACE	8
#define OPT_JNTSENDER	9
#define OPT_FOLD	10
#define OPT_MSGID	11
#define OPT_PERCENT	12
#define OPT_HIDELOCAL	13

CMD_TABLE	tbl_options [] = { /* rfc822norm commandline options */
	"-822",		OPT_822,
	"-733",		OPT_733,
	"-jnt",		OPT_JNT,
	"-bigend",	OPT_BIGEND,
	"-littleend",	OPT_LITTLEEND,
	"-striproutes",	OPT_STRIPROUTES,
	"-stripdomain",	OPT_STRIPDOMAIN,
	"-striptrace",	OPT_STRIPTRACE,
	"-jntsender",	OPT_JNTSENDER,
	"-fold",	OPT_FOLD,
	"-msgid",	OPT_MSGID,
	"-percent",	OPT_PERCENT,
	"-hidelocal",	OPT_HIDELOCAL,
	0,		-1
	};

#define	FLD_TO			1
#define	FLD_CC			2
#define	FLD_BCC			3
#define	FLD_FROM		4
#define	FLD_SENDER		5
#define FLD_REPLY_TO		6
#define	FLD_RESENT_FROM		7
#define FLD_RESENT_SENDER	8
#define	FLD_RESENT_TO		9
#define	FLD_RESENT_CC		10
#define	FLD_RESENT_BCC		11
#define	FLD_RESENT_BY		12
#define	FLD_REMAILED_FROM	13
#define FLD_REMAILED_TO		14
#define FLD_REMAILED_BY		15
#define FLD_REDISTRIBUTED_FROM	16
#define FLD_REDISTRIBUTED_TO	17
#define FLD_REDISTRIBUTED_BY	18
#define FLD_ORIG_SENDER		19
/* RFC 987 extensions */
#define FLD_RFC987		20
/* RFC 987 (88) extensions - proposed */
#define FLD_RFC987_88		21

CMD_TABLE	tbl_fields [] = {/* address field names */
/* REAL RFC 822 */
	"To",			FLD_TO,
	"CC",			FLD_CC,
	"bcc",			FLD_BCC,
	"From",			FLD_FROM,
	"Sender",		FLD_SENDER,
	"Reply-to",		FLD_REPLY_TO,
	"Resent-From",		FLD_RESENT_FROM,
	"Resent-Sender",	FLD_RESENT_SENDER,
	"Resent-To",		FLD_RESENT_TO,
	"Resent-Cc",		FLD_RESENT_CC,
	"Resent-Bcc",		FLD_RESENT_BCC,
	"Resent-By",		FLD_RESENT_BY,
/* JNT stuff */
	"Original-Sender",	FLD_ORIG_SENDER,
/* RFC 733 & other misc stuff */
	"Remailed-From",	FLD_REMAILED_FROM,
	"Remailed-To",		FLD_REMAILED_TO,
	"Remailed-By",		FLD_REMAILED_BY,
	"Redistributed-From",	FLD_REDISTRIBUTED_FROM,
	"Redistributed-To",	FLD_REDISTRIBUTED_TO,
	"Redistributed-By",	FLD_REDISTRIBUTED_BY,
/* RFC 987 fields */
	"P1-Recipient",		FLD_RFC987,
/* RFC 987 (88) fields */
	"X400-Originator",			FLD_RFC987_88,
	"X400-Recipients",			FLD_RFC987_88,
	"Notification-IPM-Originator",		FLD_RFC987_88,
	"Notification-Preferred-Recipients",	FLD_RFC987_88,
	"MTS-Originator",			FLD_RFC987_88,
	"MTS-Recipient",			FLD_RFC987_88,
	"Originally-Intended-Recipient",	FLD_RFC987_88,
	"Originator-Return-Address",		FLD_RFC987_88,
	"DL-Expansion-History",			FLD_RFC987_88,
	"Report-Reporting-DL-Name",		FLD_RFC987_88,
	"Report-Originator-and-DL-Expansion-History",
						FLD_RFC987_88,
	0,			-1
	};

#define FOLD_SPACE	0
#define FOLD_COMMA	1
#define FOLD_SEMICOLON	2
#define	FOLD_RECIEVED	3
#define FOLD_NONE	4

CMD_TABLE	tbl_nonfields [] = {/* feilds dont deal with */
	"Received",		FOLD_RECIEVED,
	"X400-Received",	FOLD_SEMICOLON,
	"Via",			FOLD_SEMICOLON,
	"References",		FOLD_COMMA,
	"Keywords",		FOLD_COMMA,
	"X400-MTS-Identifier",	FOLD_NONE,
	"Message-ID",		FOLD_NONE,
	"In-Reply-To",		FOLD_NONE,
	"Obsoletes",		FOLD_COMMA,
	0,			-1
	};

CMD_TABLE	tbl_tracefields [] = {/* fields viable for strip trace */
	"Received",		FOLD_RECIEVED,
	"Via",			FOLD_SEMICOLON,
	"X400-Received",	FOLD_SEMICOLON,
	0,			-1
};

typedef enum {maj_none, rfc822, rfc733, jnt} Major_options;
typedef enum {min_none, bigend, littleend} Minor_options;

extern void sys_init(), err_abrt();
static void norm_sender();
static int equalWithJntsender(), tidy_up();
static getitm(), out_adr();
static AP_ptr parse_and_norm();
char		*myname;
int		nadrs;
int		pcol;
int		nonempty;
int		fold_width;
int		order_pref;
int		striptrace = FALSE,
		striproutes = FALSE;
char		**stripdomains = NULL;
char		**hidedomains = NULL;
int		num_domains = 0,
		num_to_hide = 0,
		message_id = 0,
		msgid_req = FALSE;
char		*jntsender = NULL,
		*jntsendernorm = NULL;
AP_ptr		jnttree, jntgroup, jntname, jntloc, jntdom, jntroute;
static int	getach();
static int	getbufchar();
static char	*next_fold();
static char	*fold_recieved();
static AP_ptr	recur_stripdomain();
extern AP_ptr	ap_pinit();
extern char	*compress();
extern char	*rcmd_srch();
extern int	ap_outtype;
extern int	ap_perlev;
extern char	*loc_dom_site,
		*loc_dom_mta;
extern char	*calloc();
extern char	*realloc();
char		*rloc_dom_site,
		*rloc_dom_mta;
static char	*reverse();
#ifndef BSD42
#define random rand
#define srandom srand
#endif

#define	DEFAULT_FOLD_WIDTH	79

/* ARGSUSED */
main(argc,argv)
int	argc;
char	**argv;
{
	/* parse flags */
	Major_options maj = maj_none;
	Minor_options mino = min_none;

	myname = *argv++;
	sys_init(myname);
/*	malloc_debug(2);*/
	ap_outtype = AP_PARSE_SAME;
	fold_width = DEFAULT_FOLD_WIDTH;
	order_pref = CH_USA_PREF;
	jntsendernorm = NULL;
	jntsender = NULL;
	message_id = 0;
	msgid_req = FALSE;
	srandom(getpid());
	rloc_dom_site = reverse(loc_dom_site);
	rloc_dom_mta = reverse(loc_dom_mta);
	while (*argv != NULL) {
		switch(cmd_srch(*argv,tbl_options)) {
		    case -1:
			PP_LOG(LLOG_EXCEPTIONS,
			       ("unknown option '%s'",*argv));
			exit(1);
			
		    case OPT_MSGID:
			msgid_req = TRUE;
			break;

		    case OPT_PERCENT:
			ap_use_percent();
			ap_norm_all_domains();
			break;
			
		    case OPT_FOLD:
			if (*(argv+1) == NULL)
				PP_LOG(LLOG_EXCEPTIONS,
				       ("no fold width given with %s",*argv));
			else {
				++argv;
				fold_width = atoi(*argv);
			}
			break;

		    case OPT_822:
			if ((maj == maj_none) || (maj == rfc822)) {
				ap_outtype |= AP_PARSE_822;
				maj = rfc822;
			}
			break;

		    case OPT_733:
			if ((maj == maj_none) || (maj == rfc733)) {
				ap_outtype |= AP_PARSE_733;
				maj = rfc733;
			} 
			break;

		    case OPT_JNT:
			if ((maj == maj_none || maj == jnt)
			    && (mino == min_none || mino == bigend)) {
				ap_outtype |= AP_PARSE_733;
				maj = jnt;
				ap_outtype |= AP_PARSE_BIG;
				mino = bigend;
				order_pref = CH_UK_PREF;
				break;
			}
			PP_LOG(LLOG_EXCEPTIONS,
			       ("multiple major parse options"));
			exit(1);

		    case OPT_BIGEND:
			if (mino == min_none || mino == bigend) {
				ap_outtype |= AP_PARSE_BIG;
				mino = bigend;
				order_pref = CH_UK_PREF;
			}
			break;

		    case OPT_LITTLEEND:
			if (mino == min_none || mino == littleend) {
				mino = littleend;
				break;
			}
			PP_LOG(LLOG_EXCEPTIONS,
			       ("multiple minor parse options"));
			exit(1);

		    case OPT_STRIPTRACE:
			striptrace = TRUE;
			break;

		    case OPT_STRIPROUTES:
			striproutes = TRUE;
			break;

		    case OPT_STRIPDOMAIN:
			if (*(argv+1) == NULL)
				PP_LOG(LLOG_EXCEPTIONS,
				       ("no domain specified with %s", *argv));
			else {
				++argv;
				if (num_domains == 0)
					stripdomains = (char **) calloc(1, 
						(unsigned int) sizeof(char *));
				else
					stripdomains = (char **) realloc((char *) stripdomains,
									 (unsigned int) ((num_domains + 1) * sizeof(char *)));
				stripdomains[num_domains++] = *argv;
			}
			break;

		    case OPT_JNTSENDER:
			if (*(argv+1) == NULL)
				PP_LOG(LLOG_EXCEPTIONS,
				       ("no sender specified with %s", *argv));
			else {
				argv++;
				jntsender = *argv;
			}
			break;

		    case OPT_HIDELOCAL:
			if (*(argv+1) == NULL)
				PP_LOG(LLOG_EXCEPTIONS,
				       ("no domain specified with %s", *argv));
			else {
				++argv;
				if (num_to_hide == 0)
					hidedomains = (char **) calloc(1, 
						(unsigned int) sizeof(char *));
				else
					hidedomains = (char **) realloc((char *) hidedomains,
									 (unsigned int) ((num_to_hide + 1) * sizeof(char *)));
				hidedomains[num_to_hide++] = *argv;
			}
			break;

		    default:
			PP_LOG(LLOG_EXCEPTIONS,
			       ("unknown option '%s'"));
			exit(1);
		}
		argv++;
	}

	if (jntsender)
		norm_sender();
	/* ap_outtype set so now process */
	if (proc() != OK)
		err_abrt( RP_LIO, "couldn't reformat it");
	free(rloc_dom_site);
	free(rloc_dom_mta);
	/* copy out rest of file */
	exit(0);
}

/* \f

 */

static void	norm_sender()
{
	jnttree = ap_s2t(jntsender);

	jnttree = parse_and_norm (jnttree, &jntgroup, &jntname,
				  &jntloc, &jntdom, &jntroute);
	jntsendernorm = ap_p2s(jntgroup, jntname, jntloc, jntdom, jntroute);
}

static int equalWithJntsender(tree)
AP_ptr	tree;
{
	AP_ptr	jix = tree, aix = jntloc;

	while (jix != NULLAP && 
	       aix != NULLAP) {
		if (jix -> ap_obtype == AP_COMMENT)
			jix = jix -> ap_next;
		else if (aix -> ap_obtype == AP_COMMENT)
			aix = aix -> ap_next;
		else if (strcmp(aix -> ap_obvalue, jix -> ap_obvalue) != 0)
			return 0;
		else {
			aix = aix -> ap_next;
			jix = jix -> ap_next;
		}
	}
	while (jix != NULLAP && jix->ap_obvalue == NULLCP)
		jix = jix -> ap_next;

	while (aix != NULLAP && aix->ap_obvalue == NULLCP)
		aix = aix -> ap_next;

	if (aix == NULLAP && jix == NULLAP)
		return 1;
	return 0;
}

/* \f

 */

int	noFrom, from;

/* 822norm stdin to stdout */
proc ()
{
	int	amp_fail;
	int	res;
	int	done;
	register char	*cp;
	PP_TRACE(("outtype %o",ap_outtype));
	noFrom = 1;
	from = 0;
	while ((getitm(&res) == OK) && (res == OK)) {

		ap_clear();
		nadrs = 0;
		amp_fail = FALSE;
		nonempty = FALSE;
		done = FALSE;

		if (ap_pinit(getbufchar) == BADAP) {
			PP_LOG(LLOG_EXCEPTIONS,
			       ("problem parsing message"));
			return NOTOK;
		}
			
		while (done == FALSE) {
			res = ap_1adr();	/* Parse one adr */
			switch(res) {
			    case DONE:	/* done */
				done = TRUE;
				break;
			    case NOTOK:
				/* pass the garbage and generate warning */
				amp_fail = TRUE;
				break;
			    default:	/* print it */
				ap_ppush (getbufchar);
				if ((res = out_adr(ap_pstrt)) != OK) 
					return res;
				ap_ppop();
				ap_pstrt = ap_pcur = ap_alloc ();
				break;
			}
		}
		putchar('\n');
		if (ap_perlev) {	/* if still nested */
			PP_TRACE(("nested level %d",ap_perlev));
			amp_fail++;
		}
		if (amp_fail == TRUE)
			printf("PP_warning: Parse error in original version of preceding line\n");
	}

	if (message_id == 0 && msgid_req == TRUE) {
		/* output message id */
		MPDUid	msgid;
		char	buf[BUFSIZ];
		strcpy(buf, "Message-ID");
		cp = &buf[0];
		while (*cp != '\0')
			putchar (*cp++);
		putchar (':');
		putchar (' ');

		MPDUid_new(&msgid);
		(void) sprintf(buf, "<\"%s\"@%s>", 
			       msgid.mpduid_string, loc_dom_site);
		cp = buf;
		while (*cp != '\0')
			putchar (*cp++);
		putchar ('\n');
	}
						   
	if (jntsendernorm != NULL && noFrom) {
		/* output Sender */
		cp = rcmd_srch(FLD_SENDER, tbl_fields);
		while (*cp != '\0')
			putchar(*cp++);
		putchar(':');
		putchar(' ');

		cp = jntsendernorm;
		while (*cp != '\0')
			putchar(*cp++);
		putchar('\n');
	}
	if (jntsendernorm != NULL) {
		ap_sqdelete(jnttree, NULLAP);
		free((char *)jnttree);
	}
	if (res == NOTOK) 
		return NOTOK;
	
	tidy_up();
/*	putchar('\n');	/* put blank line after header */
	
	return (ferror(stdout) ? NOTOK : OK);
}

/* \f

 */
/* input routines */

static char	*fieldbuf = NULL,
		*contbuf = NULL;
static int	fieldsize = 0,
		contsize = 0;
static int	fieldlen = 0,
		contlen = 0;
static char	*contix = NULL;

#define	INC	2

/* returns current pointer = *orig + olength */
static char	*resize_buf(orig, olength, size)
char	**orig;
int	olength,
	*size;
{

	*size += INC;
	if (*orig == NULLCP)
		*orig = calloc (1, (unsigned int) (*size));
	else
		*orig = realloc (*orig, (unsigned int) (*size));
	return ((*orig)+olength);
}

/* returns OK while still got more input to read */
/* result == NOTOK if failed parsing */
static int getitm(result)
int 	*result;
{
	register int	c;
	register char	*cp,
			*ix;
	int		gotitm = FALSE;

	fieldlen = 0;
	cp = fieldbuf;
	*result = OK;

	while (gotitm == FALSE) {
		if ((c = getach()) == EOF) 
			/* end of file */
			return NOTOK;
		
		switch (c) {
		    case '\n':
			if (fieldlen != 0)
				break;
		    case '\0':
			/* end of input */
			return NOTOK;
		}
/* 		putchar(c);*/
		
		switch (c) {
		    case ':':
			/* Field name collected */
			if ((fieldlen+1) >= fieldsize) 
				cp = resize_buf(&fieldbuf,fieldlen,&fieldsize);
			*cp = '\0';
			PP_TRACE(("field '%s'",fieldbuf));
			ix = fieldbuf;
			if (isspace(*ix) && *ix != '\n') {
				while (isspace(*ix) && *ix != '\n')
					ix++;
				/* rewind back one to first nonspace */
				ix--;
			}

			/* got field now get contents */
			cp = contbuf;
			contlen = 0;

			while (((c = getach()) != 0) && (c != EOF)) {
				if (++contlen >= contsize) 
					cp = resize_buf(&contbuf,(contlen-1),&contsize);
				*cp++ = c;
			}
				
			/* terminate with a null char */
			if ((contlen + 1) >= contsize) 
				cp = resize_buf(&contbuf,contlen,&contsize);
			*cp ='\0';
			compress(fieldbuf, fieldbuf);
			compress(contbuf, contbuf);
			contix = contbuf;

			/* check if need to skip */
			if ((striptrace == TRUE)
			    && (cmd_srch(fieldbuf, tbl_tracefields) != -1)) {
				/* skip it */
				cp = fieldbuf;
				fieldlen = 0;
				break;
			}
			if (cmd_srch(fieldbuf, tbl_fields) == FLD_FROM)
				from = 1;
			else
				from = 0;
			if (jntsender != NULL
			    && (cmd_srch(fieldbuf, tbl_fields) == FLD_SENDER))
				/* convert to Original-Sender */
				cp = rcmd_srch(FLD_ORIG_SENDER, tbl_fields);
				
			else 
				cp = fieldbuf;

			pcol = strlen(cp)+2;
			while (*cp != '\0')
				putchar(*cp++);
			putchar(':');
			/* put in jpo's space */
			putchar(' ');

			if (cmd_srch(ix,tbl_fields) != -1) {
				gotitm = TRUE;

			} else {
				int	fold_num;
				/* copy rest of line out */
				nonempty = FALSE;
				pcol = strlen(fieldbuf) + 1;
				cp = contix;
				if (lexequ(fieldbuf, "Message-ID") == 0)
					message_id++;
				if ((fold_num = cmd_srch(fieldbuf,tbl_nonfields)) == -1) {
					PP_DBG(("unknown non field %s folding on spaces",fieldbuf));
					fold_num = FOLD_SPACE;
				}
				while (*contix != '\0') {
					/* go to next fold */
					cp = next_fold(contix+1,fold_num);
					
					if ((fold_width != -1)
					    && (cp - contix + pcol > fold_width) 
					    && nonempty) {
						/* new line */
						pcol = strlen(fieldbuf) + 2;
						printf("\n%*s", strlen(fieldbuf) + 2, "");
						/* strip out white space */
						while (isspace(*contix))
							contix++;
					} else
						nonempty = TRUE;
					pcol += cp - contix;
					/* output line */
					while (contix != cp)
						putchar(*contix++);
				}
				putchar('\n');				
				if (c == EOF)
					/* EOF */
					return NOTOK;
			}
			cp = fieldbuf;
			fieldlen = 0;
			break;

		    default:
			if (++fieldlen >= fieldsize) 
				cp = resize_buf(&fieldbuf,(fieldlen-1),&fieldsize);
			*cp++ = c;
		}
	}
	return OK;
}

static int isblank(ch)
char	ch;
{
	return (ch == ' ' || ch == '\t');
}

/* returns 0 when reach end of an item */
/* returns EOF when reach EOF */
static int	getach()
{
	static char	buf[FILNSIZE];
	static char	*bufp = buf;
	static int	noInput = 0;
	
	if (noInput == 0) { /* buffer is empty */
		noInput = read(0, buf, FILNSIZE);
		bufp = buf;
	}

	if (*bufp == '\n') {
		if (noInput == 1) {
			/* last char in buffer */
			noInput = read(0, buf, FILNSIZE);
			bufp = buf;
			if (isblank(*bufp)) {
				noInput--;
				return *bufp++;
			} else
				return 0;
		} else if (isblank(*(bufp+1))) {
			/* skip newline */
			bufp++;
			noInput--;
		}
	}
	if (*bufp == '\n') {
		bufp++;
		noInput--;
		return 0;
	}
	return ((--noInput >= 0) ? *bufp++ : EOF);
}

static int	getbufchar()
{
	char	ret = *contix;
	if (ret != 0) contix++;
	return (ret == 0) ? EOF : ret;
}

/* \f

 */
/* output routine */

static char	*next_fold(ix,fold)
char	*ix;
int	fold;
{
	char   	fold_ch;

	switch (fold) {
	    case FOLD_NONE:
		fold_ch = '\0';
		break;
	    case FOLD_SPACE:
		fold_ch = ' ';
		break;
	    case FOLD_COMMA:
		fold_ch = ',';
		break;
	    case FOLD_SEMICOLON:
		fold_ch = ';';
		break;
	    case FOLD_RECIEVED:
		return  fold_recieved(ix);
		break;
	    default:
		PP_LOG(LLOG_EXCEPTIONS,
		       ("unknown fold number %d",fold));
		fold_ch = ' ';
	}

	while (*ix != '\0' && *ix != fold_ch)
		ix++;
	if (*ix == '\0')
		return ix;
	else
		return ++ix;
}

static char	*fold_recieved(chs)
char	*chs;
{
	char	*ix;
	while (*chs != '\0' && *chs != ';' && *chs != ' ')
		chs++;
	if (*chs == '\0')
		return chs;
	if (*chs == ';')
		return ++chs;
	/* skip leading spaces */
	while (isspace(*chs))
		chs++;

	/* now check if have key words */
	ix = chs;
	while (*ix != '\0' &&
	       (ix - chs <= strlen("from")+1)) {
		
		if (ix - chs == 3) {
			if (strncmp(chs,"by ",3) == 0)
				return chs;
			else if (strncmp(chs,"id ",3) == 0)
				return chs;
		}
		
		if (ix - chs == 4) {
			if (strncmp(chs,"via ",4) == 0)
				return chs;
			else if (strncmp(chs,"for ",4) == 0)
				return chs;
		}
		
		if (ix - chs == 5) {
			if (strncmp(chs,"from ",5) == 0)
				return chs;
			else if (strncmp(chs, "with ", 5) == 0)
				return chs;
				
		}
		ix++;
	}
	if (*ix == '\0')
		return ix;
	return fold_recieved(chs);
}

static int specifiedDomain(ptr)
AP_ptr	ptr;
{
	int	specified = FALSE,
		i = 0;

	/* get to next domain */
	while ((ptr != NULL) && (ptr->ap_obtype != AP_DOMAIN))
		ptr = ptr->ap_next;
	if (ptr == NULL)
		return FALSE;

	while (specified != TRUE && i < num_domains) {
		if ((ptr->ap_obvalue != NULL)
		    && (strcmp(stripdomains[i], ptr->ap_obvalue) == 0))
			specified = TRUE;
		i++;
	}
	return specified;
}


static int hiddenDomain(ptr)
AP_ptr	ptr;
{
	int	hidden = FALSE,
		i, nwild, nw;
	char   *bp, *cp;

	/* get to next domain */
	while ((ptr != NULL) && (ptr->ap_obtype != AP_DOMAIN))
		ptr = ptr->ap_next;
	if (ptr == NULL)
		return 0;

	if (ptr->ap_obvalue != NULL)
		for (i = 0; hidden != TRUE && i < num_to_hide; i++) {
			for (nwild = 0; hidedomains[i][nwild] == '.' ||
			    hidedomains[i][nwild] == '*'; nwild ++)
				;
			nw = nwild;
			nwild /= 2;
			bp = cp = ptr->ap_obvalue;
			if ( ! hidedomains[i][nw] ) {
				PP_LOG (LLOG_EXCEPTIONS,
				    ("Format/rfc822norm/hiddenDomain: Too wild - Reality error"));
				continue;
			}

			while ( (nwild -- > 0) && (bp = index(bp, '.')) != NULL)
				bp ++;
			if ( !bp )
				continue;
			while ((bp = index(bp,'.')) != NULL) {
				if (++bp =='\0')
					break;
				cp = index(cp,'.'); 
				cp++;
				if ((strcmp(&hidedomains[i][nw], bp) == 0)) {
					hidden = TRUE;
					strcpy(ptr->ap_obvalue, cp);
					break;
				}
			}
		}
	return hidden;
}

static int recognisedDomain(ptr)
AP_ptr	ptr;
{
	char	official[LINESIZE], *subdom = NULLCP;
	
	while ((ptr != NULL) && (ptr->ap_obtype != AP_DOMAIN))
		ptr = ptr->ap_next;
	if ((ptr != NULL)
	    && (tb_getdomain(ptr->ap_obvalue, NULLCP, 
			     official, order_pref, &subdom) == OK)) {
		if (subdom != NULL) free(subdom);
		return TRUE;
	}else {
		if (subdom != NULL) free(subdom);
		return FALSE;
	}
}

static int	endOfRoute(ptr, group_ptr, name_ptr, loc_ptr, dom_ptr)
AP_ptr	ptr,
	group_ptr,
	name_ptr,
	loc_ptr,
	dom_ptr;
{
	if ((ptr == group_ptr) || (ptr  == name_ptr) 
	    || (ptr == loc_ptr) || (ptr == dom_ptr))
		return TRUE;
	else
		return FALSE;
}

static AP_ptr	recur_stripdomain(ptr, group_ptr, name_ptr, loc_ptr, dom_ptr)
AP_ptr	ptr,
	group_ptr,
	name_ptr,
	loc_ptr,
	dom_ptr;
{
	if (ptr == NULL)
		return NULL;

	if (endOfRoute(ptr->ap_next, group_ptr, name_ptr, 
		       loc_ptr, dom_ptr) == TRUE) {
		if ((specifiedDomain(ptr) == TRUE)
		    && (specifiedDomain(dom_ptr) == TRUE
			|| recognisedDomain(dom_ptr) == TRUE))
			return ptr->ap_next;
		else
			return ptr;
	}
		    
	if ((specifiedDomain(ptr) == TRUE)
	    && (specifiedDomain(ptr->ap_next) == TRUE
		|| recognisedDomain(ptr->ap_next) == TRUE))
		return recur_stripdomain(ptr->ap_next, group_ptr, name_ptr, 
					 loc_ptr, dom_ptr);
	else {
		ptr->ap_next = recur_stripdomain(ptr->ap_next, group_ptr, 
						 name_ptr, loc_ptr, dom_ptr);
		return ptr;
	}
}

static char	*do_stripdomains(group_ptr, name_ptr, loc_ptr, 
				dom_ptr, route_ptr)
AP_ptr  group_ptr,
	name_ptr,
	loc_ptr,
	dom_ptr,
	route_ptr;
{
	char	*retval;
	AP_ptr	ptr;
	int	cont = TRUE;

	if (route_ptr == NULL) 
		retval = ap_p2s(group_ptr, name_ptr, loc_ptr, 
				dom_ptr, (AP_ptr) NULL);
	else {
		/* route ptr is least significant domain */
		ptr = route_ptr;
		while ((endOfRoute(ptr->ap_next, group_ptr, name_ptr,
				  loc_ptr, dom_ptr) == FALSE)
			&& (cont == TRUE)) {
			cont = FALSE;
			if ((specifiedDomain(ptr) == TRUE)
			    && (specifiedDomain(ptr->ap_next) == TRUE
				|| recognisedDomain(ptr->ap_next) == TRUE)) {
				    cont = TRUE;
				    ptr = ptr->ap_next;
			    }
		}
		if (cont == TRUE) {
			/* must have reached end of route */
			/* check with dom_ptr */
			if ((specifiedDomain(ptr) == TRUE)
			    && (specifiedDomain(dom_ptr) == TRUE
				|| recognisedDomain(dom_ptr) == TRUE))
				/* strip all routes */
			    ptr = NULL;
		}
		retval = ap_p2s(group_ptr, name_ptr, loc_ptr, 
				dom_ptr, ptr);
	}
	return retval;
}

static char	*do_striproutes(group_ptr, name_ptr, loc_ptr, 
				dom_ptr, route_ptr)
AP_ptr  group_ptr,
	name_ptr,
	loc_ptr,
	dom_ptr,
	route_ptr;
{
	char	*retval;
	AP_ptr	ix_ptr,
		strip_start;

	if ((recognisedDomain(dom_ptr) == TRUE)
		|| (route_ptr == NULL))
		retval = ap_p2s(group_ptr, name_ptr, loc_ptr, 
			       dom_ptr, (AP_ptr) NULL);
	else {
		ix_ptr = route_ptr;

		while ((ix_ptr->ap_next != NULL)
		       && (recognisedDomain(ix_ptr) != TRUE))
			ix_ptr = ix_ptr->ap_next;
		strip_start = ix_ptr->ap_next;
		ix_ptr->ap_next = NULL;
		retval = ap_p2s(group_ptr, name_ptr, loc_ptr, 
				dom_ptr, route_ptr);
		ix_ptr->ap_next = strip_start;
	}
	return retval;
}

static 
do_hidedomains(dom_ptr, route_ptr)
AP_ptr  dom_ptr,
route_ptr;
{
	int	retval = 0;
	AP_ptr	ptr;
	if ( ptr = route_ptr )
		while ( ptr ) {
			retval += hiddenDomain(ptr);
			ptr = ptr->ap_next;
		}
	return retval += hiddenDomain(dom_ptr);
}

static int 	tidy_up()
{
	if (random() % 10000 != 42)
		return;
	/* lucky person gets a message */
	switch (random() % 3) {
	    case 0:
		printf ("Checked-by: NSA, MI5, CIA, KGB\n");
		break;
	    case 1:
		printf ("Green-Message: This message is stored on recycled memory\n");
		break;
	    default:
		printf ("Congratulations: You are the recipient of our %d message\n", random() % 1000000);
		break;
	}
}

static AP_ptr	parse_and_norm(ap, pgroup, pname, ploc, pdom, proute)
AP_ptr	ap,
	*pgroup,
	*pname,
	*ploc,
	*pdom,
	*proute;
{
	char	*str, *oldlocal, *newlocal;
	AP_ptr	group, name, loc, dom, route;
	AP_ptr	newtree, newgroup, newname, newloc, newdom, newroute;
	ADDR	*adr;
	RP_Buf	rp;
	int	oldap_outtype;

	ap = ap_normalize(ap, order_pref);

	(void) ap_t2p (ap, &group, &name, &loc, &dom, &route);

	oldap_outtype = ap_outtype;
	ap_outtype = AP_PARSE_822;

	str = ap_p2s_nc (group, name, loc, dom, route);
	
	adr = adr_new (str, AD_822_TYPE, 0);
	adr -> ad_resp = NO;
	if (!rp_isbad(ad_parse(adr, &rp, order_pref))) {
		/* may have to munge */
		ap_s2p(adr->ad_r822adr, &newtree, &newgroup, 
		       &newname, &newloc, &newdom, &newroute);
		oldlocal = ap_p2s_nc(NULLAP, NULLAP, loc, NULLAP, NULLAP);
		newlocal = ap_p2s_nc(NULLAP, NULLAP, newloc, NULLAP, NULLAP);
		if (lexequ(oldlocal, newlocal) != 0) {
			AP_ptr	hdr, ix, tmp;
			AP_ptr	firstdom = NULLAP, firstloc = NULLAP;

			/* oh oh munge address */
			/* add header */
			hdr = ap_new(AP_COMMENT, "header");
			hdr -> ap_next = ap;
			hdr -> ap_ptrtype = AP_PTR_MORE;
			ix = hdr;

			/* delete everything except comments from old tree */
			while (ix -> ap_next != NULLAP) {
				switch (ix -> ap_next -> ap_obtype) {
				    case AP_DOMAIN:
				    case AP_DOMAIN_LITERAL:
					if (firstloc != NULLAP
					    && firstdom == NULLAP)
						firstdom = ix;
					ap_delete(ix);
					break;
				    case AP_MAILBOX:
				    case AP_GENERIC_WORD:
					if (firstloc == NULLAP)
						firstloc = ix;
					ap_delete(ix);
					break;
				    default:
				    case AP_COMMENT:
					ix = ix -> ap_next;
					break;
				}
			}

			/* now splice new tree into old tree */
			
			for (ix = newdom; ix -> ap_next != NULLAP; 
			     ix = ix -> ap_next);
			
			/* put newdom -> ix in after firstdom */
			tmp = firstdom -> ap_next;
			firstdom -> ap_next = newdom;
			ix -> ap_next = tmp;
			
			for (ix = newloc; ix -> ap_next != NULLAP
			     && ix -> ap_next != newdom
			     && ix -> ap_next != newroute;
			     ix = ix -> ap_next);
			
			/* put newloc -> ix in after firstloc */
			tmp = firstloc -> ap_next;
			firstloc -> ap_next = newloc;
			ix -> ap_next = tmp;
			
			ap = hdr -> ap_next;
			ap_free(hdr);
			
			/* reset parts */
			(void) ap_t2p (ap, &group, &name, &loc, &dom, &route);
		} else
			ap_sqdelete (newtree, NULLAP);
		free (oldlocal);
		free (newlocal);
		adr_free(adr);
	}
	ap_outtype = oldap_outtype;

	*pgroup = group,
	*pname = name;
	*ploc = loc;
	*pdom = dom;
	*proute = route;
	return ap;
}
		
static char *re_parse_ptr;
static int	get_rp_char()
{
	char	ret = *re_parse_ptr;
	if (ret != 0) re_parse_ptr++;
	return (ret == 0) ? EOF : ret;
}

static int	out_adr(ap)
register AP_ptr	ap;
{
	AP_ptr  loc_ptr,        /* -- in case fake personal name needed -- */
	group_ptr,
	name_ptr,
	dom_ptr,
	route_ptr;
	char	*addrp;
	int	len;
	static  rd = 0;

	if (ap->ap_obtype == AP_NIL)
		return OK;

	rd++;

	ap = parse_and_norm(ap, &group_ptr, &name_ptr, 
			    &loc_ptr, &dom_ptr, &route_ptr);

	if (from && equalWithJntsender(loc_ptr))
		noFrom = 0;

	/* do all stripping then create addrp */
	if (rd < 3 && (dom_ptr != NULL)
		&& (num_to_hide != 0)
		&& do_hidedomains(dom_ptr, route_ptr)) {

	/* This might be local now, so go through the whole expansion
	 * process again.  Remember though that we might be exremely
	 * silly, and cause `infinite' recursion.  Hence the rd variable.
	 */

		re_parse_ptr = addrp = ap_p2s(group_ptr, name_ptr, loc_ptr, 
						dom_ptr, route_ptr);
		ap_sqdelete (ap, NULLAP);
		ap_free (ap);
		ap = ap_pstrt = ap_pcur = ap_alloc ();
		ap_ppush(get_rp_char);
		ap_clear();
		if (ap_1adr() == OK)
			out_adr(ap);
		ap_ppop();
		free(addrp);
		rd--;
		return OK;
		/* NOTREACHED */
	}
	if ((dom_ptr != NULL)
	    && (striproutes == TRUE))
		addrp = do_striproutes(group_ptr, name_ptr, loc_ptr,
				       dom_ptr, route_ptr);
	else if ((dom_ptr != NULL) 
		 && (num_domains != 0))
		addrp = do_stripdomains(group_ptr, name_ptr, loc_ptr,
					dom_ptr, route_ptr);
	else
		addrp = ap_p2s(group_ptr, name_ptr, loc_ptr, 
			       dom_ptr, route_ptr);

				       
	if (addrp == (char *)NOTOK) {
		PP_LOG (LLOG_EXCEPTIONS,
			("Lib/addr/ap_t2s: error from ap_p2s()"));
		addrp = strdup ("(PP Error!)");
	}
	if (rd == 3) {
		PP_LOG (LLOG_EXCEPTIONS,
			("Format/rfc822norm: Excessive local hiding - cannot resolve address"));
		free(addrp);
		addrp = strdup ("(PP Configuration Error!)");
	}

	if (nadrs != 0) {
		printf(", ");
		pcol += 2;
	}

	PP_TRACE(("output '%s'",addrp));

	if ((len = strlen(addrp)) > 0) { /* print */
		pcol += len;
		if (fold_width != -1 && pcol > fold_width && nonempty) {
			pcol = strlen(fieldbuf) + 2 + len;
			printf("\n%*s", strlen(fieldbuf) + 2, "");
		} else
			nonempty = TRUE;
		
		printf("%s",addrp);
		nadrs++;
	}
	free(addrp);
/*	ap_sqdelete (ap, NULLAP);
	ap_free (ap);*/
	rd--;
	return OK;
}

/* \f

 */
static char *reverse(str)
char	*str;
{
	char	*ret = malloc((unsigned) strlen(str)+1),
		*dup = strdup(str),
		*ix;
	ret[0] = '\0';
	while ((ix = rindex(dup,'.')) != NULL) {
		if (ret[0] == '\0')
			sprintf(ret, "%s", ix+1);
		else
			sprintf(ret,"%s.%s",ret,ix+1);
		*ix = '\0';
	}
	if (ret[0] == '\0')
		sprintf(ret, "%s", dup);
	else
		sprintf(ret,"%s.%s",ret,dup);
	free(dup);
	return ret;
}