1*294Seric # include <stdio.h>
2*294Seric # include <pwd.h>
3*294Seric # include <signal.h>
4*294Seric # include "dlvrmail.h"
5*294Seric # ifdef LOG
6*294Seric # include <log.h>
7*294Seric # endif LOG
8*294Seric 
9*294Seric /*
10*294Seric **  DELIVER -- Deliver a message to a particular address.
11*294Seric **
12*294Seric **	Algorithm:
13*294Seric **		Compute receiving network (i.e., mailer), host, & user.
14*294Seric **		If local, see if this is really a program name.
15*294Seric **		Build argument for the mailer.
16*294Seric **		Create pipe through edit fcn if appropriate.
17*294Seric **		Fork.
18*294Seric **			Child: call mailer
19*294Seric **		Parent: call editfcn if specified.
20*294Seric **		Wait for mailer to finish.
21*294Seric **		Interpret exit status.
22*294Seric **
23*294Seric **	Parameters:
24*294Seric **		to -- the address to deliver the message to.
25*294Seric **		editfcn -- if non-NULL, we want to call this function
26*294Seric **			to output the letter (instead of just out-
27*294Seric **			putting it raw).
28*294Seric **
29*294Seric **	Returns:
30*294Seric **		zero -- successfully delivered.
31*294Seric **		else -- some failure, see ExitStat for more info.
32*294Seric **
33*294Seric **	Side Effects:
34*294Seric **		The standard input is passed off to someone.
35*294Seric **
36*294Seric **	WARNING:
37*294Seric **		The standard input is shared amongst all children,
38*294Seric **		including the file pointer.  It is critical that the
39*294Seric **		parent waits for the child to finish before forking
40*294Seric **		another child.
41*294Seric **
42*294Seric **	Requires:
43*294Seric **		buildargv
44*294Seric **		giveresponse
45*294Seric **		fork (sys)
46*294Seric **		rewind (sys)
47*294Seric **		execv (sys)
48*294Seric **		exit (sys)
49*294Seric **		wait (sys)
50*294Seric **		syserr
51*294Seric **		getpwnam (sys)
52*294Seric **		endpwent (sys)
53*294Seric **		initlog
54*294Seric **		flagset
55*294Seric **		usrerr
56*294Seric **		pipe (sys)
57*294Seric **		close (sys)
58*294Seric **		dup (sys)
59*294Seric **		setuid (sys)
60*294Seric **		getuid (sys)
61*294Seric **		signal (sys)
62*294Seric **		fdopen (sys[v7] or conf.c[v6])
63*294Seric **		fclose (sys)
64*294Seric **		printf (sys)
65*294Seric **		stripquotes
66*294Seric **		mailfile
67*294Seric **		index (sys)
68*294Seric **
69*294Seric **	Called By:
70*294Seric **		main
71*294Seric **		savemail
72*294Seric **
73*294Seric **	Files:
74*294Seric **		standard input -- must be openned to the message to
75*294Seric **			deliver.
76*294Seric **
77*294Seric **	History:
78*294Seric **		3/5/80 -- modified rather extensively to change the
79*294Seric **			internal form of addresses.
80*294Seric **		12/26/79 -- written.
81*294Seric */
82*294Seric 
83*294Seric deliver(to, editfcn)
84*294Seric 	addrq *to;
85*294Seric 	int (*editfcn)();
86*294Seric {
87*294Seric 	register struct mailer *m;
88*294Seric 	char *host;
89*294Seric 	char *user;
90*294Seric 	extern struct passwd *getpwnam();
91*294Seric 	char **pvp;
92*294Seric 	extern char **buildargv();
93*294Seric 	auto int st;
94*294Seric 	register int i;
95*294Seric 	register char *p;
96*294Seric 	int pid;
97*294Seric 	int pvect[2];
98*294Seric 	extern FILE *fdopen();
99*294Seric 	extern int errno;
100*294Seric 	FILE *mfile;
101*294Seric 	extern putheader();
102*294Seric 	extern pipesig();
103*294Seric 
104*294Seric 	/*
105*294Seric 	**  Compute receiving mailer, host, and to addreses.
106*294Seric 	**	Do some initialization first.  To is the to address
107*294Seric 	**	for error messages.
108*294Seric 	*/
109*294Seric 
110*294Seric 	To = to->q_paddr;
111*294Seric 	m = to->q_mailer;
112*294Seric 	user = to->q_user;
113*294Seric 	host = to->q_host;
114*294Seric 	Error = 0;
115*294Seric 	errno = 0;
116*294Seric # ifdef DEBUG
117*294Seric 	if (Debug)
118*294Seric 		printf("deliver(%s [%d, `%s', `%s'])\n", To, m, host, user);
119*294Seric # endif DEBUG
120*294Seric 
121*294Seric 	/*
122*294Seric 	**  Remove quote bits from user/host.
123*294Seric 	*/
124*294Seric 
125*294Seric 	for (p = user; (*p++ &= 0177) != '\0'; )
126*294Seric 		continue;
127*294Seric 	if (host != NULL)
128*294Seric 		for (p = host; (*p++ &= 0177) != '\0'; )
129*294Seric 			continue;
130*294Seric 
131*294Seric 	/*
132*294Seric 	**  Strip quote bits from names if the mailer wants it.
133*294Seric 	*/
134*294Seric 
135*294Seric 	if (flagset(M_STRIPQ, m->m_flags))
136*294Seric 	{
137*294Seric 		stripquotes(user);
138*294Seric 		stripquotes(host);
139*294Seric 	}
140*294Seric 
141*294Seric 	/*
142*294Seric 	**  See if this user name is "special".
143*294Seric 	**	If the user is a program, diddle with the mailer spec.
144*294Seric 	**	If the user name has a slash in it, assume that this
145*294Seric 	**		is a file -- send it off without further ado.
146*294Seric 	**		Note that this means that editfcn's will not
147*294Seric 	**		be applied to the message.
148*294Seric 	*/
149*294Seric 
150*294Seric 	if (m == &Mailer[0])
151*294Seric 	{
152*294Seric 		if (*user == '|')
153*294Seric 		{
154*294Seric 			user++;
155*294Seric 			m = &Mailer[1];
156*294Seric 		}
157*294Seric 		else
158*294Seric 		{
159*294Seric 			if (index(user, '/') != NULL)
160*294Seric 			{
161*294Seric 				i = mailfile(user);
162*294Seric 				giveresponse(i, TRUE, m);
163*294Seric 				return (i);
164*294Seric 			}
165*294Seric 		}
166*294Seric 	}
167*294Seric 
168*294Seric # ifdef BADMAIL
169*294Seric 	/*
170*294Seric 	**  If the mailer doesn't return the proper
171*294Seric 	**  exit statuses, check here to see if the
172*294Seric 	**  user exists so that we can give a pretty
173*294Seric 	**  error message.
174*294Seric 	*/
175*294Seric 
176*294Seric 	if (m == &Mailer[0])
177*294Seric 	{
178*294Seric 		if (getpwnam(user) == NULL)
179*294Seric 		{
180*294Seric 			giveresponse(EX_NOUSER, TRUE, m);
181*294Seric 			return (EX_NOUSER);
182*294Seric 		}
183*294Seric 	}
184*294Seric # endif BADMAIL
185*294Seric 
186*294Seric 	/*
187*294Seric 	**  If the mailer wants a From line, insert a new editfcn.
188*294Seric 	*/
189*294Seric 
190*294Seric 	if (flagset(M_HDR, m->m_flags) && editfcn == NULL)
191*294Seric 		editfcn = putheader;
192*294Seric 
193*294Seric 	/*
194*294Seric 	**  Call the mailer.
195*294Seric 	**	The argument vector gets built, pipes through 'editfcn'
196*294Seric 	**	are created as necessary, and we fork & exec as
197*294Seric 	**	appropriate.  In the parent, we call 'editfcn'.
198*294Seric 	*/
199*294Seric 
200*294Seric 	pvp = buildargv(m->m_argv, m->m_flags, host, user, From.q_paddr);
201*294Seric 	if (pvp == NULL)
202*294Seric 	{
203*294Seric 		usrerr("name too long");
204*294Seric 		return (-1);
205*294Seric 	}
206*294Seric 	rewind(stdin);
207*294Seric 
208*294Seric 	/* create a pipe if we will need one */
209*294Seric 	if (editfcn != NULL && pipe(pvect) < 0)
210*294Seric 	{
211*294Seric 		syserr("pipe");
212*294Seric 		return (-1);
213*294Seric 	}
214*294Seric 	pid = fork();
215*294Seric 	if (pid < 0)
216*294Seric 	{
217*294Seric 		syserr("Cannot fork");
218*294Seric 		if (editfcn != NULL)
219*294Seric 		{
220*294Seric 			close(pvect[0]);
221*294Seric 			close(pvect[1]);
222*294Seric 		}
223*294Seric 		return (-1);
224*294Seric 	}
225*294Seric 	else if (pid == 0)
226*294Seric 	{
227*294Seric 		/* child -- set up input & exec mailer */
228*294Seric 		signal(SIGINT, SIG_IGN);
229*294Seric 		if (editfcn != NULL)
230*294Seric 		{
231*294Seric 			close(0);
232*294Seric 			if (dup(pvect[0]) < 0)
233*294Seric 			{
234*294Seric 				syserr("Cannot dup to zero!");
235*294Seric 				exit(EX_OSERR);
236*294Seric 			}
237*294Seric 			close(pvect[0]);
238*294Seric 			close(pvect[1]);
239*294Seric 		}
240*294Seric 		if (!flagset(M_RESTR, m->m_flags))
241*294Seric 			setuid(getuid());
242*294Seric # ifdef LOG
243*294Seric 		initlog(NULL, 0, LOG_CLOSE);
244*294Seric # endif LOG
245*294Seric 		endpwent();
246*294Seric 		execv(m->m_mailer, pvp);
247*294Seric 		/* syserr fails because log is closed */
248*294Seric 		/* syserr("Cannot exec %s", m->m_mailer); */
249*294Seric 		exit(EX_UNAVAIL);
250*294Seric 	}
251*294Seric 
252*294Seric 	/* arrange to write out header message if error */
253*294Seric 	if (editfcn != NULL)
254*294Seric 	{
255*294Seric 		close(pvect[0]);
256*294Seric 		signal(SIGPIPE, pipesig);
257*294Seric 		mfile = fdopen(pvect[1], "w");
258*294Seric 		(*editfcn)(mfile);
259*294Seric 		fclose(mfile);
260*294Seric 	}
261*294Seric 
262*294Seric 	/*
263*294Seric 	**  Wait for child to die and report status.
264*294Seric 	**	We should never get fatal errors (e.g., segmentation
265*294Seric 	**	violation), so we report those specially.  For other
266*294Seric 	**	errors, we choose a status message (into statmsg),
267*294Seric 	**	and if it represents an error, we print it.
268*294Seric 	*/
269*294Seric 
270*294Seric 	while ((i = wait(&st)) > 0 && i != pid)
271*294Seric 		continue;
272*294Seric 	if (i < 0)
273*294Seric 	{
274*294Seric 		syserr("wait");
275*294Seric 		return (-1);
276*294Seric 	}
277*294Seric 	if ((st & 0377) != 0)
278*294Seric 	{
279*294Seric 		syserr("%s: stat %o", pvp[0], st);
280*294Seric 		ExitStat = EX_UNAVAIL;
281*294Seric 		return (-1);
282*294Seric 	}
283*294Seric 	i = (st >> 8) & 0377;
284*294Seric 	giveresponse(i, FALSE, m);
285*294Seric 	return (i);
286*294Seric }
287*294Seric /*
288*294Seric **  GIVERESPONSE -- Interpret an error response from a mailer
289*294Seric **
290*294Seric **	Parameters:
291*294Seric **		stat -- the status code from the mailer (high byte
292*294Seric **			only; core dumps must have been taken care of
293*294Seric **			already).
294*294Seric **		force -- if set, force an error message output, even
295*294Seric **			if the mailer seems to like to print its own
296*294Seric **			messages.
297*294Seric **		m -- the mailer descriptor for this mailer.
298*294Seric **
299*294Seric **	Returns:
300*294Seric **		none.
301*294Seric **
302*294Seric **	Side Effects:
303*294Seric **		Error may be set.
304*294Seric **		ExitStat may be set.
305*294Seric **
306*294Seric **	Requires:
307*294Seric **		usrerr
308*294Seric **		syserr
309*294Seric **		flagset
310*294Seric **		logmsg (sys)
311*294Seric **
312*294Seric **	Called By:
313*294Seric **		deliver
314*294Seric **
315*294Seric **	History:
316*294Seric **		2/18/80 -- broken from deliver.
317*294Seric */
318*294Seric 
319*294Seric giveresponse(stat, force, m)
320*294Seric 	int stat;
321*294Seric 	int force;
322*294Seric 	register struct mailer *m;
323*294Seric {
324*294Seric 	register char *statmsg;
325*294Seric 	extern char *SysExMsg[];
326*294Seric 	register int i;
327*294Seric 	extern int N_SysEx;
328*294Seric 
329*294Seric 	i = stat - EX__BASE;
330*294Seric 	if (i < 0 || i > N_SysEx)
331*294Seric 		statmsg = NULL;
332*294Seric 	else
333*294Seric 		statmsg = SysExMsg[i];
334*294Seric 	if (stat == 0)
335*294Seric 		statmsg = "ok";
336*294Seric 	else
337*294Seric 	{
338*294Seric 		Error++;
339*294Seric 		if (statmsg == NULL && m->m_badstat != 0)
340*294Seric 		{
341*294Seric 			stat = m->m_badstat;
342*294Seric 			i = stat - EX__BASE;
343*294Seric # ifdef DEBUG
344*294Seric 			if (i < 0 || i >= N_SysEx)
345*294Seric 				syserr("Bad m_badstat %d", stat);
346*294Seric 			else
347*294Seric # endif DEBUG
348*294Seric 			statmsg = SysExMsg[i];
349*294Seric 		}
350*294Seric 		if (statmsg == NULL)
351*294Seric 			usrerr("unknown mailer response %d", stat);
352*294Seric 		else if (force || !flagset(M_QUIET, m->m_flags))
353*294Seric 			usrerr("%s", statmsg);
354*294Seric 	}
355*294Seric 
356*294Seric 	/*
357*294Seric 	**  Final cleanup.
358*294Seric 	**	Log a record of the transaction.  Compute the new
359*294Seric 	**	ExitStat -- if we already had an error, stick with
360*294Seric 	**	that.
361*294Seric 	*/
362*294Seric 
363*294Seric # ifdef LOG
364*294Seric 	if (statmsg == NULL)
365*294Seric 		logmsg(LOG_INFO, "%s->%s: error %d", From.q_paddr, To, stat);
366*294Seric 	else
367*294Seric 		logmsg(LOG_INFO, "%s->%s: %s", From.q_paddr, To, statmsg);
368*294Seric # endif LOG
369*294Seric 	if (ExitStat == EX_OK)
370*294Seric 		ExitStat = stat;
371*294Seric 	return (stat);
372*294Seric }
373*294Seric /*
374*294Seric **  PUTHEADER -- insert the From header into some mail
375*294Seric **
376*294Seric **	For mailers such as 'msgs' that want the header inserted
377*294Seric **	into the mail, this edit filter inserts the From line and
378*294Seric **	then passes the rest of the message through.
379*294Seric **
380*294Seric **	Parameters:
381*294Seric **		fp -- the file pointer for the output.
382*294Seric **
383*294Seric **	Returns:
384*294Seric **		none
385*294Seric **
386*294Seric **	Side Effects:
387*294Seric **		Puts a "From" line in UNIX format, and then
388*294Seric **			outputs the rest of the message.
389*294Seric **
390*294Seric **	Requires:
391*294Seric **		fprintf (sys)
392*294Seric **		fgets (sys)
393*294Seric **		fputs (sys)
394*294Seric **		time (sys)
395*294Seric **		ctime (sys)
396*294Seric **		ferror (sys)
397*294Seric **		syserr
398*294Seric **		setstat
399*294Seric **
400*294Seric **	Called By:
401*294Seric **		deliver
402*294Seric **
403*294Seric **	History:
404*294Seric **		1/8/80 -- written.
405*294Seric */
406*294Seric 
407*294Seric putheader(fp)
408*294Seric 	register FILE *fp;
409*294Seric {
410*294Seric 	char buf[MAXLINE + 1];
411*294Seric 	long tim;
412*294Seric 	extern char *ctime();
413*294Seric 
414*294Seric 	time(&tim);
415*294Seric 	fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim));
416*294Seric 	while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp))
417*294Seric 		fputs(buf, fp);
418*294Seric 	if (ferror(fp))
419*294Seric 	{
420*294Seric 		syserr("putheader: write error");
421*294Seric 		setstat(EX_IOERR);
422*294Seric 	}
423*294Seric }
424*294Seric /*
425*294Seric **  PIPESIG -- Handle broken pipe signals
426*294Seric **
427*294Seric **	This just logs an error.
428*294Seric **
429*294Seric **	Parameters:
430*294Seric **		none
431*294Seric **
432*294Seric **	Returns:
433*294Seric **		none
434*294Seric **
435*294Seric **	Side Effects:
436*294Seric **		logs an error message.
437*294Seric **
438*294Seric **	Requires:
439*294Seric **		syserr
440*294Seric **
441*294Seric **	History:
442*294Seric **		1/17/80 -- written.
443*294Seric */
444*294Seric 
445*294Seric pipesig()
446*294Seric {
447*294Seric 	syserr("Broken pipe");
448*294Seric }
449*294Seric /*
450*294Seric **  SENDTO -- Designate a send list.
451*294Seric **
452*294Seric **	The parameter is a comma-separated list of people to send to.
453*294Seric **	This routine arranges to send to all of them.
454*294Seric **
455*294Seric **	Parameters:
456*294Seric **		list -- the send list.
457*294Seric **		copyf -- the copy flag; passed to parse.
458*294Seric **
459*294Seric **	Returns:
460*294Seric **		none
461*294Seric **
462*294Seric **	Side Effects:
463*294Seric **		none.
464*294Seric **
465*294Seric **	Requires:
466*294Seric **		parse
467*294Seric **		recipient
468*294Seric **
469*294Seric **	Called By:
470*294Seric **		main
471*294Seric **		alias
472*294Seric **
473*294Seric **	History:
474*294Seric **		1/11/80 -- written.
475*294Seric */
476*294Seric 
477*294Seric sendto(list, copyf)
478*294Seric 	char *list;
479*294Seric 	int copyf;
480*294Seric {
481*294Seric 	register char *p;
482*294Seric 	register char *q;
483*294Seric 	register char c;
484*294Seric 	addrq *a;
485*294Seric 	extern addrq *parse();
486*294Seric 	bool more;
487*294Seric 
488*294Seric 	/* more keeps track of what the previous delimiter was */
489*294Seric 	more = TRUE;
490*294Seric 	for (p = list; more; )
491*294Seric 	{
492*294Seric 		/* find the end of this address */
493*294Seric 		q = p;
494*294Seric 		while ((c = *p++) != '\0' && c != ',' && c != '\n')
495*294Seric 			continue;
496*294Seric 		more = c != '\0';
497*294Seric 		*--p = '\0';
498*294Seric 		if (more)
499*294Seric 			p++;
500*294Seric 
501*294Seric 		/* parse the address */
502*294Seric 		if ((a = parse(q, (addrq *) NULL, copyf)) == NULL)
503*294Seric 			continue;
504*294Seric 
505*294Seric 		/* arrange to send to this person */
506*294Seric 		recipient(a, &SendQ);
507*294Seric 	}
508*294Seric 	To = NULL;
509*294Seric }
510*294Seric /*
511*294Seric **  RECIPIENT -- Designate a message recipient
512*294Seric **
513*294Seric **	Saves the named person for future mailing.
514*294Seric **
515*294Seric **	Designates a person as a recipient.  This routine
516*294Seric **	does the initial parsing, and checks to see if
517*294Seric **	this person has already received the mail.
518*294Seric **	It also supresses local network names and turns them into
519*294Seric **	local names.
520*294Seric **
521*294Seric **	Parameters:
522*294Seric **		a -- the (preparsed) address header for the recipient.
523*294Seric **		targetq -- the queue to add the name to.
524*294Seric **
525*294Seric **	Returns:
526*294Seric **		none.
527*294Seric **
528*294Seric **	Side Effects:
529*294Seric **		none.
530*294Seric **
531*294Seric **	Requires:
532*294Seric **		sameaddr
533*294Seric **		parse
534*294Seric **		forward
535*294Seric **		printf (sys)
536*294Seric **		strcmp (sys)
537*294Seric **		nxtinq
538*294Seric **		putonq
539*294Seric **
540*294Seric **	Called By:
541*294Seric **		sendto
542*294Seric **		main
543*294Seric **
544*294Seric **	History:
545*294Seric **		3/5/80 -- modified to know about new internal form
546*294Seric **			for addresses.
547*294Seric **		12/31/79 -- written.
548*294Seric */
549*294Seric 
550*294Seric recipient(a, targetq)
551*294Seric 	register addrq *a;
552*294Seric 	addrq *targetq;
553*294Seric {
554*294Seric 	register addrq *q;
555*294Seric 	register struct mailer *m;
556*294Seric 	register char **pvp;
557*294Seric 	extern char *xalloc();
558*294Seric 	extern bool forward();
559*294Seric 	extern int errno;
560*294Seric 	extern bool sameaddr();
561*294Seric 
562*294Seric 	To = a->q_paddr;
563*294Seric 	m = a->q_mailer;
564*294Seric 	errno = 0;
565*294Seric # ifdef DEBUG
566*294Seric 	if (Debug)
567*294Seric 		printf("recipient(%s)\n", To);
568*294Seric # endif DEBUG
569*294Seric 
570*294Seric 	/*
571*294Seric 	**  Don't go to the net if already on the target host.
572*294Seric 	**	This is important on the berkeley network, since
573*294Seric 	**	it get confused if we ask to send to ourselves.
574*294Seric 	**	For nets like the ARPANET, we probably will have
575*294Seric 	**	the local list set to NULL to simplify testing.
576*294Seric 	**	The canonical representation of the name is also set
577*294Seric 	**	to be just the local name so the duplicate letter
578*294Seric 	**	suppression algorithm will work.
579*294Seric 	*/
580*294Seric 
581*294Seric 	if ((pvp = m->m_local) != NULL)
582*294Seric 	{
583*294Seric 		while (*pvp != NULL)
584*294Seric 		{
585*294Seric 			if (strcmp(*pvp++, a->q_host) == 0)
586*294Seric 			{
587*294Seric 				a->q_mailer = m = &Mailer[0];
588*294Seric 				break;
589*294Seric 			}
590*294Seric 		}
591*294Seric 	}
592*294Seric 
593*294Seric 	/*
594*294Seric 	**  Look up this person in the recipient list.  If they
595*294Seric 	**  are there already, return, otherwise continue.
596*294Seric 	*/
597*294Seric 
598*294Seric 	if (!ForceMail)
599*294Seric 	{
600*294Seric 		for (q = &SendQ; (q = nxtinq(q)) != NULL; )
601*294Seric 			if (sameaddr(q, a, FALSE))
602*294Seric 			{
603*294Seric # ifdef DEBUG
604*294Seric 				if (Debug)
605*294Seric 					printf("(%s in SendQ)\n", a->q_paddr);
606*294Seric # endif DEBUG
607*294Seric 				return;
608*294Seric 			}
609*294Seric 		for (q = &AliasQ; (q = nxtinq(q)) != NULL; )
610*294Seric 			if (sameaddr(q, a, FALSE))
611*294Seric 			{
612*294Seric # ifdef DEBUG
613*294Seric 				if (Debug)
614*294Seric 					printf("(%s in AliasQ)\n", a->q_paddr);
615*294Seric # endif DEBUG
616*294Seric 				return;
617*294Seric 			}
618*294Seric 	}
619*294Seric 
620*294Seric 	/*
621*294Seric 	**  See if the user wants hir mail forwarded.
622*294Seric 	**	`Forward' must do the forwarding recursively.
623*294Seric 	*/
624*294Seric 
625*294Seric 	if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a))
626*294Seric 		return;
627*294Seric 
628*294Seric 	/*
629*294Seric 	**  Put the user onto the target queue.
630*294Seric 	*/
631*294Seric 
632*294Seric 	if (targetq != NULL)
633*294Seric 	{
634*294Seric 		putonq(a, targetq);
635*294Seric 	}
636*294Seric 
637*294Seric 	return;
638*294Seric }
639*294Seric /*
640*294Seric **  BUILDARGV -- Build an argument vector for a mail server.
641*294Seric **
642*294Seric **	Using a template defined in config.c, an argv is built.
643*294Seric **	The format of the template is already a vector.  The
644*294Seric **	items of this vector are copied, unless a dollar sign
645*294Seric **	is encountered.  In this case, the next character
646*294Seric **	specifies something else to copy in.  These can be
647*294Seric **		$f	The from address.
648*294Seric **		$h	The host.
649*294Seric **		$u	The user.
650*294Seric **		$c	The hop count.
651*294Seric **	The vector is built in a local buffer.  A pointer to
652*294Seric **	the static argv is returned.
653*294Seric **
654*294Seric **	Parameters:
655*294Seric **		tmplt -- a template for an argument vector.
656*294Seric **		flags -- the flags for this server.
657*294Seric **		host -- the host name to send to.
658*294Seric **		user -- the user name to send to.
659*294Seric **		from -- the person this mail is from.
660*294Seric **
661*294Seric **	Returns:
662*294Seric **		A pointer to an argv.
663*294Seric **
664*294Seric **	Side Effects:
665*294Seric **		none
666*294Seric **
667*294Seric **	WARNING:
668*294Seric **		Since the argv is staticly allocated, any subsequent
669*294Seric **		calls will clobber the old argv.
670*294Seric **
671*294Seric **	Requires:
672*294Seric **		printf (sys)
673*294Seric **		sprintf (sys)
674*294Seric **		flagset
675*294Seric **		syserr
676*294Seric **
677*294Seric **	Called By:
678*294Seric **		deliver
679*294Seric **
680*294Seric **	History:
681*294Seric **		12/26/79 -- written.
682*294Seric */
683*294Seric 
684*294Seric char **
685*294Seric buildargv(tmplt, flags, host, user, from)
686*294Seric 	char **tmplt;
687*294Seric 	int flags;
688*294Seric 	char *host;
689*294Seric 	char *user;
690*294Seric 	char *from;
691*294Seric {
692*294Seric 	register char *p;
693*294Seric 	register char *q;
694*294Seric 	static char *pv[MAXPV+1];
695*294Seric 	char **pvp;
696*294Seric 	char **mvp;
697*294Seric 	static char buf[512];
698*294Seric 	register char *bp;
699*294Seric 	char pbuf[30];
700*294Seric 
701*294Seric 	/*
702*294Seric 	**  Do initial argv setup.
703*294Seric 	**	Insert the mailer name.  Notice that $x expansion is
704*294Seric 	**	NOT done on the mailer name.  Then, if the mailer has
705*294Seric 	**	a picky -f flag, we insert it as appropriate.  This
706*294Seric 	**	code does not check for 'pv' overflow; this places a
707*294Seric 	**	manifest lower limit of 4 for MAXPV.
708*294Seric 	*/
709*294Seric 
710*294Seric 	pvp = pv;
711*294Seric 	bp = buf;
712*294Seric 
713*294Seric 	*pvp++ = tmplt[0];
714*294Seric 
715*294Seric 	/* insert -f or -r flag as appropriate */
716*294Seric 	if (flagset(M_FOPT|M_ROPT, flags) && FromFlag)
717*294Seric 	{
718*294Seric 		if (flagset(M_FOPT, flags))
719*294Seric 			*pvp++ = "-f";
720*294Seric 		else
721*294Seric 			*pvp++ = "-r";
722*294Seric 		*pvp++ = From.q_paddr;
723*294Seric 	}
724*294Seric 
725*294Seric 	/*
726*294Seric 	**  Build the rest of argv.
727*294Seric 	**	For each prototype parameter, the prototype is
728*294Seric 	**	scanned character at a time.  If a dollar-sign is
729*294Seric 	**	found, 'q' is set to the appropriate expansion,
730*294Seric 	**	otherwise it is null.  Then either the string
731*294Seric 	**	pointed to by q, or the original character, is
732*294Seric 	**	interpolated into the buffer.  Buffer overflow is
733*294Seric 	**	checked.
734*294Seric 	*/
735*294Seric 
736*294Seric 	for (mvp = tmplt; (p = *++mvp) != NULL; )
737*294Seric 	{
738*294Seric 		if (pvp >= &pv[MAXPV])
739*294Seric 		{
740*294Seric 			syserr("Too many parameters to %s", pv[0]);
741*294Seric 			return (NULL);
742*294Seric 		}
743*294Seric 		*pvp++ = bp;
744*294Seric 		for (; *p != '\0'; p++)
745*294Seric 		{
746*294Seric 			/* q will be the interpolated quantity */
747*294Seric 			q = NULL;
748*294Seric 			if (*p == '$')
749*294Seric 			{
750*294Seric 				switch (*++p)
751*294Seric 				{
752*294Seric 				  case 'f':	/* from person */
753*294Seric 					q = from;
754*294Seric 					break;
755*294Seric 
756*294Seric 				  case 'u':	/* user */
757*294Seric 					q = user;
758*294Seric 					break;
759*294Seric 
760*294Seric 				  case 'h':	/* host */
761*294Seric 					q = host;
762*294Seric 					break;
763*294Seric 
764*294Seric 				  case 'c':	/* hop count */
765*294Seric 					sprintf(pbuf, "%d", HopCount);
766*294Seric 					q = pbuf;
767*294Seric 					break;
768*294Seric 				}
769*294Seric 			}
770*294Seric 
771*294Seric 			/*
772*294Seric 			**  Interpolate q or output one character
773*294Seric 			**	Strip quote bits as we proceed.....
774*294Seric 			*/
775*294Seric 
776*294Seric 			if (q != NULL)
777*294Seric 			{
778*294Seric 				while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0')
779*294Seric 					continue;
780*294Seric 				bp--;
781*294Seric 				if (q[-2] == '"')
782*294Seric 					bp--;
783*294Seric 			}
784*294Seric 			else if (bp < &buf[sizeof buf - 1])
785*294Seric 				*bp++ = *p;
786*294Seric 		}
787*294Seric 		*bp++ = '\0';
788*294Seric 		if (bp >= &buf[sizeof buf - 1])
789*294Seric 			return (NULL);
790*294Seric 	}
791*294Seric 	*pvp = NULL;
792*294Seric 
793*294Seric # ifdef DEBUG
794*294Seric 	if (Debug)
795*294Seric 	{
796*294Seric 		printf("Interpolated argv is:\n");
797*294Seric 		for (mvp = pv; *mvp != NULL; mvp++)
798*294Seric 			printf("\t%s\n", *mvp);
799*294Seric 	}
800*294Seric # endif DEBUG
801*294Seric 
802*294Seric 	return (pv);
803*294Seric }
804*294Seric /*
805*294Seric **  MAILFILE -- Send a message to a file.
806*294Seric **
807*294Seric **	Parameters:
808*294Seric **		filename -- the name of the file to send to.
809*294Seric **
810*294Seric **	Returns:
811*294Seric **		The exit code associated with the operation.
812*294Seric **
813*294Seric **	Side Effects:
814*294Seric **		none.
815*294Seric **
816*294Seric **	Requires:
817*294Seric **		fgets (sys)
818*294Seric **		fputs (sys)
819*294Seric **		fprintf (sys)
820*294Seric **		fopen (sys)
821*294Seric **		fclose (sys)
822*294Seric **		ferror (sys)
823*294Seric **		time (sys)
824*294Seric **		ctime (sys)
825*294Seric **		rewind (sys)
826*294Seric **
827*294Seric **	Called By:
828*294Seric **		deliver
829*294Seric **
830*294Seric **	History:
831*294Seric **		3/5/80 -- written.
832*294Seric */
833*294Seric 
834*294Seric mailfile(filename)
835*294Seric 	char *filename;
836*294Seric {
837*294Seric 	char buf[MAXLINE];
838*294Seric 	register FILE *f;
839*294Seric 	auto long tim;
840*294Seric 	extern char *ctime();
841*294Seric 
842*294Seric 	f = fopen(filename, "a");
843*294Seric 	if (f == NULL)
844*294Seric 		return (EX_CANTCREAT);
845*294Seric 
846*294Seric 	/* output the timestamp */
847*294Seric 	time(&tim);
848*294Seric 	fprintf(f, "From %s %s", From.q_paddr, ctime(&tim));
849*294Seric 	rewind(stdin);
850*294Seric 	while (fgets(buf, sizeof buf, stdin) != NULL)
851*294Seric 	{
852*294Seric 		fputs(buf, f);
853*294Seric 		if (ferror(f))
854*294Seric 		{
855*294Seric 			fclose(f);
856*294Seric 			return (EX_IOERR);
857*294Seric 		}
858*294Seric 	}
859*294Seric 	fputs("\n", f);
860*294Seric 	fclose(f);
861*294Seric 	return (EX_OK);
862*294Seric }
863