/*
**		S _ G E T
**
**
**	R E V I S I O N  H I S T O R Y
**
**	03/31/83  GWH	Split the SEND program into component parts
**			This module contains getuinfo, gethead, prefix,
**			getaddr, and addrcat.
**
**	04/15/83  DPK	Changed address format from "user at host" to
**			"user@host". Also added call to getmailid() in get
**			getuinfo();
**
**	05/20/83  GWH	Modified getuinfo() to allow a number of user-
**			selectable options. These include the use of a
**			".sendrc" file on the user's home directory. With
**			the use of the "rc" file the user can set such things
**			default editor, local-alias-file, signature block,
**			and a directory for draft files. Also included is a
**			provision for keeping file copies of messages that
**			are successfully copied (incomplete at this time).
**
**	06/22/83  GWH	Added the required code to addrcat to enable local
**			alias file feature.
**
**	10/21/83  GWH	Fixed a bug in the alias file code (in addrcat).
**			Prevents segmentation faults when loops are found
**			in the alias file definitions.
**
**	11/16/83  DPK	Merged in DPK signature checking and Geo loop catcher
**
**	11/21/83  GWH	Added 'directedit' option.
**
**	03/18/85  DPK	Fixed usage of parsadr function so that after parsing
**		  VAK	we now use the components instead of original string.
**
*/


#include <stdio.h>
#include "./s.h"
#include "./s_externs.h"

extern char *getmailid();
extern char *malloc();
extern char *strdup();
extern struct passwd *getpwuid();
extern char *mktemp();
extern char *index();
extern char *getenv();

extern char *dfleditor;
extern char *dflveditor;
extern char *dflchecker;

#define END	(char *)0

struct RCOPTION rcoptions[] = {
	"copyfile", COPYFILE,
	"draftdir", DRAFTDIR,
	"signature", SIGNATURE,
	"nosignature", NOSIGNATURE,
	"replyto", REPLYTO,
	"aliases", ALIASES,
	"subargs", SUBARGS,
	"editor", EDITOR,
	"veditor", VEDITOR,
	"file", CFILE,
	"fileonquery", QFILE,
	"nofile", NOFILE,
	"directedit", DIREDIT,
	"checker", CHECKER,
	"header", ADDHEADER,
	"paging", PAGING,
	END
};

int picko();

struct passwd *pw;
char *midp;
char *d_subargs = "vm";
char *drft_tmplt = "drft.XXXXXX";
char linebuf[128];
char tmpline[128];

struct ALIAS *als, alias_ptr;
struct ALIAS *old_als;
FILE *afd;

getuinfo() {
	FILE *fp;
	int realid, effecid;
	char *av[NARGS];
	char rcfilename[128];
	register char *p;

	int i;

	/* build a draft file name */

	mktemp(drft_tmplt);
	/* Who are we this time */

	getwho(&realid, &effecid);

	/* If nothing found return false. */

	if ((pw = getpwuid(effecid)) == NULL ||
		(midp = getmailid(pw->pw_name)) == NULL)
			return(FALSE);

	/* Build a name for this user */
#ifdef PWNAME
	if (pw->pw_gecos != NULL && *pw->pw_gecos != '\0') {
		if (p = index(pw->pw_gecos, ','))
			*p = '\0';
		if (p = index(pw->pw_gecos, '<'))
			*p = '\0';
		if (p = index(pw->pw_gecos, '&')) {
			/* Deal with Berkeley folly */
			*p = 0;
			sprintf(from, "%s%c%s%s <%s@%s>", pw->pw_gecos,
				 lowtoup(pw->pw_name[0]),
				 pw->pw_name+1, p+1, midp, locname);
		} else
			sprintf(from, "%s <%s@%s>", pw->pw_gecos, midp, locname);
	} else
#endif
	sprintf(from, "%c%s@%s",	/* default from text */
		lowtoup(midp[0]), &(midp[1]), locname);
	/* Set default values for those options that need defaults */

	strcpy(editor, ((p = getenv("EDITOR")) && *p) ? p : dfleditor);
	strcpy(veditor, ((p = getenv("VISUAL")) && *p) ? p : dflveditor);
	strcpy(checker, dflchecker);
	sprintf(linebuf, "%s/.sent", pw->pw_dir);
	strcpy(copyfile, linebuf);
	sprintf(drffile, "%s/%s", pw->pw_dir, drft_tmplt);
	strcpy(subargs, d_subargs);
	wflag = 0;
	cflag = 0;
	aflag = 0;
	rflag = 0;
	qflag = 0;
	dflag = 0;
	pflag = 0;

	strcpy(signature, from);

	/* Get info from the ".sendrc" file */

	sprintf(rcfilename, "%s/%s", pw->pw_dir, SENDRC);
	if ((fp = fopen(rcfilename, "r")) != NULL) {
		char *cp;

		/* Process info a line at a time */
		while (fgets(linebuf, sizeof(linebuf), fp) != NULL) {
			if (cp = index(linebuf, '\n'))
				*cp = 0;
			if (sstr2arg(linebuf, NARGS, av, " \t") < 0)
				continue;
			if ((i=picko(av[0])) < 0)
				continue; /* bad option */
			select_o(i, av);
		}	/* end of while on lines */
	}	/* end of if statement on file open */

	return(TRUE);
}	/* end of getuinfo routine */

/* select an option */

picko(pp)
char *pp;
{
	struct RCOPTION *rcopt;

	rcopt = rcoptions;
	while (rcopt->name != END) {
		if (lexequ(rcopt->name, pp))
			return(rcopt->idnum);
		rcopt++;
	}

	return(-1); /* no match */
}

get_key(s, key, list)
char *s, **key, **list;
{
	char tmps[512];
	char *ptmps;
	int len_list;
/*
**  This routine takes the input string s and divides it into two parts,
**  the first of which consists of the first word in the original string.
**  It is returned in key. The second part is a list of the remaining names.
**  They are returned in the list. Malloc is used to allocate the necessary
**  storage for the sublists.
*/

	/* throw away initial white space */

	while (*s == ' ' || *s == '\t')
		*s++;

	/* get the first word. It may be terminated by spaces, tabs, commas, */
	/* newlines or end-of-file					     */

	ptmps = tmps;
	while (*s != ' ' && *s != '\t' && *s != ',' && *s != '\n' && 
			*s != (char)EOF) {
		*ptmps++ = *s++;
	}
	*ptmps = 0;
	len_list = strlen(tmps);
	if ((*key = malloc((unsigned)(len_list) + 1)) <= 0) {
		perror(" SEND - GET_KEY -- MALLOC ERROR");
		return(-1);
	}

	strcpy(*key, tmps);

	/* and now the rest of the list */

	ptmps = s;
	while (*s != '\n' && *s != (char)EOF && *s != '\0')
		*s++;
	*s = 0;
	len_list = strlen(ptmps);
	if (len_list <= 0)
		return(-1);	/* no list - ERROR - ERROR */
	if ((*list = malloc((unsigned)(len_list) +1)) <= 0) {
		perror(" SEND GET_KEY - MALLOC ERROR");
		return(-1);
	}

	strcpy(*list, ptmps);
	return(0);
}

select_o(key, pp)
int key;
char **pp;
{
	register char *p;
	char *akey, *list;
	char tmpbuf[512];

	switch (key) {
	case COPYFILE:
		/* Pick a file to store file copies of sent messages */
		strcpy(copyfile, *(++pp));
		if (copyfile[0] != '/') {
			strcpy(tmpline, copyfile);
			sprintf(copyfile, "%s/%s", pw->pw_dir, tmpline);
		}
		break;

	case DRAFTDIR:
		/* pickup the draft directory */
		if (*(++pp) != 0)
			sprintf(drffile, "%s/%s", *pp, drft_tmplt);
		else
			sprintf(drffile, "%s/%s", pw->pw_dir, drft_tmplt);
		break;

	case SIGNATURE:
		/* If he wants a signature block use it */
		if (*(++pp) == 0) {
#ifdef PWNAME
			/* use his login name alone */
			sprintf(signature, "%c%s@%s",
				lowtoup(midp[0]), &(midp[1]), locname);
#else
			strcpy(signature, from);	/* NO-OP ? */
#endif
		} else {
			linebuf[0] = '\0';
			while (*pp != 0) {
				strcat(linebuf, *pp);
				strcat(linebuf, " ");
				*pp++;
			}
			if (checksignature(linebuf) == 0) {
				strcpy(tmpline, locname);
				for(p=tmpline ; *p ; p++)
					*p = uptolow(*p);
				sprintf(signature, "%s <%s@%s>",
					linebuf, midp, tmpline);
			} else {
				printf("Illegal signature, using default\n");
			}
		}
		break;

	case NOSIGNATURE:
#ifdef PWNAME
		/* use his login name alone */
		sprintf(signature, "%c%s@%s",
			lowtoup(midp[0]), &(midp[1]), locname);
#else
		strcpy(signature, from);
#endif
		break;

	case REPLYTO:
		rflag = 1;
		break;

	case ALIASES:
		aflag = 1;
		strcpy(aliasfilename, *(++pp));
		if (aliasfilename[0] != '/') {
			strcpy(tmpline, aliasfilename);
			sprintf(aliasfilename, "%s/%s",pw->pw_dir,tmpline);
		}

		/*
		**  Open the alias file and store the keys and lists
		**  in core
		*/

		afd = fopen(aliasfilename, "r");
		if (afd == (FILE *)NULL) {
			aflag = 0;
			perror(" Can't open alias file ");
			break;
		}
		als = &alias_ptr;
		old_als = NULL;
		while (1) {
			if (fgets(tmpbuf, sizeof(tmpbuf), afd) != NULL) {
				get_key(tmpbuf, &akey, &list);
				if (old_als > 0) {
					if ((als = (struct ALIAS *) malloc(sizeof(alias_ptr))) <= 0) {
						perror("SEND GETUINFO - MALLOC ERROR");
						break;
					}
					old_als->next_int = als;
				}
				als->key = akey;
				als->cntr = 0;
				als->names = list;
				old_als = als;
			}
			else {
				old_als = NULL;
				break;
			}
		}
		break;

	case SUBARGS:
		strcpy(subargs, d_subargs);	/* Reset to default (required) */
		while (*(++pp) != NULL)
			strcat(subargs, *pp);
		wflag = 1;
		break;

	case EDITOR:
		/* Default editor for use with the "e" command */
		strcpy(editor, *(++pp));
		break;

	case VEDITOR:
		/* Default editor for use with the "v" command */
		strcpy(veditor, *(++pp));
		break;

	case CHECKER:
		/* Default spelling checker for use with the "c" command */
		strcpy(checker, *(++pp));
		break;

	case DIREDIT:
		dflag = 1;
		if ((*(++pp) != (char *) 0) && (**pp == 'e'))
			dflag = 2;
		break;

	case PAGING : 
		/* Turn on paging during message review */
		pflag = 1;
		break;

	case CFILE:
		cflag = 1;
		break;

	case QFILE:
		qflag = 1;
		/* DROP */
	case NOFILE:
		cflag = 0;
		break;

	case ADDHEADER:
		/* Add a header line to the message */
		++pp;
		if (isstr(*pp))
			addheader(pp[0], pp[1]);
		break;

	default:
		printf("GETUINFO - NO SUCH OPTION key = %d\n", key);

	} /* end of switch */
}

/* Prompts for a header with print as prompt and gathers it up */
gethead(print, buf, flag)
char	print[];
register char	*buf;
int	flag;			/* print contents, if any?		*/
{
	register int	c, n;
	register int	max = S_BSIZE;
	int	retval;		/* type of FLD modification		*/

	if (flag && *buf)		/* print what already is there	*/
		printf(shrtfmt, print, buf);

	flag = TRUE;		/* always & only look at first char	*/
	do {
		printf("%s", print);	/* prompt			*/
		if (*buf == '\\') {
			*buf++ = ',';
			*buf++ = '\n';
		}
		fflush(stdout);

		if (flag) {
			flag = FALSE;	/* ignore first char of next lines */
			switch (c = getchar()) {
			case '-':	/* delete single name		*/
				retval = ITMRMV;
				break;
			case '#':	/* delete all contents	*/
				*buf = 0;
				while (getchar() != '\n');
				return(FLDREMV);

			case '\n':	/* leave it as it is		*/
				return(FLDSAME);

			case '+':	/* append to end of field	*/
				retval = FLDADD;
				if (*buf != '\0') { /* if not already empty */
					/* point to end of buf */
					buf = &buf[strlen(buf)];
					/* assume it's an address field */
					*buf++ = ',';
					*buf++ = ' ';
					max -= 2;
				}
				break;

			default:	/* copy over it			*/
				retval = FLDOVER;
				*buf++ = c;
				max--;
				break;
			}
		}
		n = gather(buf, max);	/* get rest of header line	*/
		max -= n;
		buf = &buf[n - 1];
	} while (*buf == '\\');	/* user wants to add more?	*/

	return(retval);
}

prefix(buf, pre)
char *buf, *pre;
{
	while (*pre)
		if (*buf++ != *pre++)
			return(FALSE);
	return(TRUE);
}


getaddr(print, buf, flag, defhost)
char print[];
char *buf;
int flag;
char defhost[];
{
	char	addrin[S_BSIZE];	/* temporary store for user input */
	char	*dn, *gstrindex();

	if (flag && *buf)		/* print what already is there	*/
		printf(shrtfmt, print, buf);

	addrin[0] = '\0';		/* mark temp buffer as empty	*/
	switch (gethead(print, addrin, NO)) {
	case FLDREMV:		/* erase any existing contents	*/
		buf[0] = '\0';
		break;

	case ITMRMV:		/* delete single name		*/
		dn = gstrindex(addrin, buf);
		if (dn == 0) {
			printf(" no such name in the list; name = '%s'\n",
				addrin);
			break;
		}
		dlt_name(buf, dn);
		break;
	case FLDSAME:		/* leave as is			*/
		break;

	case FLDOVER:		/* overwrite existing contents	*/
		buf[0] = '\0';	/* erase existing, then add	*/
		addrcat(buf, addrin, defhost);
		break;

	case FLDADD:		/* append to existing		*/
		addrcat(buf, addrin, defhost);
		break;
	}
}

/* these varibles are made global for recursive reasons */

int linelen,
destlen,
addrlen;

addrcat(dest, src, thehost)
char *dest, *src, thehost[];
{
	char	addr[S_BSIZE], name[ADDRSIZE], address[ADDRSIZE],
		temphost[FILNSIZE], defhost[FILNSIZE], hostname[FILNSIZE];
	char	*sadrptr, *ptr;
	int	sub_flag;

	if (thehost != 0)		/* save official version of default */
		strcpy(defhost, thehost);

	for (ptr = dest, linelen = 0; *ptr != '\0'; ptr++, linelen++)
		if (*ptr == '\n')	/* get length of last line of header */
			linelen = -1;	/* make it zero for next character */
	destlen = ptr - dest;

	for (adrptr = src;;) {		/* adrptr is state variable, used in */
					/*   next_address		*/
		switch (next_address(addr)) {
		case -1:		/* all done			*/

			/* clean markers in aliases structure */
			als = &alias_ptr;
			while (als != NULL) {
				als->cntr = 0;
				als = als->next_int;
			}
			return;

		case 0:		/* empty address			*/
			continue;
		}

		name[0] = address[0] = temphost[0] = '\0';
		parsadr(addr, name, address, temphost);
		if (address[0] == '\0') {	/* change the default host */
			if (temphost[0] != '\0')
				strcpy(defhost, temphost);
			continue;
		}
		if (aflag && temphost[0]==0) {
			als = &alias_ptr;
			sub_flag = 0;
			while (als != NULL) {
				if (lexequ(address, als->key) && als->cntr == 0) {
					als->cntr++;
					sadrptr = adrptr;
					addrcat(dest, als->names, thehost);
					adrptr = sadrptr;
					sub_flag = 1;
					break;
				}
				else
					if (lexequ(address, als->key) && als->cntr != 0) {
						sub_flag = 1;
						break;
					}
				als = als->next_int;
			}
			if (sub_flag == 1)
				continue;
		}
		if (temphost[0] == '\0')
			strcpy(hostname, defhost);
		else
			strcpy(hostname, temphost);

		if (name[0] != '\0') {	/* put into canonical form	*/
					/* name <mailbox@host>		*/

			/* force hostname to lower		*/
			for (ptr = hostname; !isnull(*ptr); ptr++)
				*ptr = uptolow(*ptr);

			/* is quoting needed for specials?	*/
			for (ptr = name; ; ptr++) {
				switch (*ptr) {
				default:
					continue;

				case '\0':	/* nope			*/
					sprintf(addr, "%s <%s@%s>",
						name, address, hostname);
					goto doaddr;

				case '<':	/* quoting needed	*/
				case '>':
				case '@':
				case ',':
				case ';':
				case ':':
					sprintf(addr, "\"%s\" <%s@%s>",
						name, address, hostname);
					goto doaddr;
				}
			}
		}
		else {
			sprintf(addr, "%s@%s", address, hostname);
		}

doaddr:
		addrlen = strlen(addr);

		/* it will fit into dest buffer	*/
		if ((destlen + addrlen) < (S_BSIZE - 15)) {
			if (destlen == 0)
				strcpy(dest, addr);
			else {	/* monitor line length		*/
				if ((linelen + addrlen) < 67)
					sprintf(&dest[destlen], ", %s", addr);
				else {
					sprintf(&dest[destlen], ",\n%s", addr);
					linelen = 0;
				}
				destlen += 2;
				linelen += 2;
			}
			destlen += addrlen;
			linelen += addrlen;
		}

	}
}
/*
**	S P E C I A L  S T R I N D E X	F U N C T I O N
**
** Specially modified by George Hartwig to return a pointer to the beginning
** of the requested string instead of the offset.
*/

char *gstrindex(str, target)		/* return column str starts in target */
register char	*str,
*target;
{
	char *otarget;
	register short slen;

	for (otarget = target, slen = strlen(str); ; target++) {
		if (*target == '\0')
			return((char *) 0);

		if (equal(str, target, slen))
			return(target);
	}
}

/* buf is a pointer to a character array and pos is a pointer to a position  */
/* within that array. The purpose of this routine is delete the name field   */
/* pointed to by pos. In order to do this we will search backward to either  */
/* comma ',' or a colon ':' (if a comma, we will then delete that comma also */
/* and then delete forward until another comma or			     */
/* a end-of-string '\0' is found.					     */
dlt_name(buf, pos)
char *buf, *pos;
{
	char tmpbuffer[S_BSIZE];
	char *sd, *ed, *cp;
	int quote_flag;


	cp = pos;
	quote_flag = 0;

	while (cp >= buf) {
		if (*cp == '"')
			if (quote_flag == 0)
				quote_flag = 1;
			else
				quote_flag = 0;
		if (*cp == ',' && quote_flag == 0)
			break;
		else
			cp--;
	}
	sd = cp;

	cp = pos;

	while (*cp != '\0') {
		if (*cp == '"')
			if (quote_flag == 0)
				quote_flag = 1;
			else
				quote_flag = 0;
		if (*cp == ',' && quote_flag == 0)
			break;
		else
			cp++;
	}

	ed = cp;

	/* at this point *sd should point at the comma preceding the name
	** to be deleted or the beginning of the line and ed should be
	** pointing to the trailing comma or the end of the line. So it is
	** necessary at this point to determine just which of these cases
	** have occurred so that the string may be properly reassembled.
	*/

	if (*sd == ',' && *ed == ',') {
		sd++;
		*sd = '\0';
		ed++;
		sprintf(tmpbuffer, "%s%s", buf, ed);
	}

	else
		if (sd <= buf) {
			ed++;
			strcpy(tmpbuffer, ed);
		}
		else
			if (*ed == '\0') {
				*sd = '\0';
				strcpy(tmpbuffer, buf);
			}
	strcpy(buf, tmpbuffer);
}

/*
 *	checksignature()
 *
 *  Verifies that the signature string does not contain any special
 *  characters or is quoted.  Also checks for blank "name" portion.
 */
checksignature(sp)
register char *sp;
{
	char tptr[128], *savptr, *ptr;
	static char quote[] = "\"";

	savptr = sp;
	while (*sp && isspace(*sp))	/* eat leading whitespace */
		sp++;
	if (*sp == '\0') {
		printf("Signature is blank\n");
		return(-1);		/* must have some signature */
	}
	while (*sp) {
		switch (*sp) {
		case '<':
		case '>':
		case '@':
		case ',':
		case ';':
		case ':':
		case '.':	/*  Arggggg...	*/
		case '[':
		case ']':
			/* surround bad signature with quotes */
			tptr[0] = '\0';
			strcat(tptr,quote);
			sp = savptr;
			strcat(tptr,sp);
			if (ptr = rindex(tptr, ' '))
				*ptr = '\0';
			strcat(tptr,quote);
			strcpy(sp,tptr);
			return(0);

		case '\\':
			sp++;
			if (*sp == '\0')
				return(-1);
			break;

		case '"':
			for (sp++; *sp != '"'; sp++) {
				if (*sp == '\\') {
					sp++;
					if (*sp == '\0')
						return(-1);
				} else if (*sp == '\0') {
					printf("Unmatched '\"' in signature\n");
					return(-1);
				}
			}
			break;

		default:
			break;
		}
		sp++;
	}

	return(0);
}

addheader(name, data)
char	*name;
char	*data;
{
	register struct header *nhp;
	register struct header *ohp;
	register char *cp;
	char	buf[LINESIZE];

	if (!isstr(name))
		return;
	for (cp = name; *cp; cp++)
		if (!isprint(*cp) && *cp != '\t') {
			printf("Illegal character in header name.\n");
			return;
		}
	data = (isstr(data) ? data : "");
	for (cp = data; *cp; cp++)
		if (!isprint(*cp) && *cp != '\t') {
			printf("Illegal character in header data.\n");
			return;
		}
	if (headers) {
		for (ohp = headers; !lexequ(name, ohp->hname); ohp = ohp->hnext)
			if (ohp->hnext == (struct header *)0)
				break;
		if (lexequ(name, ohp->hname)) {
			strncpy(ohp->hdata, data, sizeof(ohp->hdata)-1);
			return;
		}
	}
	nhp = (struct header *)malloc(sizeof(*nhp));
	sprintf(buf, "%s:  ", name);
	if (nhp == NULL || (nhp->hname = strdup(buf)) == NULL) {
		printf("No memory for header '%s'\n", buf);
		if (nhp)
			free(nhp);
		return;
	}
	strncpy(nhp->hdata, data, sizeof(nhp->hdata)-1);
	nhp->hnext = (struct header *)0;
	nhp->hdata[sizeof(nhp->hdata)-1] = '\0';
	if (headers == (struct header *)0) {
		headers = nhp;
		return;
	}
	ohp->hnext = nhp;
}
