1 # include "sendmail.h"
2 # include <sys/stat.h>
3 # include <dir.h>
4 # include <signal.h>
5 # include <errno.h>
6 
7 # ifndef QUEUE
8 SCCSID(@(#)queue.c	3.53		11/24/82	(no queueing));
9 # else QUEUE
10 
11 SCCSID(@(#)queue.c	3.53		11/24/82);
12 
13 /*
14 **  QUEUEUP -- queue a message up for future transmission.
15 **
16 **	The queued message should already be in the correct place.
17 **	This routine just outputs the control file as appropriate.
18 **
19 **	Parameters:
20 **		e -- the envelope to queue up.
21 **		queueall -- if TRUE, queue all addresses, rather than
22 **			just those with the QQUEUEUP flag set.
23 **
24 **	Returns:
25 **		none.
26 **
27 **	Side Effects:
28 **		The current request (only unsatisfied addresses)
29 **			are saved in a control file.
30 */
31 
32 queueup(e, queueall)
33 	register ENVELOPE *e;
34 	bool queueall;
35 {
36 	char *tf;
37 	char *qf;
38 	char buf[MAXLINE];
39 	register FILE *tfp;
40 	register HDR *h;
41 	register ADDRESS *q;
42 
43 	/*
44 	**  Create control file.
45 	*/
46 
47 	tf = newstr(queuename(e, 't'));
48 	tfp = fopen(tf, "w");
49 	if (tfp == NULL)
50 	{
51 		syserr("queueup: cannot create temp file %s", tf);
52 		return;
53 	}
54 	(void) chmod(tf, FileMode);
55 
56 # ifdef DEBUG
57 	if (tTd(40, 1))
58 		printf("queueing in %s\n", tf);
59 # endif DEBUG
60 
61 	/*
62 	**  If there is no data file yet, create one.
63 	*/
64 
65 	if (e->e_df == NULL)
66 	{
67 		register FILE *dfp;
68 
69 		e->e_df = newstr(queuename(e, 'd'));
70 		dfp = fopen(e->e_df, "w");
71 		if (dfp == NULL)
72 		{
73 			syserr("queueup: cannot create %s", e->e_df);
74 			(void) fclose(tfp);
75 			return;
76 		}
77 		(void) chmod(e->e_df, FileMode);
78 		(*e->e_putbody)(dfp, Mailer[1], FALSE);
79 		(void) fclose(dfp);
80 	}
81 
82 	/*
83 	**  Output future work requests.
84 	*/
85 
86 	/* output name of data file */
87 	fprintf(tfp, "D%s\n", e->e_df);
88 
89 	/* output name of sender */
90 	fprintf(tfp, "S%s\n", e->e_from.q_paddr);
91 
92 	/* output creation time */
93 	fprintf(tfp, "T%ld\n", e->e_ctime);
94 
95 	/* output message priority */
96 	fprintf(tfp, "P%ld\n", e->e_msgpriority);
97 
98 	/* output message class */
99 	fprintf(tfp, "C%d\n", e->e_class);
100 
101 	/* output list of recipient addresses */
102 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
103 	{
104 # ifdef DEBUG
105 		if (tTd(40, 1))
106 		{
107 			printf("queueing ");
108 			printaddr(q, FALSE);
109 		}
110 # endif DEBUG
111 		if (queueall ? !bitset(QDONTSEND, q->q_flags) :
112 			       bitset(QQUEUEUP, q->q_flags))
113 		{
114 			fprintf(tfp, "R%s\n", q->q_paddr);
115 		}
116 	}
117 
118 	/* output headers for this message */
119 	define('g', "$f");
120 	for (h = e->e_header; h != NULL; h = h->h_link)
121 	{
122 		if (h->h_value == NULL || h->h_value[0] == '\0')
123 			continue;
124 		fprintf(tfp, "H");
125 		if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags))
126 			mfdecode(h->h_mflags, tfp);
127 		if (bitset(H_DEFAULT, h->h_flags))
128 		{
129 			(void) expand(h->h_value, buf, &buf[sizeof buf], e);
130 			fprintf(tfp, "%s: %s\n", h->h_field, buf);
131 		}
132 		else if (bitset(H_FROM|H_RCPT, h->h_flags))
133 		{
134 			commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags),
135 				 (MAILER *) NULL);
136 		}
137 		else
138 			fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
139 	}
140 
141 	/*
142 	**  Clean up.
143 	*/
144 
145 	(void) fclose(tfp);
146 	qf = queuename(e, 'q');
147 	(void) unlink(qf);
148 	if (link(tf, qf) < 0)
149 		syserr("cannot link(%s, %s), df=%s", tf, qf, e->e_df);
150 	else
151 		(void) unlink(tf);
152 
153 # ifdef LOG
154 	/* save log info */
155 	if (LogLevel > 15)
156 		syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
157 # endif LOG
158 }
159 /*
160 **  RUNQUEUE -- run the jobs in the queue.
161 **
162 **	Gets the stuff out of the queue in some presumably logical
163 **	order and processes them.
164 **
165 **	Parameters:
166 **		none.
167 **
168 **	Returns:
169 **		none.
170 **
171 **	Side Effects:
172 **		runs things in the mail queue.
173 */
174 
175 runqueue(forkflag)
176 	bool forkflag;
177 {
178 	register int i;
179 
180 	/*
181 	**  See if we want to go off and do other useful work.
182 	*/
183 
184 	if (forkflag)
185 	{
186 		int pid;
187 
188 		pid = dofork();
189 		if (pid != 0)
190 		{
191 			/* parent -- pick up intermediate zombie */
192 			do
193 			{
194 				auto int stat;
195 
196 				i = wait(&stat);
197 			} while (i >= 0 && i != pid);
198 			if (QueueIntvl != 0)
199 				(void) setevent(QueueIntvl, runqueue, TRUE);
200 			return;
201 		}
202 		/* child -- double fork */
203 		if (fork() != 0)
204 			exit(EX_OK);
205 	}
206 # ifdef LOG
207 	if (LogLevel > 11)
208 		syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid());
209 # endif LOG
210 
211 	/*
212 	**  Start making passes through the queue.
213 	**	First, read and sort the entire queue.
214 	**	Then, process the work in that order.
215 	**		But if you take too long, start over.
216 	*/
217 
218 	/* order the existing work requests */
219 	orderq();
220 
221 	/* process them once at a time */
222 	while (WorkQ != NULL)
223 	{
224 		WORK *w = WorkQ;
225 
226 		WorkQ = WorkQ->w_next;
227 		dowork(w);
228 		free(w->w_name);
229 		free((char *) w);
230 	}
231 	finis();
232 }
233 /*
234 **  ORDERQ -- order the work queue.
235 **
236 **	Parameters:
237 **		none.
238 **
239 **	Returns:
240 **		none.
241 **
242 **	Side Effects:
243 **		Sets WorkQ to the queue of available work, in order.
244 */
245 
246 # define WLSIZE		120	/* max size of worklist per sort */
247 
248 orderq()
249 {
250 	register struct direct *d;
251 	register WORK *w;
252 	register WORK **wp;		/* parent of w */
253 	DIR *f;
254 	register int i;
255 	WORK wlist[WLSIZE];
256 	int wn = 0;
257 	extern workcmpf();
258 
259 	/* clear out old WorkQ */
260 	for (w = WorkQ; w != NULL; )
261 	{
262 		register WORK *nw = w->w_next;
263 
264 		WorkQ = nw;
265 		free(w->w_name);
266 		free((char *) w);
267 		w = nw;
268 	}
269 
270 	/* open the queue directory */
271 	f = opendir(".");
272 	if (f == NULL)
273 	{
274 		syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
275 		return;
276 	}
277 
278 	/*
279 	**  Read the work directory.
280 	*/
281 
282 	while (wn < WLSIZE && (d = readdir(f)) != NULL)
283 	{
284 		char lbuf[MAXNAME];
285 		FILE *cf;
286 
287 		/* is this an interesting entry? */
288 		if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
289 			continue;
290 
291 		/* yes -- open control file */
292 		cf = fopen(d->d_name, "r");
293 		if (cf == NULL)
294 		{
295 			/* this may be some random person sending hir msgs */
296 			/* syserr("orderq: cannot open %s", cbuf); */
297 			errno = 0;
298 			continue;
299 		}
300 		wlist[wn].w_name = newstr(d->d_name);
301 
302 		/* extract useful information */
303 		while (fgets(lbuf, sizeof lbuf, cf) != NULL)
304 		{
305 			fixcrlf(lbuf, TRUE);
306 
307 			switch (lbuf[0])
308 			{
309 			  case 'P':		/* message priority */
310 				(void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri);
311 				break;
312 			}
313 		}
314 		wn++;
315 		(void) fclose(cf);
316 	}
317 	(void) closedir(f);
318 
319 	/*
320 	**  Sort the work directory.
321 	*/
322 
323 	qsort(wlist, wn, sizeof *wlist, workcmpf);
324 
325 	/*
326 	**  Convert the work list into canonical form.
327 	*/
328 
329 	wp = &WorkQ;
330 	for (i = 0; i < wn; i++)
331 	{
332 		w = (WORK *) xalloc(sizeof *w);
333 		w->w_name = wlist[i].w_name;
334 		w->w_pri = wlist[i].w_pri;
335 		w->w_next = NULL;
336 		*wp = w;
337 		wp = &w->w_next;
338 	}
339 
340 # ifdef DEBUG
341 	if (tTd(40, 1))
342 	{
343 		for (w = WorkQ; w != NULL; w = w->w_next)
344 			printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
345 	}
346 # endif DEBUG
347 }
348 /*
349 **  WORKCMPF -- compare function for ordering work.
350 **
351 **	Parameters:
352 **		a -- the first argument.
353 **		b -- the second argument.
354 **
355 **	Returns:
356 **		-1 if a < b
357 **		0 if a == b
358 **		1 if a > b
359 **
360 **	Side Effects:
361 **		none.
362 */
363 
364 # define PRIFACT	1800		/* bytes each priority point is worth */
365 
366 workcmpf(a, b)
367 	register WORK *a;
368 	register WORK *b;
369 {
370 	if (a->w_pri == b->w_pri)
371 		return (0);
372 	else if (a->w_pri > b->w_pri)
373 		return (1);
374 	else
375 		return (-1);
376 }
377 /*
378 **  DOWORK -- do a work request.
379 **
380 **	Parameters:
381 **		w -- the work request to be satisfied.
382 **
383 **	Returns:
384 **		none.
385 **
386 **	Side Effects:
387 **		The work request is satisfied if possible.
388 */
389 
390 dowork(w)
391 	register WORK *w;
392 {
393 	register int i;
394 	auto int xstat;
395 
396 # ifdef DEBUG
397 	if (tTd(40, 1))
398 		printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
399 # endif DEBUG
400 
401 	/*
402 	**  Fork for work.
403 	*/
404 
405 	i = fork();
406 	if (i < 0)
407 	{
408 		syserr("dowork: cannot fork");
409 		return;
410 	}
411 
412 	if (i == 0)
413 	{
414 		/*
415 		**  CHILD
416 		**	Lock the control file to avoid duplicate deliveries.
417 		**		Then run the file as though we had just read it.
418 		**	We save an idea of the temporary name so we
419 		**		can recover on interrupt.
420 		*/
421 
422 		/* set basic modes, etc. */
423 		(void) alarm(0);
424 		CurEnv->e_flags &= ~EF_FATALERRS;
425 		QueueRun = TRUE;
426 		MailBack = TRUE;
427 		CurEnv->e_id = &w->w_name[2];
428 # ifdef LOG
429 		if (LogLevel > 11)
430 			syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id,
431 			       getpid());
432 # endif LOG
433 
434 		/* don't use the headers from sendmail.cf... */
435 		CurEnv->e_header = NULL;
436 		(void) chompheader("from: $q", TRUE);
437 
438 		/* create the link to the control file during processing */
439 		if (link(w->w_name, queuename(CurEnv, 'l')) < 0)
440 		{
441 			/* being processed by another queuer */
442 # ifdef LOG
443 			if (LogLevel > 4)
444 				syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id);
445 # endif LOG
446 			exit(EX_OK);
447 		}
448 
449 		/* create ourselves a transcript file */
450 		openxscrpt();
451 
452 		/* do basic system initialization */
453 		initsys();
454 
455 		/* read the queue control file */
456 		readqf(queuename(CurEnv, 'q'));
457 		CurEnv->e_flags |= EF_INQUEUE;
458 		eatheader();
459 
460 		/* do the delivery */
461 		if (!bitset(EF_FATALERRS, CurEnv->e_flags))
462 			sendall(CurEnv, SM_DELIVER);
463 
464 		/* if still not sent, perhaps we should time out.... */
465 # ifdef DEBUG
466 		if (tTd(40, 3))
467 			printf("curtime=%ld, TimeOut=%ld\n", curtime(),
468 					     CurEnv->e_ctime + TimeOut);
469 # endif DEBUG
470 		if (curtime() > CurEnv->e_ctime + TimeOut)
471 			CurEnv->e_flags |= EF_TIMEOUT;
472 
473 		/* finish up and exit */
474 		finis();
475 	}
476 
477 	/*
478 	**  Parent -- pick up results.
479 	*/
480 
481 	errno = 0;
482 	while ((i = wait(&xstat)) > 0 && errno != EINTR)
483 	{
484 		if (errno == EINTR)
485 		{
486 			errno = 0;
487 		}
488 	}
489 }
490 /*
491 **  READQF -- read queue file and set up environment.
492 **
493 **	Parameters:
494 **		cf -- name of queue control file.
495 **
496 **	Returns:
497 **		none.
498 **
499 **	Side Effects:
500 **		cf is read and created as the current job, as though
501 **		we had been invoked by argument.
502 */
503 
504 readqf(cf)
505 	char *cf;
506 {
507 	register FILE *f;
508 	char buf[MAXFIELD];
509 	extern char *fgetfolded();
510 
511 	/*
512 	**  Open the file created by queueup.
513 	*/
514 
515 	f = fopen(cf, "r");
516 	if (f == NULL)
517 	{
518 		syserr("readqf: no cf file %s", cf);
519 		return;
520 	}
521 
522 	/*
523 	**  Read and process the file.
524 	*/
525 
526 	if (Verbose)
527 		printf("\nRunning %s\n", cf);
528 	while (fgetfolded(buf, sizeof buf, f) != NULL)
529 	{
530 		switch (buf[0])
531 		{
532 		  case 'R':		/* specify recipient */
533 			sendto(&buf[1], (ADDRESS *) NULL, &CurEnv->e_sendqueue);
534 			break;
535 
536 		  case 'H':		/* header */
537 			(void) chompheader(&buf[1], FALSE);
538 			break;
539 
540 		  case 'S':		/* sender */
541 			setsender(newstr(&buf[1]));
542 			break;
543 
544 		  case 'D':		/* data file name */
545 			CurEnv->e_df = newstr(&buf[1]);
546 			TempFile = fopen(CurEnv->e_df, "r");
547 			if (TempFile == NULL)
548 				syserr("readqf: cannot open %s", CurEnv->e_df);
549 			break;
550 
551 		  case 'T':		/* init time */
552 			(void) sscanf(&buf[1], "%ld", &CurEnv->e_ctime);
553 			break;
554 
555 		  case 'P':		/* message priority */
556 			(void) sscanf(&buf[1], "%ld", &CurEnv->e_msgpriority);
557 
558 			/* make sure that big things get sent eventually */
559 			CurEnv->e_msgpriority -= WKTIMEFACT;
560 			break;
561 
562 		  case 'C':		/* message class */
563 			(void) sscanf(&buf[1], "%hd", &CurEnv->e_class);
564 			break;
565 
566 		  case 'M':		/* define macro */
567 			define(buf[1], newstr(&buf[2]));
568 			break;
569 
570 		  default:
571 			syserr("readqf(%s): bad line \"%s\"", cf, buf);
572 			break;
573 		}
574 	}
575 }
576 /*
577 **  TIMEOUT -- process timeout on queue file.
578 **
579 **	Parameters:
580 **		e -- the envelope that timed out.
581 **
582 **	Returns:
583 **		none.
584 **
585 **	Side Effects:
586 **		Returns a message to the sender saying that this
587 **		message has timed out.
588 */
589 
590 timeout(e)
591 	register ENVELOPE *e;
592 {
593 	char buf[MAXLINE];
594 	extern char *pintvl();
595 
596 # ifdef DEBUG
597 	if (tTd(40, 3))
598 		printf("timeout(%s)\n", e->e_id);
599 # endif DEBUG
600 	e->e_to = NULL;
601 	message(Arpa_Info, "Message has timed out");
602 
603 	/* return message to sender */
604 	(void) sprintf(buf, "Cannot send mail for %s", pintvl(TimeOut, FALSE));
605 	(void) returntosender(buf, &e->e_from, TRUE);
606 
607 	/* arrange to remove files from queue */
608 	e->e_flags |= EF_CLRQUEUE;
609 }
610 
611 # endif QUEUE
612