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