main.c 12 KB

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