|
|
1.1 root 1: #include <stdio.h>
2: #include <regexp.h>
3: #include <signal.h>
4: #include "mail.h"
5: #include "string.h"
6: #include "message.h"
7: #include "dest.h"
8: #include "aux.h"
9: #include "process.h"
10:
11: /* globals to all files */
12: int rmail = 0;
13: int onatty = 0;
14: char *thissys;
15: int nflg = 0;
16: int debug = 0;
17:
18: #define PIPETIMELIMIT (2*60)
19:
20: /* global to this file */
21: static string *errstr;
22: static message *mp;
23: static int interrupt;
24: static char stderrbuf[BUFSIZ];
25: static int savemail;
26:
27: /* imports (other than in .h's) */
28: extern int nsysfile;
29: extern void exit();
30: extern FILE* lockopen();
31:
32: /* interrupt handling */
33: SIGRETURN
34: catch_int(s)
35: int s;
36: {
37: signal(SIGINT, catch_int);
38: interrupt = 1;
39: }
40:
41: /* save the mail and go away */
42: SIGRETURN
43: save_on_int(s)
44: int s;
45: {
46: cleanlocks();
47: save_mail(mp);
48: exit(1);
49: }
50:
51: /* save the mail and go away */
52: SIGRETURN
53: die_on_quit(s)
54: int s;
55: {
56: cleanlocks();
57: exit(1);
58: }
59:
60: main(ac, av)
61: int ac;
62: char *av[];
63: {
64: int i,j;
65: dest *dp=NULL;
66: int checkforward;
67: SIG_TYP fint, fquit, fhup;
68: char *base;
69: int rv;
70:
71: /* process args */
72: setbuf(stderr, stderrbuf);
73: for (i = 1; i < ac; i++) {
74: if (av[i][0] == '-') {
75: for(j=1; av[i][j]; j++)
76: /* option */
77: switch (av[i][1]) {
78: case '#':
79: nflg = 1;
80: break;
81: case 'd':
82: debug = 1;
83: break;
84: default:
85: fprintf(stderr, "usage: mail [-#] list-of-addresses\n");
86: exit(1);
87: }
88: } else
89: /* destination */
90: d_insert(&dp, d_new(s_copy(av[i])));
91: }
92: if (dp == NULL) {
93: fprintf(stderr, "usage: mail [-#] address-list\n");
94: exit(1);
95: }
96:
97: /*
98: * get context:
99: * - whether we're rmail or mail
100: * - whether on a tty
101: */
102: base = basename(av[0]);
103: checkforward = rmail = strcmp(base, "rmail")==0;
104: onatty = (!rmail) && isatty(0);
105: thissys = sysname_read();
106: if (!nflg) {
107: if ((fint=signal(SIGINT, SIG_IGN))!=SIG_DFL)
108: signal(SIGINT, fint);
109: else
110: signal(SIGINT, fint = (SIG_TYP)catch_int);
111: if ((fquit=signal(SIGQUIT, SIG_IGN))!=SIG_DFL)
112: signal(SIGQUIT, fquit);
113: else
114: signal(SIGQUIT, die_on_quit);
115: if ((fhup=signal(SIGHUP, SIG_IGN))!=SIG_DFL)
116: signal(SIGHUP, fhup);
117: else
118: signal(SIGHUP, fhup = (SIG_TYP)catch_int);
119:
120: mp = m_read(stdin, rmail, onatty);
121: if (mp == NULL)
122: exit(0);
123: if (interrupt != 0) {
124: save_mail(mp);
125: exit(1);
126: }
127:
128: if (fint==(SIG_TYP)catch_int)
129: signal(SIGINT, save_on_int);
130: if (fhup==(SIG_TYP)catch_int)
131: signal(SIGHUP, save_on_int);
132: } else {
133: mp = m_new();
134: default_from(mp);
135: }
136: errstr = s_new();
137: (void)getrules();
138:
139: /*
140: * If this is a gateway, translate the sender address into a local
141: * address. This only happens if mail to the local address is
142: * forwarded to the sender.
143: */
144: gateway(mp);
145:
146: /*
147: * Protect against shell characters in the sender name for
148: * security reasons.
149: */
150: s_restart(mp->sender);
151: if (shellchars(s_to_c(mp->sender)))
152: mp->replyaddr = s_copy("postmaster");
153: else
154: mp->replyaddr = s_clone(mp->sender);
155: s_restart(mp->replyaddr);
156:
157: /*
158: * reject messages that are too long. We don't do it earlier
159: * in m_read since we haven't set up enough things yet.
160: */
161: if(mp->size < 0)
162: return refuse(dp, mp, "message too long", 0);
163:
164: rv = send(dp, mp, checkforward);
165: if(savemail)
166: save_mail(mp);
167: return rv;
168: }
169:
170:
171: /* send a message to a list of sites */
172: int
173: send(destp, mp, checkforward)
174: dest *destp;
175: message *mp;
176: {
177: dest *dp; /* destination being acted upon */
178: dest *bound; /* bound destinations */
179: int errors=0;
180: static int forked;
181: extern dest *up_bind();
182:
183: /* bind the destinations to actions */
184: bound = up_bind(destp, mp, checkforward);
185:
186: /* loop through and execute commands */
187: for (dp = d_rm(&bound); dp != NULL; dp = d_rm(&bound)) {
188: switch (dp->status) {
189: case d_cat:
190: errors += cat_mail(dp, mp);
191: break;
192: case d_pipeto:
193: case d_pipe:
194: if (!rmail && !nflg && !forked) {
195: forked = 1;
196: lesstedious();
197: }
198: errors += pipe_mail(dp, mp, dp->status==d_pipeto);
199: break;
200: default:
201: errors += complain_mail(dp, mp);
202: break;
203: }
204: }
205:
206: return errors;
207: }
208:
209: /* avoid user tedium (as Mike Lesk said in a previous version) */
210: lesstedious()
211: {
212: int i;
213:
214: switch(fork()){
215: case -1:
216: onatty = 0;
217: break;
218: case 0:
219: signal(SIGHUP, SIG_IGN);
220: signal(SIGINT, SIG_IGN);
221: signal(SIGQUIT, SIG_IGN);
222: signal(SIGTERM, SIG_IGN);
223: setpgrp(getpid());
224: for(i=0; i<nsysfile; i++)
225: close(i);
226: onatty = 0;
227: savemail=0;
228: break;
229: default:
230: exit(0);
231: }
232: }
233:
234:
235: /* save the mail */
236: save_mail(mp)
237: message *mp;
238: {
239: FILE *fp;
240: string *file=s_new();
241: char *home;
242: static saved = 0;
243: int uid, gid;
244: extern char *getenv();
245:
246: setuid(uid=getuid());
247: setgid(gid=getgid());
248: if ((home = getenv("HOME")) == NULL)
249: return;
250: s_append(file, home);
251: s_append(file, "/dead.letter");
252: if ((fp = lockopen(s_to_c(file), "w", MBOXMODE, uid, gid)) == NULL)
253: return;
254: m_bprint(mp, fp);
255: lockclose(fp);
256: fprintf(stderr, "saved in %s\n", s_to_c(file));
257: }
258:
259: /* remember the interrupt happened */
260: /* dispose of incorrect addresses */
261: complain_mail(dp, mp)
262: dest *dp;
263: message *mp;
264: {
265: char *msg;
266:
267: switch (dp->status) {
268: case d_undefined:
269: msg = "Invalid address"; /* a little different, for debugging */
270: break;
271: case d_syntax:
272: msg = "invalid address";
273: break;
274: case d_unknown:
275: msg = "unknown user";
276: break;
277: case d_eloop:
278: case d_loop:
279: msg = "forwarding loop";
280: break;
281: case d_noforward:
282: if(dp->pstat && *s_to_c(dp->repl2))
283: return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat);
284: else
285: msg = "destination unknown or forwarding disallowed";
286: break;
287: case d_pipe:
288: msg = "broken pipe";
289: break;
290: case d_cat:
291: msg = "broken cat";
292: break;
293: case d_translate:
294: if(dp->pstat && *s_to_c(dp->repl2))
295: return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat);
296: else
297: msg = "name translation failed";
298: break;
299: case d_alias:
300: msg = "broken alias";
301: break;
302: case d_badmbox:
303: msg = "corrupted mailbox";
304: break;
305: case d_resource:
306: msg = "out of some resource. Try again later.";
307: break;
308: }
309: if (nflg) {
310: printf("%s: %s\n", msg, s_to_c(dp->addr));
311: return 1;
312: }
313: return refuse(dp, mp, msg, 0);
314: }
315:
316: static process *pp;
317: static int timedout;
318:
319: SIGRETURN
320: times_up() {
321: kill(pp->pid, SIGKILL);
322: timedout = 1;
323: }
324:
325: /* dispose of remote addresses */
326: pipe_mail(dp, mp, pipeto)
327: dest *dp;
328: message *mp;
329: int pipeto;
330: {
331: string *file;
332: FILE *fp;
333: dest *next, *list=NULL;
334: string *cmd;
335: int status;
336: string *errstr=s_new();
337: char *mbox;
338:
339: /*
340: * collect the arguments
341: */
342: file = s_new();
343: mbox = s_to_c(dp->addr);
344: if (strncmp (mbox, "local!", 6) == 0)
345: mbox += 6;
346: abspath(mbox, MAILROOT, file);
347: next = d_rm_same(&dp);
348: cmd = s_clone(s_restart(next->repl1));
349: for(; next != NULL; next = d_rm_same(&dp)) {
350: if ((next->uid!=-1 || next->gid !=-1)
351: && shellchars(s_to_c(next->addr))){
352: /* this could be a serious security violation */
353: next->status=d_syntax;
354: complain_mail(next, mp);
355: continue;
356: }
357: if (next->repl2 != NULL) {
358: s_append(cmd, " ");
359: s_append(cmd, s_to_c(next->repl2));
360: }
361: d_insert(&list, next);
362: }
363:
364: if (nflg) {
365: puts(s_to_c(cmd));
366: s_free(cmd);
367: s_free(file);
368: return 0;
369: }
370:
371: /*
372: * lock the mailbox (so that `Pipe to's run sequentially)
373: */
374: if(pipeto)
375: fp = lockopen(s_to_c(file), "r", 0, 0, 0);
376: else
377: fp = NULL;
378: s_free(file);
379:
380: /*
381: * run the process
382: */
383: pp = proc_start(s_to_c(cmd), instream(), (stream *)NULL,
384: outstream(), list->uid);
385: if(pp==NULL || pp->std[0]==NULL || pp->std[2]==NULL)
386: return refuse(list, mp, "out of processes, pipes, or memory", 0);
387: m_print(mp, pp->std[0]->fp, thissys, 0);
388: stream_free(pp->std[0]); pp->std[0] = NULL;
389: timedout = 0;
390: if (pipeto) {
391: signal(SIGALRM, times_up);
392: alarm(PIPETIMELIMIT); /* don't let a user hold us up. */
393: }
394: while(s_read_line(pp->std[2]->fp, errstr) != NULL)
395: ;
396: status = proc_wait(pp);
397: if (pipeto) {
398: alarm(0);
399: signal(SIGALRM, SIG_IGN);
400: }
401: proc_free(pp);
402: s_free(cmd);
403:
404: /*
405: * unlock the mailbox (if it was locked)
406: */
407: if(fp!=NULL)
408: lockclose(fp);
409:
410: /*
411: * return status
412: */
413: if (status != 0) {
414: if (timedout)
415: s_append(errstr, "'Pipe to' command took too long");
416: return refuse(list, mp, s_to_c(errstr), status);
417: }
418: loglist(list, mp, "remote");
419: return 0;
420: }
421:
422: /* dispose of local addresses */
423: cat_mail(dp, mp)
424: dest *dp;
425: message *mp;
426: {
427: FILE *fp;
428: char *rcvr;
429:
430: if (nflg) {
431: printf("cat >> %s\n", s_to_c(dp->repl1));
432: return 0;
433: }
434: fp=lockopen(s_to_c(dp->repl1), "a", MBOXMODE, dp->uid, dp->gid);
435: if (fp == NULL)
436: return refuse(dp, mp, "mail file cannot be opened", 0);
437: m_print(mp, fp, (char *)NULL, 1);
438: fputs("\n", fp);
439: fflush(fp);
440: if (ferror(fp)) {
441: lockclose(fp);
442: return refuse(dp, mp, "error writing mail file", 0);
443: }
444: lockclose(fp);
445: rcvr = basename(s_to_c(dp->repl1));
446: logdelivery(dp, rcvr, mp);
447: notify(rcvr, mp);
448: return 0;
449: }
450:
451: static void
452: appaddr(sp, dp)
453: string *sp;
454: dest *dp;
455: {
456: dest *parent;
457:
458: if (dp->parent != NULL) {
459: for(parent=dp->parent; parent->parent!=NULL; parent=parent->parent)
460: ;
461: s_append(sp, s_to_c(parent->addr));
462: s_append(sp, "' alias `");
463: }
464: s_append(sp, s_to_c(dp->addr));
465: }
466:
467: /* reject delivery */
468: refuse(list, mp, cp, status)
469: dest *list;
470: message *mp;
471: char *cp;
472: int status;
473: {
474: string *errstr=s_new();
475: dest *dp;
476: int rv=0;
477:
478: dp = d_rm(&list);
479: mkerrstr(errstr, mp, dp, list, cp, status);
480: /*
481: * if on a tty just report the error. Otherwise send mail
482: * reporting the error. N.B. To avoid mail loops, don't
483: * send mail reporting a failure of mail to reach the postmaster.
484: */
485: if (onatty) {
486: fputs(s_to_c(errstr), stderr);
487: savemail = 1;
488: rv = 1;
489: } else
490: rv = replymsg(errstr, mp, dp);
491: logrefusal(dp, mp, s_to_c(errstr));
492: s_free(errstr);
493: return rv;
494: }
495:
496: /* make the error message */
497: mkerrstr(errstr, mp, dp, list, cp, status)
498: string *errstr;
499: message *mp;
500: dest *dp;
501: dest *list;
502: char *cp;
503: {
504: dest *next;
505: char smsg[64];
506:
507: /* list all aliases */
508: s_append(errstr, "Mail to `");
509: appaddr(errstr, dp);
510: for(next = d_rm(&list); next != NULL; next = d_rm(&list)) {
511: s_append(errstr, "', '");
512: appaddr(errstr, next);
513: d_insert(&dp, next);
514: }
515: s_append(errstr, "' from '");
516: s_append(errstr, s_to_c(mp->sender));
517: s_append(errstr, "' (");
518: s_append(errstr, s_to_c(mp->replyaddr));
519: s_append(errstr, ")");
520: s_append(errstr, " failed.\n");
521:
522: /* >> and | deserve different flavored messages */
523: switch(dp->status) {
524: case d_pipe:
525: s_append(errstr, "The mailer `");
526: s_append(errstr, s_to_c(dp->repl1));
527: sprintf(smsg, "' returned error status %x.\n", status);
528: s_append(errstr, smsg);
529: s_append(errstr, "The error message was:\n");
530: s_append(errstr, cp);
531: break;
532: default:
533: s_append(errstr, "The error message was:\n");
534: s_append(errstr, cp);
535: break;
536: }
537: }
538:
539: /*
540: * reply with up to 1024 characters of the
541: * original message
542: */
543: replymsg(errstr, mp, dp)
544: string *errstr;
545: message *mp;
546: dest *dp;
547: {
548: message *refp = m_new();
549: dest *ndp;
550: char *rcvr;
551: int rv;
552:
553: rcvr = dp->status==d_eloop ? "postmaster" : s_to_c(mp->replyaddr);
554: ndp = d_new(s_copy(rcvr));
555: s_append(refp->sender, "postmaster");
556: s_append(refp->replyaddr, "postmaster");
557: s_append(refp->date, thedate());
558: s_append(refp->body, s_to_c(errstr));
559: s_append(refp->body, "\nThe message began:\n");
560: s_nappend(refp->body, s_to_c(mp->body), 8*1024);
561: refp->size = strlen(s_to_c(refp->body));
562: rv = send(ndp, refp, 0);
563: m_free(refp);
564: d_free(ndp);
565: return rv;
566: }
567:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.