smtp.c 18 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. #include "common.h"
  2. #include "smtp.h"
  3. #include <ctype.h>
  4. #include <mp.h>
  5. #include <libsec.h>
  6. #include <auth.h>
  7. static char* connect(char*);
  8. static char* dotls(char*);
  9. static char* doauth(void);
  10. char* hello(char*, int);
  11. char* mailfrom(char*);
  12. char* rcptto(char*);
  13. char* data(String*, Biobuf*);
  14. void quit(char*);
  15. int getreply(void);
  16. void addhostdom(String*, char*);
  17. String* bangtoat(char*);
  18. String* convertheader(String*);
  19. int printheader(void);
  20. char* domainify(char*, char*);
  21. void putcrnl(char*, int);
  22. char* getcrnl(String*);
  23. int printdate(Node*);
  24. char *rewritezone(char *);
  25. int dBprint(char*, ...);
  26. int dBputc(int);
  27. String* fixrouteaddr(String*, Node*, Node*);
  28. #define Retry "Retry, Temporary Failure"
  29. #define Giveup "Permanent Failure"
  30. int debug; /* true if we're debugging */
  31. String *reply; /* last reply */
  32. String *toline;
  33. int last = 'n'; /* last character sent by putcrnl() */
  34. int filter;
  35. int trysecure; /* Try to use TLS if the other side supports it */
  36. int tryauth; /* Try to authenticate, if supported */
  37. int quitting; /* when error occurs in quit */
  38. char *quitrv; /* deferred return value when in quit */
  39. char ddomain[1024]; /* domain name of destination machine */
  40. char *gdomain; /* domain name of gateway */
  41. char *uneaten; /* first character after rfc822 headers */
  42. char *farend; /* system we are trying to send to */
  43. char *user; /* user we are authenticating as, if authenticating */
  44. char hostdomain[256];
  45. Biobuf bin;
  46. Biobuf bout;
  47. Biobuf berr;
  48. Biobuf bfile;
  49. void
  50. usage(void)
  51. {
  52. fprint(2, "usage: smtp [-ads] [-uuser] [-hhost] [.domain] net!host[!service] sender rcpt-list\n");
  53. exits(Giveup);
  54. }
  55. int
  56. timeout(void *x, char *msg)
  57. {
  58. USED(x);
  59. syslog(0, "smtp.fail", "interrupt: %s: %s", farend, msg);
  60. if(strstr(msg, "alarm")){
  61. fprint(2, "smtp timeout: connection to %s timed out\n", farend);
  62. if(quitting)
  63. exits(quitrv);
  64. exits(Retry);
  65. }
  66. if(strstr(msg, "closed pipe")){
  67. /* call _exits() to prevent Bio from trying to flush closed pipe */
  68. fprint(2, "smtp timeout: connection closed to %s\n", farend);
  69. if(quitting){
  70. syslog(0, "smtp.fail", "closed pipe to %s", farend);
  71. _exits(quitrv);
  72. }
  73. _exits(Retry);
  74. }
  75. return 0;
  76. }
  77. void
  78. main(int argc, char **argv)
  79. {
  80. char hellodomain[256];
  81. char *host, *domain;
  82. String *from;
  83. String *fromm;
  84. String *sender;
  85. char *addr;
  86. char *rv, *trv;
  87. int i, ok, rcvrs;
  88. char **errs;
  89. errs = malloc(argc*sizeof(char*));
  90. reply = s_new();
  91. host = 0;
  92. ARGBEGIN{
  93. case 'a':
  94. tryauth = 1;
  95. trysecure = 1;
  96. break;
  97. case 'f':
  98. filter = 1;
  99. break;
  100. case 'd':
  101. debug = 1;
  102. break;
  103. case 'g':
  104. gdomain = ARGF();
  105. break;
  106. case 'h':
  107. host = ARGF();
  108. break;
  109. case 's':
  110. trysecure = 1;
  111. break;
  112. case 'u':
  113. user = ARGF();
  114. break;
  115. default:
  116. usage();
  117. break;
  118. }ARGEND;
  119. Binit(&berr, 2, OWRITE);
  120. Binit(&bfile, 0, OREAD);
  121. /*
  122. * get domain and add to host name
  123. */
  124. if(*argv && **argv=='.') {
  125. domain = *argv;
  126. argv++; argc--;
  127. } else
  128. domain = domainname_read();
  129. if(host == 0)
  130. host = sysname_read();
  131. strcpy(hostdomain, domainify(host, domain));
  132. strcpy(hellodomain, domainify(sysname_read(), domain));
  133. /*
  134. * get destination address
  135. */
  136. if(*argv == 0)
  137. usage();
  138. addr = *argv++; argc--;
  139. farend = addr;
  140. /*
  141. * get sender's machine.
  142. * get sender in internet style. domainify if necessary.
  143. */
  144. if(*argv == 0)
  145. usage();
  146. sender = unescapespecial(s_copy(*argv++));
  147. argc--;
  148. fromm = s_clone(sender);
  149. rv = strrchr(s_to_c(fromm), '!');
  150. if(rv)
  151. *rv = 0;
  152. else
  153. *s_to_c(fromm) = 0;
  154. from = bangtoat(s_to_c(sender));
  155. /*
  156. * send the mail
  157. */
  158. if(filter){
  159. Binit(&bout, 1, OWRITE);
  160. rv = data(from, &bfile);
  161. if(rv != 0)
  162. goto error;
  163. exits(0);
  164. }
  165. /* 10 minutes to get through the initial handshake */
  166. atnotify(timeout, 1);
  167. alarm(10*60*1000);
  168. if((rv = connect(addr)) != 0)
  169. exits(rv);
  170. alarm(10*60*1000);
  171. if((rv = hello(hellodomain, 0)) != 0)
  172. goto error;
  173. alarm(10*60*1000);
  174. if((rv = mailfrom(s_to_c(from))) != 0)
  175. goto error;
  176. ok = 0;
  177. rcvrs = 0;
  178. /* if any rcvrs are ok, we try to send the message */
  179. for(i = 0; i < argc; i++){
  180. if((trv = rcptto(argv[i])) != 0){
  181. /* remember worst error */
  182. if(rv != Giveup)
  183. rv = trv;
  184. errs[rcvrs] = strdup(s_to_c(reply));
  185. } else {
  186. ok++;
  187. errs[rcvrs] = 0;
  188. }
  189. rcvrs++;
  190. }
  191. /* if no ok rcvrs or worst error is retry, give up */
  192. if(ok == 0 || rv == Retry)
  193. goto error;
  194. rv = data(from, &bfile);
  195. if(rv != 0)
  196. goto error;
  197. quit(0);
  198. if(rcvrs == ok)
  199. exits(0);
  200. /*
  201. * here when some but not all rcvrs failed
  202. */
  203. fprint(2, "%s connect to %s:\n", thedate(), addr);
  204. for(i = 0; i < rcvrs; i++){
  205. if(errs[i]){
  206. syslog(0, "smtp.fail", "delivery to %s failed: %s", addr, errs[i]);
  207. fprint(2, " mail to %s failed: %s", argv[i], errs[i]);
  208. }
  209. }
  210. exits(Giveup);
  211. /*
  212. * here when all rcvrs failed
  213. */
  214. error:
  215. syslog(0, "smtp.fail", "delivery to %s failed: %s", addr, s_to_c(reply));
  216. fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply));
  217. if(!filter)
  218. quit(rv);
  219. exits(rv);
  220. }
  221. /*
  222. * connect to the remote host
  223. */
  224. static char *
  225. connect(char* net)
  226. {
  227. char buf[256];
  228. int fd;
  229. fd = mxdial(net, ddomain, gdomain);
  230. if(fd < 0){
  231. rerrstr(buf, sizeof(buf));
  232. Bprint(&berr, "smtp: %s (%s)\n", buf, net);
  233. syslog(0, "smtp.fail", "%s (%s)", buf, net);
  234. if(strstr(buf, "illegal")
  235. || strstr(buf, "unknown")
  236. || strstr(buf, "can't translate"))
  237. return Giveup;
  238. else
  239. return Retry;
  240. }
  241. Binit(&bin, fd, OREAD);
  242. fd = dup(fd, -1);
  243. Binit(&bout, fd, OWRITE);
  244. return 0;
  245. }
  246. /*
  247. * exchange names with remote host, possibly
  248. * enable encryption and do authentication.
  249. */
  250. static char *
  251. dotls(char *me)
  252. {
  253. TLSconn *c;
  254. Thumbprint *goodcerts;
  255. char *h;
  256. int fd;
  257. uchar hash[SHA1dlen];
  258. c = mallocz(sizeof(*c), 1); /* Note: not freed on success */
  259. if (c == nil)
  260. return Giveup;
  261. dBprint("STARTTLS\r\n");
  262. getreply();
  263. fd = tlsClient(Bfildes(&bout), c);
  264. if (fd < 0)
  265. {if(debug)fprint(2, "error starting tlsClient: %r\n");
  266. return Giveup;
  267. }
  268. goodcerts = initThumbprints("/sys/lib/tls/smtp", "/sys/lib/tls/smtp.exclude");
  269. if (goodcerts == nil) {
  270. if(debug)fprint(2, "bad cert\n");
  271. free(c);
  272. return Giveup;
  273. }
  274. sha1(c->cert, c->certlen, hash, nil);
  275. if (!okThumbprint(hash, goodcerts)) {
  276. h = malloc(2 * sizeof hash + 1);
  277. if (h != nil) {
  278. enc16(h, 2 * sizeof hash + 1, hash, sizeof hash);
  279. print("x509 sha1=%s", h);
  280. free(h);
  281. }
  282. return Giveup;
  283. }
  284. freeThumbprints(goodcerts);
  285. Bterm(&bin);
  286. Bterm(&bout);
  287. Binit(&bin, fd, OREAD);
  288. fd = dup(fd, -1);
  289. Binit(&bout, fd, OWRITE);
  290. return(hello(me, 1));
  291. }
  292. static char *
  293. doauth(void)
  294. {
  295. char *buf, *base64;
  296. UserPasswd *p;
  297. int n;
  298. if(user != nil)
  299. p = auth_getuserpasswd(nil,
  300. "proto=pass service=smtp server=%q user=%q",
  301. ddomain, user);
  302. else
  303. p = auth_getuserpasswd(nil,
  304. "proto=pass service=smtp server=%q",
  305. ddomain);
  306. if (p == nil)
  307. return Giveup;
  308. n = strlen(p->user) + strlen(p->passwd) + 3;
  309. buf = malloc(n);
  310. if (buf == nil)
  311. return Retry; /* Out of memory */
  312. base64 = malloc(2 * n);
  313. if (base64 == nil) {
  314. free(buf);
  315. return Retry; /* Out of memory */
  316. }
  317. snprint(buf, n, "%c%s%c%s", 0, p->user, 0, p->passwd);
  318. enc64(base64, 2 * n, (uchar *)buf, n - 1);
  319. free(buf);
  320. dBprint("AUTH PLAIN %s\r\n", base64);
  321. free(base64);
  322. if (getreply() != 2)
  323. return Retry;
  324. return(0);
  325. }
  326. char *
  327. hello(char *me, int encrypted)
  328. {
  329. int ehlo;
  330. String *r;
  331. char *s, *t;
  332. if (!encrypted)
  333. switch(getreply()){
  334. case 2:
  335. break;
  336. case 5:
  337. return Giveup;
  338. default:
  339. return Retry;
  340. }
  341. ehlo = 1;
  342. Again:
  343. if(ehlo)
  344. dBprint("EHLO %s\r\n", me);
  345. else
  346. dBprint("HELO %s\r\n", me);
  347. switch (getreply()) {
  348. case 2:
  349. break;
  350. case 5:
  351. if(ehlo){
  352. ehlo = 0;
  353. goto Again;
  354. }
  355. return Giveup;
  356. default:
  357. return Retry;
  358. }
  359. r = s_clone(reply);
  360. if(r == nil)
  361. return Retry; /* Out of memory or couldn't get string */
  362. /* Invariant: every line has a newline, a result of getcrlf() */
  363. for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){
  364. *t = '\0';
  365. for (t = s; *t != '\0'; t++)
  366. *t = toupper(*t);
  367. if(!encrypted && trysecure &&
  368. (strcmp(s, "250-STARTTLS") == 0 ||
  369. strcmp(s, "250 STARTTLS") == 0)){
  370. s_free(r);
  371. return(dotls(me));
  372. }
  373. if(tryauth && encrypted &&
  374. (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 ||
  375. strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0) &&
  376. strstr(s, "PLAIN") != nil){
  377. s_free(r);
  378. return(doauth());
  379. }
  380. }
  381. s_free(r);
  382. return 0;
  383. }
  384. /*
  385. * report sender to remote
  386. */
  387. char *
  388. mailfrom(char *from)
  389. {
  390. if(!returnable(from))
  391. dBprint("MAIL FROM:<>\r\n");
  392. else
  393. if(strchr(from, '@'))
  394. dBprint("MAIL FROM:<%s>\r\n", from);
  395. else
  396. dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain);
  397. switch(getreply()){
  398. case 2:
  399. break;
  400. case 5:
  401. return Giveup;
  402. default:
  403. return Retry;
  404. }
  405. return 0;
  406. }
  407. /*
  408. * report a recipient to remote
  409. */
  410. char *
  411. rcptto(char *to)
  412. {
  413. String *s;
  414. s = unescapespecial(bangtoat(to));
  415. if(toline == 0)
  416. toline = s_new();
  417. else
  418. s_append(toline, ", ");
  419. s_append(toline, s_to_c(s));
  420. if(strchr(s_to_c(s), '@'))
  421. dBprint("RCPT TO:<%s>\r\n", s_to_c(s));
  422. else {
  423. s_append(toline, "@");
  424. s_append(toline, ddomain);
  425. dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain);
  426. }
  427. alarm(10*60*1000);
  428. switch(getreply()){
  429. case 2:
  430. break;
  431. case 5:
  432. return Giveup;
  433. default:
  434. return Retry;
  435. }
  436. return 0;
  437. }
  438. static char hex[] = "0123456789abcdef";
  439. /*
  440. * send the damn thing
  441. */
  442. char *
  443. data(String *from, Biobuf *b)
  444. {
  445. char *buf, *cp;
  446. int i, n, nbytes, bufsize, eof, r;
  447. String *fromline;
  448. char errmsg[Errlen];
  449. char id[40];
  450. /*
  451. * input the header.
  452. */
  453. buf = malloc(1);
  454. if(buf == 0){
  455. s_append(s_restart(reply), "out of memory");
  456. return Retry;
  457. }
  458. n = 0;
  459. eof = 0;
  460. for(;;){
  461. cp = Brdline(b, '\n');
  462. if(cp == nil){
  463. eof = 1;
  464. break;
  465. }
  466. nbytes = Blinelen(b);
  467. buf = realloc(buf, n+nbytes+1);
  468. if(buf == 0){
  469. s_append(s_restart(reply), "out of memory");
  470. return Retry;
  471. }
  472. strncpy(buf+n, cp, nbytes);
  473. n += nbytes;
  474. if(nbytes == 1) /* end of header */
  475. break;
  476. }
  477. buf[n] = 0;
  478. bufsize = n;
  479. /*
  480. * parse the header, turn all addresses into @ format
  481. */
  482. yyinit(buf, n);
  483. yyparse();
  484. /*
  485. * print message observing '.' escapes and using \r\n for \n
  486. */
  487. alarm(20*60*1000);
  488. if(!filter){
  489. dBprint("DATA\r\n");
  490. switch(getreply()){
  491. case 3:
  492. break;
  493. case 5:
  494. free(buf);
  495. return Giveup;
  496. default:
  497. free(buf);
  498. return Retry;
  499. }
  500. }
  501. /*
  502. * send header. add a message-id, a sender, and a date if there
  503. * isn't one
  504. */
  505. nbytes = 0;
  506. fromline = convertheader(from);
  507. uneaten = buf;
  508. srand(truerand());
  509. if(messageid == 0){
  510. for(i=0; i<16; i++){
  511. r = rand()&0xFF;
  512. id[2*i] = hex[r&0xF];
  513. id[2*i+1] = hex[(r>>4)&0xF];
  514. }
  515. id[2*i] = '\0';
  516. nbytes += Bprint(&bout, "Message-ID: <%s@%s>\r\n", id, hostdomain);
  517. if(debug)
  518. Bprint(&berr, "Message-ID: <%s@%s>\r\n", id, hostdomain);
  519. }
  520. if(originator==0){
  521. nbytes += Bprint(&bout, "From: %s\r\n", s_to_c(fromline));
  522. if(debug)
  523. Bprint(&berr, "From: %s\r\n", s_to_c(fromline));
  524. }
  525. s_free(fromline);
  526. if(destination == 0 && toline)
  527. if(*s_to_c(toline) == '@'){ /* route addr */
  528. nbytes += Bprint(&bout, "To: <%s>\r\n", s_to_c(toline));
  529. if(debug)
  530. Bprint(&berr, "To: <%s>\r\n", s_to_c(toline));
  531. } else {
  532. nbytes += Bprint(&bout, "To: %s\r\n", s_to_c(toline));
  533. if(debug)
  534. Bprint(&berr, "To: %s\r\n", s_to_c(toline));
  535. }
  536. if(date==0 && udate)
  537. nbytes += printdate(udate);
  538. if (usys)
  539. uneaten = usys->end + 1;
  540. nbytes += printheader();
  541. if (*uneaten != '\n')
  542. putcrnl("\n", 1);
  543. /*
  544. * send body
  545. */
  546. putcrnl(uneaten, buf+n - uneaten);
  547. nbytes += buf+n - uneaten;
  548. if(eof == 0){
  549. for(;;){
  550. n = Bread(b, buf, bufsize);
  551. if(n < 0){
  552. rerrstr(errmsg, sizeof(errmsg));
  553. s_append(s_restart(reply), errmsg);
  554. free(buf);
  555. return Retry;
  556. }
  557. if(n == 0)
  558. break;
  559. alarm(10*60*1000);
  560. putcrnl(buf, n);
  561. nbytes += n;
  562. }
  563. }
  564. free(buf);
  565. if(!filter){
  566. if(last != '\n')
  567. dBprint("\r\n.\r\n");
  568. else
  569. dBprint(".\r\n");
  570. alarm(10*60*1000);
  571. switch(getreply()){
  572. case 2:
  573. break;
  574. case 5:
  575. return Giveup;
  576. default:
  577. return Retry;
  578. }
  579. syslog(0, "smtp", "%s sent %d bytes to %s", s_to_c(from),
  580. nbytes, s_to_c(toline));/**/
  581. }
  582. return 0;
  583. }
  584. /*
  585. * we're leaving
  586. */
  587. void
  588. quit(char *rv)
  589. {
  590. /* 60 minutes to quit */
  591. quitting = 1;
  592. quitrv = rv;
  593. alarm(60*60*1000);
  594. dBprint("QUIT\r\n");
  595. getreply();
  596. Bterm(&bout);
  597. Bterm(&bfile);
  598. }
  599. /*
  600. * read a reply into a string, return the reply code
  601. */
  602. int
  603. getreply(void)
  604. {
  605. char *line;
  606. int rv;
  607. reply = s_reset(reply);
  608. for(;;){
  609. line = getcrnl(reply);
  610. if(line == 0)
  611. return -1;
  612. if(!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2]))
  613. return -1;
  614. if(line[3] != '-')
  615. break;
  616. }
  617. if(debug)
  618. Bflush(&berr);
  619. rv = atoi(line)/100;
  620. return rv;
  621. }
  622. void
  623. addhostdom(String *buf, char *host)
  624. {
  625. s_append(buf, "@");
  626. s_append(buf, host);
  627. }
  628. /*
  629. * Convert from `bang' to `source routing' format.
  630. *
  631. * a.x.y!b.p.o!c!d -> @a.x.y:c!d@b.p.o
  632. */
  633. String *
  634. bangtoat(char *addr)
  635. {
  636. String *buf;
  637. register int i;
  638. int j, d;
  639. char *field[128];
  640. /* parse the '!' format address */
  641. buf = s_new();
  642. for(i = 0; addr; i++){
  643. field[i] = addr;
  644. addr = strchr(addr, '!');
  645. if(addr)
  646. *addr++ = 0;
  647. }
  648. if (i==1) {
  649. s_append(buf, field[0]);
  650. return buf;
  651. }
  652. /*
  653. * count leading domain fields (non-domains don't count)
  654. */
  655. for(d = 0; d<i-1; d++)
  656. if(strchr(field[d], '.')==0)
  657. break;
  658. /*
  659. * if there are more than 1 leading domain elements,
  660. * put them in as source routing
  661. */
  662. if(d > 1){
  663. addhostdom(buf, field[0]);
  664. for(j=1; j<d-1; j++){
  665. s_append(buf, ",");
  666. s_append(buf, "@");
  667. s_append(buf, field[j]);
  668. }
  669. s_append(buf, ":");
  670. }
  671. /*
  672. * throw in the non-domain elements separated by '!'s
  673. */
  674. s_append(buf, field[d]);
  675. for(j=d+1; j<=i-1; j++) {
  676. s_append(buf, "!");
  677. s_append(buf, field[j]);
  678. }
  679. if(d)
  680. addhostdom(buf, field[d-1]);
  681. return buf;
  682. }
  683. /*
  684. * convert header addresses to @ format.
  685. * if the address is a source address, and a domain is specified,
  686. * make sure it falls in the domain.
  687. */
  688. String*
  689. convertheader(String *from)
  690. {
  691. Field *f;
  692. Node *p, *lastp;
  693. String *a;
  694. if(!returnable(s_to_c(from))){
  695. from = s_new();
  696. s_append(from, "Postmaster");
  697. addhostdom(from, hostdomain);
  698. } else
  699. if(strchr(s_to_c(from), '@') == 0){
  700. a = username(from);
  701. if(a) {
  702. s_append(a, " <");
  703. s_append(a, s_to_c(from));
  704. addhostdom(a, hostdomain);
  705. s_append(a, ">");
  706. from = a;
  707. } else {
  708. from = s_copy(s_to_c(from));
  709. addhostdom(from, hostdomain);
  710. }
  711. } else
  712. from = s_copy(s_to_c(from));
  713. for(f = firstfield; f; f = f->next){
  714. lastp = 0;
  715. for(p = f->node; p; lastp = p, p = p->next){
  716. if(!p->addr)
  717. continue;
  718. a = bangtoat(s_to_c(p->s));
  719. s_free(p->s);
  720. if(strchr(s_to_c(a), '@') == 0)
  721. addhostdom(a, hostdomain);
  722. else if(*s_to_c(a) == '@')
  723. a = fixrouteaddr(a, p->next, lastp);
  724. p->s = a;
  725. }
  726. }
  727. return from;
  728. }
  729. /*
  730. * ensure route addr has brackets around it
  731. */
  732. String*
  733. fixrouteaddr(String *raddr, Node *next, Node *last)
  734. {
  735. String *a;
  736. if(last && last->c == '<' && next && next->c == '>')
  737. return raddr; /* properly formed already */
  738. a = s_new();
  739. s_append(a, "<");
  740. s_append(a, s_to_c(raddr));
  741. s_append(a, ">");
  742. s_free(raddr);
  743. return a;
  744. }
  745. /*
  746. * print out the parsed header
  747. */
  748. int
  749. printheader(void)
  750. {
  751. int n, len;
  752. Field *f;
  753. Node *p;
  754. char *cp;
  755. char c[1];
  756. n = 0;
  757. for(f = firstfield; f; f = f->next){
  758. for(p = f->node; p; p = p->next){
  759. if(p->s)
  760. n += dBprint("%s", s_to_c(p->s));
  761. else {
  762. c[0] = p->c;
  763. putcrnl(c, 1);
  764. n++;
  765. }
  766. if(p->white){
  767. cp = s_to_c(p->white);
  768. len = strlen(cp);
  769. putcrnl(cp, len);
  770. n += len;
  771. }
  772. uneaten = p->end;
  773. }
  774. putcrnl("\n", 1);
  775. n++;
  776. uneaten++; /* skip newline */
  777. }
  778. return n;
  779. }
  780. /*
  781. * add a domain onto an name, return the new name
  782. */
  783. char *
  784. domainify(char *name, char *domain)
  785. {
  786. static String *s;
  787. char *p;
  788. if(domain==0 || strchr(name, '.')!=0)
  789. return name;
  790. s = s_reset(s);
  791. s_append(s, name);
  792. p = strchr(domain, '.');
  793. if(p == 0){
  794. s_append(s, ".");
  795. p = domain;
  796. }
  797. s_append(s, p);
  798. return s_to_c(s);
  799. }
  800. /*
  801. * print message observing '.' escapes and using \r\n for \n
  802. */
  803. void
  804. putcrnl(char *cp, int n)
  805. {
  806. int c;
  807. for(; n; n--, cp++){
  808. c = *cp;
  809. if(c == '\n')
  810. dBputc('\r');
  811. else if(c == '.' && last=='\n')
  812. dBputc('.');
  813. dBputc(c);
  814. last = c;
  815. }
  816. }
  817. /*
  818. * Get a line including a crnl into a string. Convert crnl into nl.
  819. */
  820. char *
  821. getcrnl(String *s)
  822. {
  823. int c;
  824. int count;
  825. count = 0;
  826. for(;;){
  827. c = Bgetc(&bin);
  828. if(debug)
  829. Bputc(&berr, c);
  830. switch(c){
  831. case -1:
  832. s_append(s, "connection closed unexpectedly by remote system");
  833. s_terminate(s);
  834. return 0;
  835. case '\r':
  836. c = Bgetc(&bin);
  837. if(c == '\n'){
  838. s_putc(s, c);
  839. if(debug)
  840. Bputc(&berr, c);
  841. count++;
  842. s_terminate(s);
  843. return s->ptr - count;
  844. }
  845. Bungetc(&bin);
  846. s_putc(s, '\r');
  847. if(debug)
  848. Bputc(&berr, '\r');
  849. count++;
  850. break;
  851. default:
  852. s_putc(s, c);
  853. count++;
  854. break;
  855. }
  856. }
  857. return 0;
  858. }
  859. /*
  860. * print out a parsed date
  861. */
  862. int
  863. printdate(Node *p)
  864. {
  865. int n, sep = 0;
  866. n = dBprint("Date: %s,", s_to_c(p->s));
  867. for(p = p->next; p; p = p->next){
  868. if(p->s){
  869. if(sep == 0) {
  870. dBputc(' ');
  871. n++;
  872. }
  873. if (p->next)
  874. n += dBprint("%s", s_to_c(p->s));
  875. else
  876. n += dBprint("%s", rewritezone(s_to_c(p->s)));
  877. sep = 0;
  878. } else {
  879. dBputc(p->c);
  880. n++;
  881. sep = 1;
  882. }
  883. }
  884. n += dBprint("\r\n");
  885. return n;
  886. }
  887. char *
  888. rewritezone(char *z)
  889. {
  890. int mindiff;
  891. char s;
  892. Tm *tm;
  893. static char x[7];
  894. tm = localtime(time(0));
  895. mindiff = tm->tzoff/60;
  896. /* if not in my timezone, don't change anything */
  897. if(strcmp(tm->zone, z) != 0)
  898. return z;
  899. if(mindiff < 0){
  900. s = '-';
  901. mindiff = -mindiff;
  902. } else
  903. s = '+';
  904. sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
  905. return x;
  906. }
  907. /*
  908. * stolen from libc/port/print.c
  909. */
  910. #define SIZE 4096
  911. int
  912. dBprint(char *fmt, ...)
  913. {
  914. char buf[SIZE], *out;
  915. va_list arg;
  916. int n;
  917. va_start(arg, fmt);
  918. out = vseprint(buf, buf+SIZE, fmt, arg);
  919. va_end(arg);
  920. if(debug){
  921. Bwrite(&berr, buf, (long)(out-buf));
  922. Bflush(&berr);
  923. }
  924. n = Bwrite(&bout, buf, (long)(out-buf));
  925. Bflush(&bout);
  926. return n;
  927. }
  928. int
  929. dBputc(int x)
  930. {
  931. if(debug)
  932. Bputc(&berr, x);
  933. return Bputc(&bout, x);
  934. }