1294Seric # include <stdio.h>
2294Seric # include <pwd.h>
3294Seric # include <signal.h>
4294Seric # include "dlvrmail.h"
5294Seric # ifdef LOG
6294Seric # include <log.h>
7294Seric # endif LOG
8294Seric 
9*405Seric static char SccsId[] = "@(#)deliver.c	1.3	07/25/80";
10*405Seric 
11294Seric /*
12294Seric **  DELIVER -- Deliver a message to a particular address.
13294Seric **
14294Seric **	Algorithm:
15294Seric **		Compute receiving network (i.e., mailer), host, & user.
16294Seric **		If local, see if this is really a program name.
17294Seric **		Build argument for the mailer.
18294Seric **		Create pipe through edit fcn if appropriate.
19294Seric **		Fork.
20294Seric **			Child: call mailer
21294Seric **		Parent: call editfcn if specified.
22294Seric **		Wait for mailer to finish.
23294Seric **		Interpret exit status.
24294Seric **
25294Seric **	Parameters:
26294Seric **		to -- the address to deliver the message to.
27294Seric **		editfcn -- if non-NULL, we want to call this function
28294Seric **			to output the letter (instead of just out-
29294Seric **			putting it raw).
30294Seric **
31294Seric **	Returns:
32294Seric **		zero -- successfully delivered.
33294Seric **		else -- some failure, see ExitStat for more info.
34294Seric **
35294Seric **	Side Effects:
36294Seric **		The standard input is passed off to someone.
37294Seric **
38294Seric **	WARNING:
39294Seric **		The standard input is shared amongst all children,
40294Seric **		including the file pointer.  It is critical that the
41294Seric **		parent waits for the child to finish before forking
42294Seric **		another child.
43294Seric **
44294Seric **	Requires:
45294Seric **		buildargv
46294Seric **		giveresponse
47294Seric **		fork (sys)
48294Seric **		rewind (sys)
49294Seric **		execv (sys)
50294Seric **		exit (sys)
51294Seric **		wait (sys)
52294Seric **		syserr
53294Seric **		getpwnam (sys)
54294Seric **		endpwent (sys)
55294Seric **		initlog
56294Seric **		flagset
57294Seric **		usrerr
58294Seric **		pipe (sys)
59294Seric **		close (sys)
60294Seric **		dup (sys)
61294Seric **		setuid (sys)
62294Seric **		getuid (sys)
63294Seric **		signal (sys)
64294Seric **		fdopen (sys[v7] or conf.c[v6])
65294Seric **		fclose (sys)
66294Seric **		printf (sys)
67294Seric **		stripquotes
68294Seric **		mailfile
69294Seric **		index (sys)
70294Seric **
71294Seric **	Called By:
72294Seric **		main
73294Seric **		savemail
74294Seric **
75294Seric **	Files:
76294Seric **		standard input -- must be openned to the message to
77294Seric **			deliver.
78294Seric **
79294Seric **	History:
80294Seric **		3/5/80 -- modified rather extensively to change the
81294Seric **			internal form of addresses.
82294Seric **		12/26/79 -- written.
83294Seric */
84294Seric 
85294Seric deliver(to, editfcn)
86294Seric 	addrq *to;
87294Seric 	int (*editfcn)();
88294Seric {
89294Seric 	register struct mailer *m;
90294Seric 	char *host;
91294Seric 	char *user;
92294Seric 	extern struct passwd *getpwnam();
93294Seric 	char **pvp;
94294Seric 	extern char **buildargv();
95294Seric 	auto int st;
96294Seric 	register int i;
97294Seric 	register char *p;
98294Seric 	int pid;
99294Seric 	int pvect[2];
100294Seric 	extern FILE *fdopen();
101294Seric 	extern int errno;
102294Seric 	FILE *mfile;
103294Seric 	extern putheader();
104294Seric 	extern pipesig();
105294Seric 
106294Seric 	/*
107294Seric 	**  Compute receiving mailer, host, and to addreses.
108294Seric 	**	Do some initialization first.  To is the to address
109294Seric 	**	for error messages.
110294Seric 	*/
111294Seric 
112294Seric 	To = to->q_paddr;
113294Seric 	m = to->q_mailer;
114294Seric 	user = to->q_user;
115294Seric 	host = to->q_host;
116294Seric 	Error = 0;
117294Seric 	errno = 0;
118294Seric # ifdef DEBUG
119294Seric 	if (Debug)
120294Seric 		printf("deliver(%s [%d, `%s', `%s'])\n", To, m, host, user);
121294Seric # endif DEBUG
122294Seric 
123294Seric 	/*
124294Seric 	**  Remove quote bits from user/host.
125294Seric 	*/
126294Seric 
127294Seric 	for (p = user; (*p++ &= 0177) != '\0'; )
128294Seric 		continue;
129294Seric 	if (host != NULL)
130294Seric 		for (p = host; (*p++ &= 0177) != '\0'; )
131294Seric 			continue;
132294Seric 
133294Seric 	/*
134294Seric 	**  Strip quote bits from names if the mailer wants it.
135294Seric 	*/
136294Seric 
137294Seric 	if (flagset(M_STRIPQ, m->m_flags))
138294Seric 	{
139294Seric 		stripquotes(user);
140294Seric 		stripquotes(host);
141294Seric 	}
142294Seric 
143294Seric 	/*
144294Seric 	**  See if this user name is "special".
145294Seric 	**	If the user is a program, diddle with the mailer spec.
146294Seric 	**	If the user name has a slash in it, assume that this
147294Seric 	**		is a file -- send it off without further ado.
148294Seric 	**		Note that this means that editfcn's will not
149294Seric 	**		be applied to the message.
150294Seric 	*/
151294Seric 
152294Seric 	if (m == &Mailer[0])
153294Seric 	{
154294Seric 		if (*user == '|')
155294Seric 		{
156294Seric 			user++;
157294Seric 			m = &Mailer[1];
158294Seric 		}
159294Seric 		else
160294Seric 		{
161294Seric 			if (index(user, '/') != NULL)
162294Seric 			{
163294Seric 				i = mailfile(user);
164294Seric 				giveresponse(i, TRUE, m);
165294Seric 				return (i);
166294Seric 			}
167294Seric 		}
168294Seric 	}
169294Seric 
170294Seric # ifdef BADMAIL
171294Seric 	/*
172294Seric 	**  If the mailer doesn't return the proper
173294Seric 	**  exit statuses, check here to see if the
174294Seric 	**  user exists so that we can give a pretty
175294Seric 	**  error message.
176294Seric 	*/
177294Seric 
178294Seric 	if (m == &Mailer[0])
179294Seric 	{
180294Seric 		if (getpwnam(user) == NULL)
181294Seric 		{
182294Seric 			giveresponse(EX_NOUSER, TRUE, m);
183294Seric 			return (EX_NOUSER);
184294Seric 		}
185294Seric 	}
186294Seric # endif BADMAIL
187294Seric 
188294Seric 	/*
189294Seric 	**  If the mailer wants a From line, insert a new editfcn.
190294Seric 	*/
191294Seric 
192294Seric 	if (flagset(M_HDR, m->m_flags) && editfcn == NULL)
193294Seric 		editfcn = putheader;
194294Seric 
195294Seric 	/*
196294Seric 	**  Call the mailer.
197294Seric 	**	The argument vector gets built, pipes through 'editfcn'
198294Seric 	**	are created as necessary, and we fork & exec as
199294Seric 	**	appropriate.  In the parent, we call 'editfcn'.
200294Seric 	*/
201294Seric 
202294Seric 	pvp = buildargv(m->m_argv, m->m_flags, host, user, From.q_paddr);
203294Seric 	if (pvp == NULL)
204294Seric 	{
205294Seric 		usrerr("name too long");
206294Seric 		return (-1);
207294Seric 	}
208294Seric 	rewind(stdin);
209294Seric 
210294Seric 	/* create a pipe if we will need one */
211294Seric 	if (editfcn != NULL && pipe(pvect) < 0)
212294Seric 	{
213294Seric 		syserr("pipe");
214294Seric 		return (-1);
215294Seric 	}
216294Seric 	pid = fork();
217294Seric 	if (pid < 0)
218294Seric 	{
219294Seric 		syserr("Cannot fork");
220294Seric 		if (editfcn != NULL)
221294Seric 		{
222294Seric 			close(pvect[0]);
223294Seric 			close(pvect[1]);
224294Seric 		}
225294Seric 		return (-1);
226294Seric 	}
227294Seric 	else if (pid == 0)
228294Seric 	{
229294Seric 		/* child -- set up input & exec mailer */
230294Seric 		signal(SIGINT, SIG_IGN);
231294Seric 		if (editfcn != NULL)
232294Seric 		{
233294Seric 			close(0);
234294Seric 			if (dup(pvect[0]) < 0)
235294Seric 			{
236294Seric 				syserr("Cannot dup to zero!");
237294Seric 				exit(EX_OSERR);
238294Seric 			}
239294Seric 			close(pvect[0]);
240294Seric 			close(pvect[1]);
241294Seric 		}
242294Seric 		if (!flagset(M_RESTR, m->m_flags))
243294Seric 			setuid(getuid());
244294Seric # ifdef LOG
245294Seric 		initlog(NULL, 0, LOG_CLOSE);
246294Seric # endif LOG
247294Seric 		endpwent();
248294Seric 		execv(m->m_mailer, pvp);
249294Seric 		/* syserr fails because log is closed */
250294Seric 		/* syserr("Cannot exec %s", m->m_mailer); */
251294Seric 		exit(EX_UNAVAIL);
252294Seric 	}
253294Seric 
254294Seric 	/* arrange to write out header message if error */
255294Seric 	if (editfcn != NULL)
256294Seric 	{
257294Seric 		close(pvect[0]);
258294Seric 		signal(SIGPIPE, pipesig);
259294Seric 		mfile = fdopen(pvect[1], "w");
260294Seric 		(*editfcn)(mfile);
261294Seric 		fclose(mfile);
262294Seric 	}
263294Seric 
264294Seric 	/*
265294Seric 	**  Wait for child to die and report status.
266294Seric 	**	We should never get fatal errors (e.g., segmentation
267294Seric 	**	violation), so we report those specially.  For other
268294Seric 	**	errors, we choose a status message (into statmsg),
269294Seric 	**	and if it represents an error, we print it.
270294Seric 	*/
271294Seric 
272294Seric 	while ((i = wait(&st)) > 0 && i != pid)
273294Seric 		continue;
274294Seric 	if (i < 0)
275294Seric 	{
276294Seric 		syserr("wait");
277294Seric 		return (-1);
278294Seric 	}
279294Seric 	if ((st & 0377) != 0)
280294Seric 	{
281294Seric 		syserr("%s: stat %o", pvp[0], st);
282294Seric 		ExitStat = EX_UNAVAIL;
283294Seric 		return (-1);
284294Seric 	}
285294Seric 	i = (st >> 8) & 0377;
286294Seric 	giveresponse(i, FALSE, m);
287294Seric 	return (i);
288294Seric }
289294Seric /*
290294Seric **  GIVERESPONSE -- Interpret an error response from a mailer
291294Seric **
292294Seric **	Parameters:
293294Seric **		stat -- the status code from the mailer (high byte
294294Seric **			only; core dumps must have been taken care of
295294Seric **			already).
296294Seric **		force -- if set, force an error message output, even
297294Seric **			if the mailer seems to like to print its own
298294Seric **			messages.
299294Seric **		m -- the mailer descriptor for this mailer.
300294Seric **
301294Seric **	Returns:
302294Seric **		none.
303294Seric **
304294Seric **	Side Effects:
305294Seric **		Error may be set.
306294Seric **		ExitStat may be set.
307294Seric **
308294Seric **	Requires:
309294Seric **		usrerr
310294Seric **		syserr
311294Seric **		flagset
312294Seric **		logmsg (sys)
313294Seric **
314294Seric **	Called By:
315294Seric **		deliver
316294Seric **
317294Seric **	History:
318294Seric **		2/18/80 -- broken from deliver.
319294Seric */
320294Seric 
321294Seric giveresponse(stat, force, m)
322294Seric 	int stat;
323294Seric 	int force;
324294Seric 	register struct mailer *m;
325294Seric {
326294Seric 	register char *statmsg;
327294Seric 	extern char *SysExMsg[];
328294Seric 	register int i;
329294Seric 	extern int N_SysEx;
330294Seric 
331294Seric 	i = stat - EX__BASE;
332294Seric 	if (i < 0 || i > N_SysEx)
333294Seric 		statmsg = NULL;
334294Seric 	else
335294Seric 		statmsg = SysExMsg[i];
336294Seric 	if (stat == 0)
337294Seric 		statmsg = "ok";
338294Seric 	else
339294Seric 	{
340294Seric 		Error++;
341294Seric 		if (statmsg == NULL && m->m_badstat != 0)
342294Seric 		{
343294Seric 			stat = m->m_badstat;
344294Seric 			i = stat - EX__BASE;
345294Seric # ifdef DEBUG
346294Seric 			if (i < 0 || i >= N_SysEx)
347294Seric 				syserr("Bad m_badstat %d", stat);
348294Seric 			else
349294Seric # endif DEBUG
350294Seric 			statmsg = SysExMsg[i];
351294Seric 		}
352294Seric 		if (statmsg == NULL)
353294Seric 			usrerr("unknown mailer response %d", stat);
354294Seric 		else if (force || !flagset(M_QUIET, m->m_flags))
355294Seric 			usrerr("%s", statmsg);
356294Seric 	}
357294Seric 
358294Seric 	/*
359294Seric 	**  Final cleanup.
360294Seric 	**	Log a record of the transaction.  Compute the new
361294Seric 	**	ExitStat -- if we already had an error, stick with
362294Seric 	**	that.
363294Seric 	*/
364294Seric 
365294Seric # ifdef LOG
366294Seric 	if (statmsg == NULL)
367294Seric 		logmsg(LOG_INFO, "%s->%s: error %d", From.q_paddr, To, stat);
368294Seric 	else
369294Seric 		logmsg(LOG_INFO, "%s->%s: %s", From.q_paddr, To, statmsg);
370294Seric # endif LOG
371294Seric 	if (ExitStat == EX_OK)
372294Seric 		ExitStat = stat;
373294Seric 	return (stat);
374294Seric }
375294Seric /*
376294Seric **  PUTHEADER -- insert the From header into some mail
377294Seric **
378294Seric **	For mailers such as 'msgs' that want the header inserted
379294Seric **	into the mail, this edit filter inserts the From line and
380294Seric **	then passes the rest of the message through.
381294Seric **
382294Seric **	Parameters:
383294Seric **		fp -- the file pointer for the output.
384294Seric **
385294Seric **	Returns:
386294Seric **		none
387294Seric **
388294Seric **	Side Effects:
389294Seric **		Puts a "From" line in UNIX format, and then
390294Seric **			outputs the rest of the message.
391294Seric **
392294Seric **	Requires:
393294Seric **		fprintf (sys)
394294Seric **		fgets (sys)
395294Seric **		fputs (sys)
396294Seric **		time (sys)
397294Seric **		ctime (sys)
398294Seric **		ferror (sys)
399294Seric **		syserr
400294Seric **		setstat
401294Seric **
402294Seric **	Called By:
403294Seric **		deliver
404294Seric **
405294Seric **	History:
406294Seric **		1/8/80 -- written.
407294Seric */
408294Seric 
409294Seric putheader(fp)
410294Seric 	register FILE *fp;
411294Seric {
412294Seric 	char buf[MAXLINE + 1];
413294Seric 	long tim;
414294Seric 	extern char *ctime();
415294Seric 
416294Seric 	time(&tim);
417294Seric 	fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim));
418294Seric 	while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp))
419294Seric 		fputs(buf, fp);
420294Seric 	if (ferror(fp))
421294Seric 	{
422294Seric 		syserr("putheader: write error");
423294Seric 		setstat(EX_IOERR);
424294Seric 	}
425294Seric }
426294Seric /*
427294Seric **  PIPESIG -- Handle broken pipe signals
428294Seric **
429294Seric **	This just logs an error.
430294Seric **
431294Seric **	Parameters:
432294Seric **		none
433294Seric **
434294Seric **	Returns:
435294Seric **		none
436294Seric **
437294Seric **	Side Effects:
438294Seric **		logs an error message.
439294Seric **
440294Seric **	Requires:
441294Seric **		syserr
442294Seric **
443294Seric **	History:
444294Seric **		1/17/80 -- written.
445294Seric */
446294Seric 
447294Seric pipesig()
448294Seric {
449294Seric 	syserr("Broken pipe");
450294Seric }
451294Seric /*
452294Seric **  SENDTO -- Designate a send list.
453294Seric **
454294Seric **	The parameter is a comma-separated list of people to send to.
455294Seric **	This routine arranges to send to all of them.
456294Seric **
457294Seric **	Parameters:
458294Seric **		list -- the send list.
459294Seric **		copyf -- the copy flag; passed to parse.
460294Seric **
461294Seric **	Returns:
462294Seric **		none
463294Seric **
464294Seric **	Side Effects:
465294Seric **		none.
466294Seric **
467294Seric **	Requires:
468294Seric **		parse
469294Seric **		recipient
470294Seric **
471294Seric **	Called By:
472294Seric **		main
473294Seric **		alias
474294Seric **
475294Seric **	History:
476294Seric **		1/11/80 -- written.
477294Seric */
478294Seric 
479294Seric sendto(list, copyf)
480294Seric 	char *list;
481294Seric 	int copyf;
482294Seric {
483294Seric 	register char *p;
484294Seric 	register char *q;
485294Seric 	register char c;
486294Seric 	addrq *a;
487294Seric 	extern addrq *parse();
488294Seric 	bool more;
489294Seric 
490294Seric 	/* more keeps track of what the previous delimiter was */
491294Seric 	more = TRUE;
492294Seric 	for (p = list; more; )
493294Seric 	{
494294Seric 		/* find the end of this address */
495294Seric 		q = p;
496294Seric 		while ((c = *p++) != '\0' && c != ',' && c != '\n')
497294Seric 			continue;
498294Seric 		more = c != '\0';
499294Seric 		*--p = '\0';
500294Seric 		if (more)
501294Seric 			p++;
502294Seric 
503294Seric 		/* parse the address */
504294Seric 		if ((a = parse(q, (addrq *) NULL, copyf)) == NULL)
505294Seric 			continue;
506294Seric 
507294Seric 		/* arrange to send to this person */
508294Seric 		recipient(a, &SendQ);
509294Seric 	}
510294Seric 	To = NULL;
511294Seric }
512294Seric /*
513294Seric **  RECIPIENT -- Designate a message recipient
514294Seric **
515294Seric **	Saves the named person for future mailing.
516294Seric **
517294Seric **	Designates a person as a recipient.  This routine
518294Seric **	does the initial parsing, and checks to see if
519294Seric **	this person has already received the mail.
520294Seric **	It also supresses local network names and turns them into
521294Seric **	local names.
522294Seric **
523294Seric **	Parameters:
524294Seric **		a -- the (preparsed) address header for the recipient.
525294Seric **		targetq -- the queue to add the name to.
526294Seric **
527294Seric **	Returns:
528294Seric **		none.
529294Seric **
530294Seric **	Side Effects:
531294Seric **		none.
532294Seric **
533294Seric **	Requires:
534294Seric **		sameaddr
535294Seric **		parse
536294Seric **		forward
537294Seric **		printf (sys)
538294Seric **		strcmp (sys)
539294Seric **		nxtinq
540294Seric **		putonq
541294Seric **
542294Seric **	Called By:
543294Seric **		sendto
544294Seric **		main
545294Seric **
546294Seric **	History:
547294Seric **		3/5/80 -- modified to know about new internal form
548294Seric **			for addresses.
549294Seric **		12/31/79 -- written.
550294Seric */
551294Seric 
552294Seric recipient(a, targetq)
553294Seric 	register addrq *a;
554294Seric 	addrq *targetq;
555294Seric {
556294Seric 	register addrq *q;
557294Seric 	register struct mailer *m;
558294Seric 	register char **pvp;
559294Seric 	extern char *xalloc();
560294Seric 	extern bool forward();
561294Seric 	extern int errno;
562294Seric 	extern bool sameaddr();
563294Seric 
564294Seric 	To = a->q_paddr;
565294Seric 	m = a->q_mailer;
566294Seric 	errno = 0;
567294Seric # ifdef DEBUG
568294Seric 	if (Debug)
569294Seric 		printf("recipient(%s)\n", To);
570294Seric # endif DEBUG
571294Seric 
572294Seric 	/*
573294Seric 	**  Don't go to the net if already on the target host.
574294Seric 	**	This is important on the berkeley network, since
575294Seric 	**	it get confused if we ask to send to ourselves.
576294Seric 	**	For nets like the ARPANET, we probably will have
577294Seric 	**	the local list set to NULL to simplify testing.
578294Seric 	**	The canonical representation of the name is also set
579294Seric 	**	to be just the local name so the duplicate letter
580294Seric 	**	suppression algorithm will work.
581294Seric 	*/
582294Seric 
583294Seric 	if ((pvp = m->m_local) != NULL)
584294Seric 	{
585294Seric 		while (*pvp != NULL)
586294Seric 		{
587294Seric 			if (strcmp(*pvp++, a->q_host) == 0)
588294Seric 			{
589294Seric 				a->q_mailer = m = &Mailer[0];
590294Seric 				break;
591294Seric 			}
592294Seric 		}
593294Seric 	}
594294Seric 
595294Seric 	/*
596294Seric 	**  Look up this person in the recipient list.  If they
597294Seric 	**  are there already, return, otherwise continue.
598294Seric 	*/
599294Seric 
600294Seric 	if (!ForceMail)
601294Seric 	{
602294Seric 		for (q = &SendQ; (q = nxtinq(q)) != NULL; )
603294Seric 			if (sameaddr(q, a, FALSE))
604294Seric 			{
605294Seric # ifdef DEBUG
606294Seric 				if (Debug)
607294Seric 					printf("(%s in SendQ)\n", a->q_paddr);
608294Seric # endif DEBUG
609294Seric 				return;
610294Seric 			}
611294Seric 		for (q = &AliasQ; (q = nxtinq(q)) != NULL; )
612294Seric 			if (sameaddr(q, a, FALSE))
613294Seric 			{
614294Seric # ifdef DEBUG
615294Seric 				if (Debug)
616294Seric 					printf("(%s in AliasQ)\n", a->q_paddr);
617294Seric # endif DEBUG
618294Seric 				return;
619294Seric 			}
620294Seric 	}
621294Seric 
622294Seric 	/*
623294Seric 	**  See if the user wants hir mail forwarded.
624294Seric 	**	`Forward' must do the forwarding recursively.
625294Seric 	*/
626294Seric 
627294Seric 	if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a))
628294Seric 		return;
629294Seric 
630294Seric 	/*
631294Seric 	**  Put the user onto the target queue.
632294Seric 	*/
633294Seric 
634294Seric 	if (targetq != NULL)
635294Seric 	{
636294Seric 		putonq(a, targetq);
637294Seric 	}
638294Seric 
639294Seric 	return;
640294Seric }
641294Seric /*
642294Seric **  BUILDARGV -- Build an argument vector for a mail server.
643294Seric **
644294Seric **	Using a template defined in config.c, an argv is built.
645294Seric **	The format of the template is already a vector.  The
646294Seric **	items of this vector are copied, unless a dollar sign
647294Seric **	is encountered.  In this case, the next character
648294Seric **	specifies something else to copy in.  These can be
649294Seric **		$f	The from address.
650294Seric **		$h	The host.
651294Seric **		$u	The user.
652294Seric **		$c	The hop count.
653294Seric **	The vector is built in a local buffer.  A pointer to
654294Seric **	the static argv is returned.
655294Seric **
656294Seric **	Parameters:
657294Seric **		tmplt -- a template for an argument vector.
658294Seric **		flags -- the flags for this server.
659294Seric **		host -- the host name to send to.
660294Seric **		user -- the user name to send to.
661294Seric **		from -- the person this mail is from.
662294Seric **
663294Seric **	Returns:
664294Seric **		A pointer to an argv.
665294Seric **
666294Seric **	Side Effects:
667294Seric **		none
668294Seric **
669294Seric **	WARNING:
670294Seric **		Since the argv is staticly allocated, any subsequent
671294Seric **		calls will clobber the old argv.
672294Seric **
673294Seric **	Requires:
674294Seric **		printf (sys)
675294Seric **		sprintf (sys)
676294Seric **		flagset
677294Seric **		syserr
678294Seric **
679294Seric **	Called By:
680294Seric **		deliver
681294Seric **
682294Seric **	History:
683294Seric **		12/26/79 -- written.
684294Seric */
685294Seric 
686294Seric char **
687294Seric buildargv(tmplt, flags, host, user, from)
688294Seric 	char **tmplt;
689294Seric 	int flags;
690294Seric 	char *host;
691294Seric 	char *user;
692294Seric 	char *from;
693294Seric {
694294Seric 	register char *p;
695294Seric 	register char *q;
696294Seric 	static char *pv[MAXPV+1];
697294Seric 	char **pvp;
698294Seric 	char **mvp;
699294Seric 	static char buf[512];
700294Seric 	register char *bp;
701294Seric 	char pbuf[30];
702294Seric 
703294Seric 	/*
704294Seric 	**  Do initial argv setup.
705294Seric 	**	Insert the mailer name.  Notice that $x expansion is
706294Seric 	**	NOT done on the mailer name.  Then, if the mailer has
707294Seric 	**	a picky -f flag, we insert it as appropriate.  This
708294Seric 	**	code does not check for 'pv' overflow; this places a
709294Seric 	**	manifest lower limit of 4 for MAXPV.
710294Seric 	*/
711294Seric 
712294Seric 	pvp = pv;
713294Seric 	bp = buf;
714294Seric 
715294Seric 	*pvp++ = tmplt[0];
716294Seric 
717294Seric 	/* insert -f or -r flag as appropriate */
718294Seric 	if (flagset(M_FOPT|M_ROPT, flags) && FromFlag)
719294Seric 	{
720294Seric 		if (flagset(M_FOPT, flags))
721294Seric 			*pvp++ = "-f";
722294Seric 		else
723294Seric 			*pvp++ = "-r";
724294Seric 		*pvp++ = From.q_paddr;
725294Seric 	}
726294Seric 
727294Seric 	/*
728294Seric 	**  Build the rest of argv.
729294Seric 	**	For each prototype parameter, the prototype is
730294Seric 	**	scanned character at a time.  If a dollar-sign is
731294Seric 	**	found, 'q' is set to the appropriate expansion,
732294Seric 	**	otherwise it is null.  Then either the string
733294Seric 	**	pointed to by q, or the original character, is
734294Seric 	**	interpolated into the buffer.  Buffer overflow is
735294Seric 	**	checked.
736294Seric 	*/
737294Seric 
738294Seric 	for (mvp = tmplt; (p = *++mvp) != NULL; )
739294Seric 	{
740294Seric 		if (pvp >= &pv[MAXPV])
741294Seric 		{
742294Seric 			syserr("Too many parameters to %s", pv[0]);
743294Seric 			return (NULL);
744294Seric 		}
745294Seric 		*pvp++ = bp;
746294Seric 		for (; *p != '\0'; p++)
747294Seric 		{
748294Seric 			/* q will be the interpolated quantity */
749294Seric 			q = NULL;
750294Seric 			if (*p == '$')
751294Seric 			{
752294Seric 				switch (*++p)
753294Seric 				{
754294Seric 				  case 'f':	/* from person */
755294Seric 					q = from;
756294Seric 					break;
757294Seric 
758294Seric 				  case 'u':	/* user */
759294Seric 					q = user;
760294Seric 					break;
761294Seric 
762294Seric 				  case 'h':	/* host */
763294Seric 					q = host;
764294Seric 					break;
765294Seric 
766294Seric 				  case 'c':	/* hop count */
767294Seric 					sprintf(pbuf, "%d", HopCount);
768294Seric 					q = pbuf;
769294Seric 					break;
770294Seric 				}
771294Seric 			}
772294Seric 
773294Seric 			/*
774294Seric 			**  Interpolate q or output one character
775294Seric 			**	Strip quote bits as we proceed.....
776294Seric 			*/
777294Seric 
778294Seric 			if (q != NULL)
779294Seric 			{
780294Seric 				while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0')
781294Seric 					continue;
782294Seric 				bp--;
783294Seric 			}
784294Seric 			else if (bp < &buf[sizeof buf - 1])
785294Seric 				*bp++ = *p;
786294Seric 		}
787294Seric 		*bp++ = '\0';
788294Seric 		if (bp >= &buf[sizeof buf - 1])
789294Seric 			return (NULL);
790294Seric 	}
791294Seric 	*pvp = NULL;
792294Seric 
793294Seric # ifdef DEBUG
794294Seric 	if (Debug)
795294Seric 	{
796294Seric 		printf("Interpolated argv is:\n");
797294Seric 		for (mvp = pv; *mvp != NULL; mvp++)
798294Seric 			printf("\t%s\n", *mvp);
799294Seric 	}
800294Seric # endif DEBUG
801294Seric 
802294Seric 	return (pv);
803294Seric }
804294Seric /*
805294Seric **  MAILFILE -- Send a message to a file.
806294Seric **
807294Seric **	Parameters:
808294Seric **		filename -- the name of the file to send to.
809294Seric **
810294Seric **	Returns:
811294Seric **		The exit code associated with the operation.
812294Seric **
813294Seric **	Side Effects:
814294Seric **		none.
815294Seric **
816294Seric **	Requires:
817294Seric **		fgets (sys)
818294Seric **		fputs (sys)
819294Seric **		fprintf (sys)
820294Seric **		fopen (sys)
821294Seric **		fclose (sys)
822294Seric **		ferror (sys)
823294Seric **		time (sys)
824294Seric **		ctime (sys)
825294Seric **		rewind (sys)
826294Seric **
827294Seric **	Called By:
828294Seric **		deliver
829294Seric **
830294Seric **	History:
831294Seric **		3/5/80 -- written.
832294Seric */
833294Seric 
834294Seric mailfile(filename)
835294Seric 	char *filename;
836294Seric {
837294Seric 	char buf[MAXLINE];
838294Seric 	register FILE *f;
839294Seric 	auto long tim;
840294Seric 	extern char *ctime();
841294Seric 
842294Seric 	f = fopen(filename, "a");
843294Seric 	if (f == NULL)
844294Seric 		return (EX_CANTCREAT);
845294Seric 
846294Seric 	/* output the timestamp */
847294Seric 	time(&tim);
848294Seric 	fprintf(f, "From %s %s", From.q_paddr, ctime(&tim));
849294Seric 	rewind(stdin);
850294Seric 	while (fgets(buf, sizeof buf, stdin) != NULL)
851294Seric 	{
852294Seric 		fputs(buf, f);
853294Seric 		if (ferror(f))
854294Seric 		{
855294Seric 			fclose(f);
856294Seric 			return (EX_IOERR);
857294Seric 		}
858294Seric 	}
859294Seric 	fputs("\n", f);
860294Seric 	fclose(f);
861294Seric 	return (EX_OK);
862294Seric }
863