|
|
1.1 root 1: #include <stdio.h>
2: #include <ctype.h>
3: #include <signal.h>
4: #include <sysexits.h>
5: #include "string.h"
6: #include "smtp.h"
7: #include "mail.h"
8: #include <sys/stat.h>
9:
10: /*
11: * names of the limit files
12: */
13: #define CON ".smtpscheds"
14: #define NCON ".nsmtpscheds"
15:
16: #define OLD 1*3600L /* old C file -> try less often */
17: #define VOLD 6*3600L /* very old C file -> try much less often */
18: #define OLDW 1*3600L /* wait time between tries at old files */
19: #define VOLDW 4*3600L /* wait time between tries at very old files */
20:
21: extern char *UPASROOT;
22: extern char *fileoftype();
23: int warn = -1;
24: int remove = -1;
25: int cleanup;
26: int verbose;
27: int testmode;
28: int Xonly;
29: int Conly;
30: string *replyaddr;
31: string *dest;
32: int mypid;
33: char **getcmd();
34:
35: #define MAXDEST 50 /* # destinations remembered */
36: #define MAXTPERD 5*60 /* total time allowed before skipping dests */
37: struct {
38: string *dest;
39: time_t time;
40: } destlist[MAXDEST]; /* time consumed by unsuccessful tries at dest */
41: int ndest = 0;
42:
43: int debug;
44:
45:
46: /*
47: * actions to take when locking
48: */
49: #define BLOCK 0 /* wait for 5 minutes if the directory is locked */
50: #define SKIP 1 /* skip the directory if it is locked */
51: #define IGNORE 2 /* don't lock the directory or care if it is */
52:
53: /*
54: * If called with arguments, those arguments are spool directories. Descend
55: * each one processing the control files in it (C.* and X.*).
56: *
57: * If called without arguments, descend all spool directories.
58: *
59: * -s #scheds specifies a maximum number of concurrent smtpscheds.
60: * -w #days causes users to be warned if their mail files are older
61: * than #days days
62: * -r #days causes mail older than #days days to be returned to sender
63: * -c cleanup empty directories
64: * -D debug
65: * -L level logging level
66: */
67: usage()
68: {
69: fprintf(stderr, "smtpsched [-cvtDL] [-w #days] [-r #days] [-s #scheds] [dir]\n");
70: exit(1);
71: }
72:
73: main(ac, av)
74: int ac;
75: char *av[];
76: {
77: DIR *dirp;
78: Direct *dp;
79: int max=0;
80: int c;
81: extern int optind;
82: extern char *optarg;
83:
84: umask(2);
85:
86: mypid = getpid();
87:
88: /*
89: * avoid annoying distractions
90: */
91: signal(SIGPIPE, SIG_IGN);
92: signal(SIGHUP, SIG_IGN);
93: Openlog("smtpsched", LOG_PID, LOG_SMTPSCHED);
94: setlogmask(LOG_UPTO(LOG_INFO));
95:
96: while ((c = getopt(ac, av, "XCDcvtr:w:s:L:")) != EOF)
97: switch (c) {
98: case 'X': Xonly++; break;
99: case 'C': Conly++; break;
100: case 't': testmode++; break;
101: case 'v': verbose++; break;
102: case 'c': cleanup++; break;
103: case 'r': remove = atoi(optarg); break;
104: case 's': max = atoi(optarg); break;
105: case 'w': warn = atoi(optarg); break;
106: case 'L': setloglevel(optarg); break;
107: case 'D': debug++; break;
108: default: usage();
109: }
110:
111: /*
112: * go to top spool directory
113: */
114: if(chdir(SMTPQROOT)<0){
115: Syslog(LOG_ALERT, "can't chdir to %s\n", SMTPQROOT);
116: exit(1);
117: }
118:
119: /*
120: * if there are too many running exit
121: */
122: if(max && toomany(max)<0)
123: exit(0);
124:
125: /*
126: * If specific directories, do just them. Keep running the directory till there
127: * is no change.
128: */
129: if(optind!=ac){
130: for(; optind<ac; optind++)
131: while(dodir(av[optind], SKIP) && !warn && !remove)
132: ;
133: return 0;
134: }
135:
136: /*
137: * walk through all directories in top directory. the lock is
138: * non-blocking (if neither r nor w options specified) to let
139: * different instances of smtpsched skip over each other.
140: */
141: dirp = opendir(".");
142: if(dirp<(DIR *)0){
143: Syslog(LOG_ALERT, "couldn't read %s\n", SMTPQROOT);
144: exit(1);
145: }
146: while(dp = readdir(dirp)){
147: if(strcmp(dp->d_name, ".")!=0 && strcmp(dp->d_name, "..")!=0)
148: dodir(dp->d_name, (warn>=0 || remove>=0) ? IGNORE : SKIP);
149: }
150: closedir(dirp);
151: return 0;
152: }
153:
154: /*
155: * do both directions in a directory
156: */
157: dodir(dname, action)
158: char *dname;
159: {
160: int i, err;
161: static string *ds;
162: struct stat buf;
163:
164: if ((err=stat(dname, &buf)) < 0)
165: return 0;
166:
167: if (!(buf.st_mode & S_IFDIR))
168: return 0;
169:
170: ds = s_reset(ds);
171: s_append(ds, dname);
172:
173: if (debug)
174: fprintf(stderr, "Checking %s\n", dname);
175: i = dodirdir(s_to_c(ds), action, "X.");
176: i += dodirdir(s_to_c(ds), action, "C.");
177: return i;
178: }
179:
180: /*
181: * walk through all entries in this directory. process any
182: * not starting with '.'. lock the directory before proceeding.
183: */
184: dodirdir(dname, action, direction)
185: char *dname;
186: char *direction;
187: {
188: DIR *dirp;
189: Direct *dp;
190: int i;
191: int changed=0;
192: int ents=0, files=0;
193: static string *ls;
194: extern int errno;
195:
196: ls = s_reset(ls);
197: s_append(ls, direction);
198: s_append(ls, dname);
199:
200: /*
201: * lock the directory. the lock is in the parent directory.
202: */
203: switch(action){
204: case BLOCK:
205: for(i=0; i<3; i++){
206: if(lock(s_to_c(ls))==0)
207: break;
208: if (debug)
209: Syslog(LOG_DEBUG, "pausing for lock");
210: sleep(5);
211: }
212: if(i==3)
213: return changed;
214: break;
215: case SKIP:
216: if(lock(s_to_c(ls))<0){
217: Syslog(LOG_DEBUG, "couldn't lock %s\n", dname);
218: return changed;
219: }
220: break;
221: case IGNORE:
222: break;
223: }
224:
225: /*
226: * descend into the directory. if it isn't a directory,
227: * this will fail.
228: */
229: if(chdir(dname)<0){
230: if(action != IGNORE)
231: unlock(s_to_c(ls));
232: return changed;
233: }
234:
235: /*
236: * walk through the entries
237: */
238: dirp = opendir(".");
239: if(dirp<(DIR *)0){
240: Syslog(LOG_INFO, "couldn't read directory %s\n", dname);
241: if(chdir(SMTPQROOT)<0){
242: Syslog(LOG_ALERT, "can't chdir back to SMTPQROOT\n");
243: exit(1);
244: }
245: if(action != IGNORE)
246: unlock(s_to_c(ls));
247: return changed;
248: }
249: while(dp = readdir(dirp)){
250: if(strcmp(dp->d_name, ".")==0 || strcmp(dp->d_name, "..")==0)
251: continue;
252: files++;
253: if (cleanup)
254: continue;
255: if (dp->d_name[0] == *direction) {
256: switch(dofile(dname, dp->d_name)){
257: case 0:
258: /* file removed */
259: changed = 1;
260: break;
261: case 1:
262: /* file left alone */
263: ents += 1;
264: break;
265: }
266: }
267: }
268: closedir(dirp);
269:
270: /*
271: * go back up. symbolic links could be painful!!!!
272: */
273: if(chdir(SMTPQROOT)<0){
274: Syslog(LOG_ALERT, "Can't chdir back to SMTPQROOT\n");
275: exit(1);
276: }
277:
278: /*
279: * cleanup empty directories
280: */
281: if(cleanup && files==0){
282: Syslog(LOG_DEBUG, "%s empty\n", dname);
283: if(rmdir(dname)<0)
284: Syslog(LOG_ALERT, "can't unlink: %d\n", errno);
285: }
286:
287: if(action != IGNORE)
288: unlock(s_to_c(ls));
289: return changed;
290: }
291:
292: /*
293: * process a spool control file. control file names start with C. or
294: * X. all error goes into an error file.
295: *
296: * return 0 if file removed, 1 otherwise.
297: */
298: dofile(dname, name)
299: char *dname;
300: char *name;
301: {
302: int rv;
303: int fd, ofd;
304: char *ef;
305: struct stat sb;
306: time_t now, Edate, Cdate;
307:
308: rv = -1;
309:
310: /*
311: * if the file is inconsistent, remove it
312: */
313: if(cleanup && inconsistent(name)){
314: Syslog(LOG_NOTICE, "%s/%s inconsistent\n", dname, name);
315: unlink(name);
316: return 0;
317: }
318:
319: /*
320: * if this is not a control file, ignore it
321: */
322: if(name[1]!='.' || (name[0]!='C' && name[0]!='X'))
323: return 1;
324:
325: /*
326: * if file is too old, warn user and remove it. if checking age,
327: * don't run the control file.
328: */
329: if(warn>=0 || remove>=0) {
330: if(checkage(name)==0) {
331: Syslog(LOG_NOTICE, "%s/%s too old\n", dname, name);
332: doremove(name);
333: return 0;
334: }
335: return 1;
336: }
337:
338: /*
339: * don't run control file when cleaning up
340: */
341: if(cleanup)
342: return 1;
343:
344: /*
345: * Backoff scheme: don't try old C files very often
346: */
347: ef = fileoftype('E', name);
348: if (name[0]=='C') {
349: now = time((time_t *)0);
350: Cdate = now;
351: Edate = now-VOLDW-1;
352: if (stat(name, &sb)==0)
353: Cdate = sb.st_ctime;
354: if (stat(ef, &sb)==0)
355: Edate = sb.st_mtime;
356: if (now-Cdate>VOLD && now-Edate<VOLDW
357: || now-Cdate>OLD && now-Edate<OLDW) {
358: if (verbose)
359: Syslog(LOG_DEBUG, "ignore %s/%s: not time yet\n", dname, name);
360: if (debug==0)
361: return 1;
362: }
363: }
364:
365: /*
366: * in test mode, just return
367: */
368: if (testmode) {
369: Syslog(LOG_DEBUG, "would process %s/%s\n", dname, name);
370: return 1;
371: }
372: /*
373: * redirect output to an error file
374: */
375: ofd = dup(2);
376: close(2);
377: fd = open(ef, 1);
378: if(fd<0)
379: fd = creat(ef, 0666);
380: if(fd>=0){
381: lseek(fd, 0l, 2);
382:
383: /*
384: * process the file
385: */
386: if(name[0]=='C') {
387: rv = dosmtp(dname, name);
388: } else if(name[0]=='X') {
389: rv = dormail(dname, name);
390: }
391:
392: /*
393: * get old error file back
394: */
395: close(2);
396: (void) dup(ofd);
397: close(ofd);
398: }
399:
400: /*
401: * if processing was successful, remove the spool files
402: */
403: if(rv==0) {
404: doremove(name);
405: return 0;
406: }
407: return 1;
408: }
409:
410: /*
411: * remove the control file, data file, and error file
412: */
413: doremove(ctl)
414: char *ctl;
415: {
416: fflush(stdout);
417: unlink(fileoftype('E', ctl));
418: unlink(ctl);
419: unlink(fileoftype('D', ctl));
420: }
421:
422: /*
423: * run rmail. rmail takes care of its own errors, so if rmail fails,
424: * just don't remove the files.
425: */
426: dormail(dname, ctl)
427: char *dname;
428: char *ctl;
429: {
430: char **av;
431: int rc;
432:
433: /*
434: * fork off the command
435: */
436: if ((av = getcmd(ctl, "/bin/rmail")) == NULL) {
437: Syslog(LOG_WARNING, "Could not get rmail params for %s", ctl);
438: return -1;
439: }
440:
441: if ((rc = docmd(ctl, av)) == 0){
442: Syslog(LOG_DEBUG, "success");
443: return 0;
444: } else {
445: Syslog(LOG_DEBUG, "failed, rc = %d", rc);
446: return -1;
447: }
448: }
449:
450: /*
451: * run smtp. if an error occurs, determine its importance and send
452: * a error mail message if it is fatal.
453: */
454: dosmtp(dname, ctl)
455: char *dname;
456: char *ctl;
457: {
458: static string *cmd;
459: int status, i;
460: char **av;
461: time_t t0, t1;
462:
463: /*
464: * fork off the command
465: */
466: cmd = s_reset(cmd);
467: s_append(cmd, UPASROOT);
468: s_append(cmd, "smtp");
469: av = getcmd(ctl, s_to_c(cmd));
470: if (av==NULL) {
471: Syslog(LOG_WARNING, "Could not get smtp params for %s", ctl);
472: return -1;
473: }
474: /*
475: * Check whether unsuccessful attempts at this destination
476: * have taken too much time. If so, pass over the file.
477: */
478: for (i=0; i<ndest; i++) {
479: if (strcmp(s_to_c(dest), s_to_c(destlist[i].dest))==0) {
480: if (destlist[i].time > MAXTPERD) {
481: Syslog(LOG_DEBUG, "passed %s (%d sec)\n", s_to_c(dest), destlist[i].time);
482: fprintf(stderr, "can't contact destination\n");
483: return -1;
484: }
485: break;
486: }
487: }
488: if (i==ndest) {
489: if (ndest<MAXDEST)
490: ndest++;
491: else
492: i = 0; /* loses storage */
493: /*
494: * the following s_copy died on a malformed `C' file. The
495: * contents of these files should be checked more carefully.
496: */
497: destlist[i].dest = s_copy(s_to_c(dest));
498: destlist[i].time = 0;
499: }
500: time(&t0);
501: switch(status=docmd(ctl, av)){
502: case 0: /* it worked */
503: Syslog(LOG_DEBUG, "success\n");
504: destlist[i].time = 0;
505: return 0;
506:
507: case EX_UNAVAILABLE: /* service unavailable */
508: case EX_NOPERM: /* permission denied */
509: case EX_NOUSER: /* rejected by the other side */
510: case EX_NOHOST: /* host name unknown */
511: case EX_DATAERR: /* data format error */
512: case EX_USAGE: /* command line usage error */
513: Syslog(LOG_INFO, "failed with status %d\n", status);
514: returnmail(ctl, 1); /*permanant failure*/
515: destlist[i].time = 0;
516: return 0;
517:
518: case EX_CANTCREAT: /* can't create (user) output file */
519: case EX_IOERR: /* input/output error */
520: case EX_OSERR: /* system error (e.g., can't fork) */
521: case EX_OSFILE: /* critical OS file missing */
522: case EX_SOFTWARE: /* internal software error */
523: case EX_NOINPUT: /* cannot open input */
524: case EX_PROTOCOL: /* remote error in protocol */
525: /* gauss is having flakey datakit errors that confuse the
526: * SMTP protocol. EX_PROTOCOL is a temporary error for gauss-ches*/
527: case EX_TEMPFAIL: /* temp failure; user is invited to retry */
528: Syslog(LOG_INFO, "temp fail with status %d\n", status);
529: time(&t1); /*temporary failure*/
530: destlist[i].time += t1-t0;
531: return -1;
532:
533: default: /* possibly a temporary problem */
534: Syslog(LOG_WARNING, "unknown fail with status %d\n", status);
535: time(&t1);
536: destlist[i].time += t1-t0;
537: return -1;
538: }
539: }
540:
541: /*
542: * open a control file and parse the first line. It contains
543: * the reply address and the destination (for returning the mail).
544: *
545: * It leaves the control file open and returns the fp.
546: */
547: FILE *
548: parseline1(ctl)
549: char *ctl;
550: {
551: FILE *fp;
552: static string *line;
553:
554: fp = fopen(ctl, "r");
555: if(fp==NULL)
556: return NULL;
557:
558: /*
559: * get reply address and destination
560: */
561: line = s_reset(line);
562: if(s_read_line(fp, line)==NULL){
563: fprintf(stderr, "smtpsched: error reading ctl file %s: %s\n", ctl,
564: s_to_c(line));
565: fclose(fp);
566: return NULL;
567: }
568: replyaddr = s_parse(s_restart(line), s_reset(replyaddr));
569: if(replyaddr==NULL){
570: fprintf(stderr, "smtpsched: error reading ctl file replyaddr %s\n",
571: ctl);
572: fclose(fp);
573: return NULL;
574: }
575: dest = s_parse(line, s_reset(dest));
576: if(dest==NULL){
577: fprintf(stderr, "smtpsched: error reading ctl file dest %s\n",
578: ctl);
579: fclose(fp);
580: return NULL;
581: }
582: return fp;
583: }
584:
585: /*
586: * Read control file to get arguments for command. Leave dest and replyaddr
587: * available. The control file has two lines. The first is reply address
588: * and recipients. the second is the arguments for the command.
589: */
590: char **
591: getcmd(ctl, cmd)
592: char *ctl;
593: char *cmd;
594: {
595: static string *args;
596: FILE *fp;
597: static char *av[1024];
598: int ac=0;
599: char *cp;
600:
601: fp = parseline1(ctl);
602: if (fp==NULL)
603: return NULL;
604:
605: /*
606: * make command line
607: */
608: av[ac++] = cmd;
609: args = s_reset(args);
610: if(s_read_line(fp, args)==NULL){
611: fprintf(stderr, "smtpsched: error reading ctl file %s\n", ctl);
612: fclose(fp);
613: return NULL;
614: }
615: fclose(fp);
616: cp = s_to_c(args);
617: cp[strlen(cp) - 1] = '\0'; /*zap the newline*/
618: Syslog(LOG_INFO, "%s <%s", cmd, ctl);
619: for(cp = s_to_c(args); *cp && ac<1023;){
620: av[ac++] = cp++;
621: while(*cp && !isspace(*cp))
622: cp++;
623: while(isspace(*cp))
624: *cp++ = 0;
625: }
626: av[ac] = 0;
627: return av;
628: }
629:
630: /*
631: * execute a command, put standard error in the error file.
632: */
633: docmd(ctl, av)
634: char *ctl;
635: char **av;
636: {
637: int fd;
638: int pid, status;
639: int n;
640:
641: /*
642: * fork off the command
643: */
644: switch(pid = fork()){
645: case -1:
646: return -1;
647: case 0:
648: /*
649: * make data file standard input
650: */
651: close(0);
652: fd = open(fileoftype('D', ctl), 0);
653: if(fd<0){
654: perror("smtpsched: error reading data file:\n");
655: exit(1);
656: }
657:
658: /*
659: * make error file standard output
660: */
661: close(1);
662: fd = dup(2);
663:
664: /*
665: * start the command
666: */
667: execvp(av[0], av);
668: exit(-2);
669: default:
670: /*
671: * wait for the command to terminate
672: */
673: while((n = wait(&status))>=0)
674: if(n == pid)
675: break;
676: if(status&0xff)
677: return -2;
678: else
679: return (status>>8)&0xff;
680: }
681:
682: }
683:
684: /*
685: * see if the number of consumers has been exceeded. if not, add this process
686: * to the list.
687: *
688: * returns 0 if there were the number was not exceeded, -1 otherwise
689: */
690: toomany(max)
691: int max;
692: {
693: FILE *ifp=NULL;
694: FILE *ofp=NULL;
695: int cur=0;
696: int pid;
697:
698: /*
699: * lock consumers file
700: */
701: if(lock(CON)<0)
702: return -1;
703:
704: /*
705: * open old and new consumer files
706: */
707: ofp = fopen(NCON, "w");
708: if(ofp==NULL){
709: fprintf(stderr, "can't open %s\n", NCON);
710: goto error;
711: }
712: ifp = fopen(CON, "r");
713: if(ifp!=NULL){
714: /*
715: * see how many consumers are still around
716: */
717: while(fscanf(ifp, "%d", &pid)==1){
718: if(kill(pid, 0) == 0){
719: cur++;
720: if(fprintf(ofp, "%d\n", pid)<0){
721: fprintf(stderr, "error writing %s\n", NCON);
722: goto error;
723: }
724: }
725: }
726: if(cur >= max)
727: goto error;
728: }
729:
730: /*
731: * add us to the group of consumers
732: */
733: if(fprintf(ofp, "%d\n", getpid())<0){
734: fprintf(stderr, "error writing %s\n", NCON);
735: goto error;
736: }
737: if(ifp!=NULL)
738: fclose(ifp);
739: if(fclose(ofp)==EOF)
740: goto error;
741: unlink(CON);
742: if(link(NCON, CON)<0)
743: fprintf(stderr, "can't link %s to %s file\n", CON, NCON);
744: unlink(NCON);
745: unlock(CON);
746: return 0;
747: error:
748: /*
749: * too many consumers or we can't make a new consumer file
750: */
751: if(ifp!=NULL)
752: fclose(ifp);
753: if(ofp!=NULL)
754: fclose(ofp);
755: unlink(NCON);
756: unlock(CON);
757: return -1;
758: }
759:
760: /*
761: * return true if the file is inconsistent. The following are inconsistent:
762: * - a control file without a datafile
763: * - an error file without a datafile
764: * - a day old data file without a control file
765: * - a limit file of any kind
766: */
767: inconsistent(file)
768: char *file;
769: {
770: struct stat s;
771: int days;
772:
773: /*
774: * switch on file type
775: */
776: switch(file[0]){
777: case 'C':
778: case 'X':
779: /*
780: * if no data file, control file is inconsistent
781: */
782: if(stat(fileoftype('D', file), &s)<0)
783: return 1;
784: break;
785: case 'E':
786: /*
787: * if no control file, error file is inconsistent
788: */
789: if(stat(fileoftype('X', file), &s)<0
790: && stat(fileoftype('C', file), &s)<0)
791: return 1;
792:
793: /*
794: * if no data file, error file is inconsistent
795: */
796: if(stat(fileoftype('D', file), &s)<0)
797: return 1;
798: break;
799: case 'D':
800: /*
801: * look for a control file
802: */
803: if(stat(fileoftype('X', file), &s)==0
804: || stat(fileoftype('C', file), &s)==0)
805: break;
806:
807: /*
808: * no control file, data file inconsistent if >=1 day old
809: */
810: if(stat(file, &s)<0)
811: return 0;
812: days = (time((long *)0) - s.st_ctime)/(24*60*60);
813: if(days>0)
814: return 1;
815: break;
816: default:
817: break;
818: }
819: return 0;
820: }
821:
822: /*
823: * check the age of a file. if it is greater than warn or remove, tell the
824: * sender. return 0 if the file is to be removed, -1 otherwise.
825: */
826: checkage(ctl)
827: char *ctl;
828: {
829: struct stat s;
830: int days;
831: char buf[256];
832: FILE *fp;
833:
834: /*
835: * get the file's age
836: */
837: if(stat(ctl, &s)<0)
838: return -1;
839: days = (time((long *)0) - s.st_ctime)/(24*60*60);
840:
841: /*
842: * check for removal
843: */
844: if(remove>=0 && days>=remove){
845: fp = parseline1(ctl);
846: if(fp==NULL)
847: return -1;
848: fclose(fp);
849:
850: Syslog(LOG_INFO,"returning mail to %s orig to %s after %d days",
851: s_to_c(replyaddr), s_to_c(dest), days);
852: return returnmail(ctl, 1);
853: }
854:
855: /*
856: * check for warning
857: */
858: if(warn>=0 && days>=warn){
859: fp = parseline1(ctl);
860: if(fp==NULL)
861: return -1;
862: fclose(fp);
863:
864: Syslog(LOG_INFO, "warning %s about %s after %d days",
865: s_to_c(replyaddr), s_to_c(dest), days);
866: returnmail(ctl, 0);
867: }
868:
869: return -1;
870: }
871:
872: /*
873: * return a piece of mail with a reason for the return
874: */
875: returnmail(ctl, warn)
876: char *ctl;
877: {
878: int pid, status;
879: string *cmd;
880: int pfd[2];
881: char buf[132];
882: int fd, n;
883: int reads;
884: FILE *fp;
885: FILE *ifp;
886: long now;
887: char asct[27];
888:
889: if(pipe(pfd)<0)
890: return -1;
891:
892: switch(pid=fork()){
893: case -1:
894: close(pfd[0]);
895: close(pfd[1]);
896: return -1;
897: case 0:
898: /*
899: * start up the mailer to take the refusal message
900: */
901: close(0);
902: dup(pfd[0]);
903: close(pfd[1]);
904: execl("/bin/rmail", "/bin/rmail", s_to_c(replyaddr), 0);
905: exit(1);
906: default:
907: /*
908: * pipe the refusal message to the mailer
909: */
910: close(pfd[0]);
911: fp = fdopen(pfd[1], "w");
912: if(fp==NULL) {
913: close(pfd[1]);
914: break;
915: }
916:
917: /*
918: * the From line
919: */
920: now = time((long *)0);
921: strcpy(asct, ctime(&now));
922: asct[24] = 0;
923: fprintf(fp, "From postmaster %s remote from \n", asct);
924:
925: /*
926: * the refusal message
927: */
928: if(warn) {
929: fprintf(fp, "Subject: smtp mail failed\n\n");
930: fprintf(fp, "Your mail to %s is undeliverable.\n",
931: s_to_c(dest));
932: } else {
933: fprintf(fp, "Subject: smtp mail warning\n\n");
934: fprintf(fp, "Your mail to %s is not yet delivered.\n",
935: s_to_c(dest));
936: fprintf(fp, "Delivery attempts continue.\n");
937: }
938:
939: /*
940: * then diagnosis of error
941: */
942: fprintf(fp, "---------- diagnosis ----------\n");
943: ifp = fopen(fileoftype('E', ctl), "r");
944: if(ifp!=NULL){
945: for(reads=0; reads<20; reads++) {
946: if(fgets(buf, sizeof(buf), ifp)==NULL)
947: break;
948: fputs(buf, fp);
949: }
950: fclose(ifp);
951: }
952:
953: /*
954: * finally the message itself
955: */
956: fprintf(fp, "---------- unsent mail ----------\n");
957: ifp = fopen(fileoftype('D', ctl), "r");
958: if(ifp!=NULL){
959: for(reads=0; reads<50; reads++) {
960: if(fgets(buf, sizeof(buf), ifp)==NULL)
961: break;
962: fputs(buf, fp);
963: }
964: fclose(ifp);
965: }
966: fclose(fp);
967:
968: /*
969: * wait for the warning to finish
970: */
971: while((n = wait(&status))>=0)
972: if(n == pid)
973: break;
974: return status ? -1 : 0;
975: }
976: close(pfd[1]);
977: return -1;
978: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.