Blob


1 #include "common.h"
2 #include "send.h"
4 /* globals to all files */
5 int rmail;
6 char *thissys, *altthissys;
7 int nflg;
8 int xflg;
9 int debug;
10 int rflg;
11 int iflg = 1;
12 int nosummary;
14 /* global to this file */
15 static String *errstring;
16 static message *mp;
17 static int interrupt;
18 static int savemail;
19 static Biobuf in;
20 static int forked;
21 static int add822headers = 1;
22 static String *arglist;
24 /* predeclared */
25 static int send(dest *, message *, int);
26 static void lesstedious(void);
27 static void save_mail(message *);
28 static int complain_mail(dest *, message *);
29 static int pipe_mail(dest *, message *);
30 static void appaddr(String *, dest *);
31 static void mkerrstring(String *, message *, dest *, dest *, char *, int);
32 static int replymsg(String *, message *, dest *);
33 static int catchint(void*, char*);
35 void
36 usage(void)
37 {
38 fprint(2, "usage: mail [-birtx] list-of-addresses\n");
39 exits("usage");
40 }
42 void
43 main(int argc, char *argv[])
44 {
45 dest *dp=0;
46 int checkforward;
47 char *base;
48 int rv;
50 /* process args */
51 ARGBEGIN{
52 case '#':
53 nflg = 1;
54 break;
55 case 'b':
56 add822headers = 0;
57 break;
58 case 'x':
59 nflg = 1;
60 xflg = 1;
61 break;
62 case 'd':
63 debug = 1;
64 break;
65 case 'i':
66 iflg = 0;
67 break;
68 case 'r':
69 rflg = 1;
70 break;
71 default:
72 usage();
73 }ARGEND
75 while(*argv){
76 if(shellchars(*argv)){
77 fprint(2, "illegal characters in destination\n");
78 exits("syntax");
79 }
80 d_insert(&dp, d_new(s_copy(*argv++)));
81 }
83 if (dp == 0)
84 usage();
85 arglist = d_to(dp);
87 /*
88 * get context:
89 * - whether we're rmail or mail
90 */
91 base = basename(argv0);
92 checkforward = rmail = (strcmp(base, "rmail")==0) | rflg;
93 thissys = sysname_read();
94 altthissys = alt_sysname_read();
95 if(rmail)
96 add822headers = 0;
98 /*
99 * read the mail. If an interrupt occurs while reading, save in
100 * dead.letter
101 */
102 if (!nflg) {
103 Binit(&in, 0, OREAD);
104 if(!rmail)
105 atnotify(catchint, 1);
106 mp = m_read(&in, rmail, !iflg);
107 if (mp == 0)
108 exit(0);
109 if (interrupt != 0) {
110 save_mail(mp);
111 exit(1);
113 } else {
114 mp = m_new();
115 if(default_from(mp) < 0){
116 fprint(2, "%s: can't determine login name\n", argv0);
117 exit(1);
120 errstring = s_new();
121 getrules();
123 /*
124 * If this is a gateway, translate the sender address into a local
125 * address. This only happens if mail to the local address is
126 * forwarded to the sender.
127 */
128 gateway(mp);
130 /*
131 * Protect against shell characters in the sender name for
132 * security reasons.
133 */
134 mp->sender = escapespecial(mp->sender);
135 if (shellchars(s_to_c(mp->sender)))
136 mp->replyaddr = s_copy("postmaster");
137 else
138 mp->replyaddr = s_clone(mp->sender);
140 /*
141 * reject messages that have been looping for too long
142 */
143 if(mp->received > 32)
144 exit(refuse(dp, mp, "possible forward loop", 0, 0));
146 /*
147 * reject messages that are too long. We don't do it earlier
148 * in m_read since we haven't set up enough things yet.
149 */
150 if(mp->size < 0)
151 exit(refuse(dp, mp, "message too long", 0, 0));
153 rv = send(dp, mp, checkforward);
154 if(savemail)
155 save_mail(mp);
156 if(mp)
157 m_free(mp);
158 exit(rv);
161 /* send a message to a list of sites */
162 static int
163 send(dest *destp, message *mp, int checkforward)
165 dest *dp; /* destination being acted upon */
166 dest *bound; /* bound destinations */
167 int errors=0;
169 /* bind the destinations to actions */
170 bound = up_bind(destp, mp, checkforward);
171 if(add822headers && mp->haveto == 0){
172 if(nosummary)
173 mp->to = d_to(bound);
174 else
175 mp->to = arglist;
178 /* loop through and execute commands */
179 for (dp = d_rm(&bound); dp != 0; dp = d_rm(&bound)) {
180 switch (dp->status) {
181 case d_cat:
182 errors += cat_mail(dp, mp);
183 break;
184 case d_pipeto:
185 case d_pipe:
186 if (!rmail && !nflg && !forked) {
187 forked = 1;
188 lesstedious();
190 errors += pipe_mail(dp, mp);
191 break;
192 default:
193 errors += complain_mail(dp, mp);
194 break;
198 return errors;
201 /* avoid user tedium (as Mike Lesk said in a previous version) */
202 static void
203 lesstedious(void)
205 int i;
207 if(debug)
208 return;
210 switch(fork()){
211 case -1:
212 break;
213 case 0:
214 sysdetach();
215 for(i=0; i<3; i++)
216 close(i);
217 savemail = 0;
218 break;
219 default:
220 exit(0);
225 /* save the mail */
226 static void
227 save_mail(message *mp)
229 Biobuf *fp;
230 String *file;
232 file = s_new();
233 deadletter(file);
234 fp = sysopen(s_to_c(file), "cAt", 0660);
235 if (fp == 0)
236 return;
237 m_bprint(mp, fp);
238 sysclose(fp);
239 fprint(2, "saved in %s\n", s_to_c(file));
240 s_free(file);
243 /* remember the interrupt happened */
245 static int
246 catchint(void *a, char *msg)
248 USED(a);
249 if(strstr(msg, "interrupt") || strstr(msg, "hangup")) {
250 interrupt = 1;
251 return 1;
253 return 0;
256 /* dispose of incorrect addresses */
257 static int
258 complain_mail(dest *dp, message *mp)
260 char *msg;
262 switch (dp->status) {
263 case d_undefined:
264 msg = "Invalid address"; /* a little different, for debugging */
265 break;
266 case d_syntax:
267 msg = "invalid address";
268 break;
269 case d_unknown:
270 msg = "unknown user";
271 break;
272 case d_eloop:
273 case d_loop:
274 msg = "forwarding loop";
275 break;
276 case d_noforward:
277 if(dp->pstat && *s_to_c(dp->repl2))
278 return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat, 0);
279 else
280 msg = "destination unknown or forwarding disallowed";
281 break;
282 case d_pipe:
283 msg = "broken pipe";
284 break;
285 case d_cat:
286 msg = "broken cat";
287 break;
288 case d_translate:
289 if(dp->pstat && *s_to_c(dp->repl2))
290 return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat, 0);
291 else
292 msg = "name translation failed";
293 break;
294 case d_alias:
295 msg = "broken alias";
296 break;
297 case d_badmbox:
298 msg = "corrupted mailbox";
299 break;
300 case d_resource:
301 return refuse(dp, mp, "out of some resource. Try again later.", 0, 1);
302 default:
303 msg = "unknown d_";
304 break;
306 if (nflg) {
307 print("%s: %s\n", msg, s_to_c(dp->addr));
308 return 0;
310 return refuse(dp, mp, msg, 0, 0);
313 /* dispose of remote addresses */
314 static int
315 pipe_mail(dest *dp, message *mp)
317 dest *next, *list=0;
318 String *cmd;
319 process *pp;
320 int status;
321 char *none;
322 String *errstring=s_new();
324 if (dp->status == d_pipeto)
325 none = "none";
326 else
327 none = 0;
328 /*
329 * collect the arguments
330 */
331 next = d_rm_same(&dp);
332 if(xflg)
333 cmd = s_new();
334 else
335 cmd = s_clone(next->repl1);
336 for(; next != 0; next = d_rm_same(&dp)){
337 if(xflg){
338 s_append(cmd, s_to_c(next->addr));
339 s_append(cmd, "\n");
340 } else {
341 if (next->repl2 != 0) {
342 s_append(cmd, " ");
343 s_append(cmd, s_to_c(next->repl2));
346 d_insert(&list, next);
349 if (nflg) {
350 if(xflg)
351 print("%s", s_to_c(cmd));
352 else
353 print("%s\n", s_to_c(cmd));
354 s_free(cmd);
355 return 0;
358 /*
359 * run the process
360 */
361 pp = proc_start(s_to_c(cmd), instream(), 0, outstream(), 1, none);
362 if(pp==0 || pp->std[0]==0 || pp->std[2]==0)
363 return refuse(list, mp, "out of processes, pipes, or memory", 0, 1);
364 pipesig(0);
365 m_print(mp, pp->std[0]->fp, thissys, 0);
366 pipesigoff();
367 stream_free(pp->std[0]);
368 pp->std[0] = 0;
369 while(s_read_line(pp->std[2]->fp, errstring))
371 status = proc_wait(pp);
372 proc_free(pp);
373 s_free(cmd);
375 /*
376 * return status
377 */
378 if (status != 0)
379 return refuse(list, mp, s_to_c(errstring), status, 0);
380 loglist(list, mp, "remote");
381 return 0;
384 static void
385 appaddr(String *sp, dest *dp)
387 dest *parent;
388 String *s;
390 if (dp->parent != 0) {
391 for(parent=dp->parent; parent->parent!=0; parent=parent->parent)
393 s = unescapespecial(s_clone(parent->addr));
394 s_append(sp, s_to_c(s));
395 s_free(s);
396 s_append(sp, "' alias `");
398 s = unescapespecial(s_clone(dp->addr));
399 s_append(sp, s_to_c(s));
400 s_free(s);
403 /*
404 * reject delivery
406 * returns 0 - if mail has been disposed of
407 * other - if mail has not been disposed
408 */
409 int
410 refuse(dest *list, message *mp, char *cp, int status, int outofresources)
412 String *errstring=s_new();
413 dest *dp;
414 int rv;
416 dp = d_rm(&list);
417 mkerrstring(errstring, mp, dp, list, cp, status);
419 /*
420 * log first in case we get into trouble
421 */
422 logrefusal(dp, mp, s_to_c(errstring));
424 /*
425 * bulk mail is never replied to, if we're out of resources,
426 * let the sender try again
427 */
428 if(rmail){
429 /* accept it or request a retry */
430 if(outofresources){
431 fprint(2, "Mail %s\n", s_to_c(errstring));
432 rv = 1; /* try again later */
433 } else if(mp->bulk)
434 rv = 0; /* silently discard bulk */
435 else
436 rv = replymsg(errstring, mp, dp); /* try later if we can't reply */
437 } else {
438 /* aysnchronous delivery only happens if !rmail */
439 if(forked){
440 /*
441 * if spun off for asynchronous delivery, we own the mail now.
442 * return it or dump it on the floor. rv really doesn't matter.
443 */
444 rv = 0;
445 if(!outofresources && !mp->bulk)
446 replymsg(errstring, mp, dp);
447 } else {
448 fprint(2, "Mail %s\n", s_to_c(errstring));
449 savemail = 1;
450 rv = 1;
454 s_free(errstring);
455 return rv;
458 /* make the error message */
459 static void
460 mkerrstring(String *errstring, message *mp, dest *dp, dest *list, char *cp, int status)
462 dest *next;
463 char smsg[64];
464 String *sender;
466 sender = unescapespecial(s_clone(mp->sender));
468 /* list all aliases */
469 s_append(errstring, " from '");
470 s_append(errstring, s_to_c(sender));
471 s_append(errstring, "'\nto '");
472 appaddr(errstring, dp);
473 for(next = d_rm(&list); next != 0; next = d_rm(&list)) {
474 s_append(errstring, "'\nand '");
475 appaddr(errstring, next);
476 d_insert(&dp, next);
478 s_append(errstring, "'\nfailed with error '");
479 s_append(errstring, cp);
480 s_append(errstring, "'.\n");
482 /* >> and | deserve different flavored messages */
483 switch(dp->status) {
484 case d_pipe:
485 s_append(errstring, "The mailer `");
486 s_append(errstring, s_to_c(dp->repl1));
487 sprint(smsg, "' returned error status %x.\n\n", status);
488 s_append(errstring, smsg);
489 break;
492 s_free(sender);
495 /*
496 * create a new boundary
497 */
498 static String*
499 mkboundary(void)
501 char buf[32];
502 int i;
503 static int already;
505 if(already == 0){
506 srand((time(0)<<16)|getpid());
507 already = 1;
509 strcpy(buf, "upas-");
510 for(i = 5; i < sizeof(buf)-1; i++)
511 buf[i] = 'a' + nrand(26);
512 buf[i] = 0;
513 return s_copy(buf);
516 /*
517 * reply with up to 1024 characters of the
518 * original message
519 */
520 static int
521 replymsg(String *errstring, message *mp, dest *dp)
523 message *refp = m_new();
524 dest *ndp;
525 char *rcvr;
526 int rv;
527 String *boundary;
529 boundary = mkboundary();
531 refp->bulk = 1;
532 refp->rfc822headers = 1;
533 rcvr = dp->status==d_eloop ? "postmaster" : s_to_c(mp->replyaddr);
534 ndp = d_new(s_copy(rcvr));
535 s_append(refp->sender, "postmaster");
536 s_append(refp->replyaddr, "/dev/null");
537 s_append(refp->date, thedate());
538 refp->haveto = 1;
539 s_append(refp->body, "To: ");
540 s_append(refp->body, rcvr);
541 s_append(refp->body, "\n");
542 s_append(refp->body, "Subject: bounced mail\n");
543 s_append(refp->body, "MIME-Version: 1.0\n");
544 s_append(refp->body, "Content-Type: multipart/mixed;\n");
545 s_append(refp->body, "\tboundary=\"");
546 s_append(refp->body, s_to_c(boundary));
547 s_append(refp->body, "\"\n");
548 s_append(refp->body, "Content-Disposition: inline\n");
549 s_append(refp->body, "\n");
550 s_append(refp->body, "This is a multi-part message in MIME format.\n");
551 s_append(refp->body, "--");
552 s_append(refp->body, s_to_c(boundary));
553 s_append(refp->body, "\n");
554 s_append(refp->body, "Content-Disposition: inline\n");
555 s_append(refp->body, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
556 s_append(refp->body, "Content-Transfer-Encoding: 7bit\n");
557 s_append(refp->body, "\n");
558 s_append(refp->body, "The attached mail");
559 s_append(refp->body, s_to_c(errstring));
560 s_append(refp->body, "--");
561 s_append(refp->body, s_to_c(boundary));
562 s_append(refp->body, "\n");
563 s_append(refp->body, "Content-Type: message/rfc822\n");
564 s_append(refp->body, "Content-Disposition: inline\n\n");
565 s_append(refp->body, s_to_c(mp->body));
566 s_append(refp->body, "--");
567 s_append(refp->body, s_to_c(boundary));
568 s_append(refp->body, "--\n");
570 refp->size = s_len(refp->body);
571 rv = send(ndp, refp, 0);
572 m_free(refp);
573 d_free(ndp);
574 return rv;