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