message.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. #include "common.h"
  2. #include "send.h"
  3. #include "../smtp/smtp.h"
  4. #include "../smtp/y.tab.h"
  5. /* global to this file */
  6. static Reprog *rfprog;
  7. static Reprog *fprog;
  8. #define VMLIMIT (64*1024)
  9. #define MSGLIMIT (128*1024*1024)
  10. int received; /* from rfc822.y */
  11. static String* getstring(Node *p);
  12. static String* getaddr(Node *p);
  13. extern int
  14. default_from(message *mp)
  15. {
  16. char *cp, *lp;
  17. cp = getenv("upasname");
  18. lp = getlog();
  19. if(lp == nil)
  20. return -1;
  21. if(cp && *cp)
  22. s_append(mp->sender, cp);
  23. else
  24. s_append(mp->sender, lp);
  25. s_append(mp->date, thedate());
  26. return 0;
  27. }
  28. extern message *
  29. m_new(void)
  30. {
  31. message *mp;
  32. mp = (message *)mallocz(sizeof(message), 1);
  33. if (mp == 0) {
  34. perror("message:");
  35. exit(1);
  36. }
  37. mp->sender = s_new();
  38. mp->replyaddr = s_new();
  39. mp->date = s_new();
  40. mp->body = s_new();
  41. mp->size = 0;
  42. mp->fd = -1;
  43. return mp;
  44. }
  45. extern void
  46. m_free(message *mp)
  47. {
  48. if(mp->fd >= 0){
  49. close(mp->fd);
  50. sysremove(s_to_c(mp->tmp));
  51. s_free(mp->tmp);
  52. }
  53. s_free(mp->sender);
  54. s_free(mp->date);
  55. s_free(mp->body);
  56. s_free(mp->havefrom);
  57. s_free(mp->havesender);
  58. s_free(mp->havereplyto);
  59. s_free(mp->havesubject);
  60. free((char *)mp);
  61. }
  62. /* read a message into a temp file , return an open fd to it */
  63. static int
  64. m_read_to_file(Biobuf *fp, message *mp)
  65. {
  66. int fd;
  67. int n;
  68. String *file;
  69. char buf[4*1024];
  70. file = s_new();
  71. /*
  72. * create temp file to be remove on close
  73. */
  74. abspath("mtXXXXXX", UPASTMP, file);
  75. mktemp(s_to_c(file));
  76. if((fd = syscreate(s_to_c(file), ORDWR|ORCLOSE, 0600))<0){
  77. s_free(file);
  78. return -1;
  79. }
  80. mp->tmp = file;
  81. /*
  82. * read the rest into the temp file
  83. */
  84. while((n = Bread(fp, buf, sizeof(buf))) > 0){
  85. if(write(fd, buf, n) != n){
  86. close(fd);
  87. return -1;
  88. }
  89. mp->size += n;
  90. if(mp->size > MSGLIMIT){
  91. mp->size = -1;
  92. break;
  93. }
  94. }
  95. mp->fd = fd;
  96. return 0;
  97. }
  98. /* get the first address from a node */
  99. static String*
  100. getaddr(Node *p)
  101. {
  102. for(; p; p = p->next)
  103. if(p->s && p->addr)
  104. return s_copy(s_to_c(p->s));
  105. }
  106. /* get the text of a header line minus the field name */
  107. static String*
  108. getstring(Node *p)
  109. {
  110. String *s;
  111. s = s_new();
  112. if(p == nil)
  113. return s;
  114. for(p = p->next; p; p = p->next){
  115. if(p->s){
  116. s_append(s, s_to_c(p->s));
  117. }else{
  118. s_putc(s, p->c);
  119. s_terminate(s);
  120. }
  121. if(p->white)
  122. s_append(s, s_to_c(p->white));
  123. }
  124. return s;
  125. }
  126. static char *fieldname[] =
  127. {
  128. [WORD-WORD] "WORD",
  129. [DATE-WORD] "DATE",
  130. [RESENT_DATE-WORD] "RESENT_DATE",
  131. [RETURN_PATH-WORD] "RETURN_PATH",
  132. [FROM-WORD] "FROM",
  133. [SENDER-WORD] "SENDER",
  134. [REPLY_TO-WORD] "REPLY_TO",
  135. [RESENT_FROM-WORD] "RESENT_FROM",
  136. [RESENT_SENDER-WORD] "RESENT_SENDER",
  137. [RESENT_REPLY_TO-WORD] "RESENT_REPLY_TO",
  138. [SUBJECT-WORD] "SUBJECT",
  139. [TO-WORD] "TO",
  140. [CC-WORD] "CC",
  141. [BCC-WORD] "BCC",
  142. [RESENT_TO-WORD] "RESENT_TO",
  143. [RESENT_CC-WORD] "RESENT_CC",
  144. [RESENT_BCC-WORD] "RESENT_BCC",
  145. [REMOTE-WORD] "REMOTE",
  146. [PRECEDENCE-WORD] "PRECEDENCE",
  147. [MIMEVERSION-WORD] "MIMEVERSION",
  148. [CONTENTTYPE-WORD] "CONTENTTYPE",
  149. [MESSAGEID-WORD] "MESSAGEID",
  150. [RECEIVED-WORD] "RECEIVED",
  151. [MAILER-WORD] "MAILER",
  152. [BADTOKEN-WORD] "BADTOKEN",
  153. };
  154. /* fix 822 addresses */
  155. static void
  156. rfc822cruft(message *mp)
  157. {
  158. Field *f;
  159. Node *p;
  160. String *body, *s;
  161. char *cp;
  162. /*
  163. * parse headers in in-core part
  164. */
  165. yyinit(s_to_c(mp->body), s_len(mp->body));
  166. mp->rfc822headers = 0;
  167. yyparse();
  168. mp->rfc822headers = 1;
  169. mp->received = received;
  170. /*
  171. * remove equivalent systems in all addresses
  172. */
  173. body = s_new();
  174. cp = s_to_c(mp->body);
  175. for(f = firstfield; f; f = f->next){
  176. if(f->node->c == MIMEVERSION)
  177. mp->havemime = 1;
  178. if(f->node->c == FROM)
  179. mp->havefrom = getaddr(f->node);
  180. if(f->node->c == SENDER)
  181. mp->havesender = getaddr(f->node);
  182. if(f->node->c == REPLY_TO)
  183. mp->havereplyto = getaddr(f->node);
  184. if(f->node->c == TO)
  185. mp->haveto = 1;
  186. if(f->node->c == DATE)
  187. mp->havedate = 1;
  188. if(f->node->c == SUBJECT)
  189. mp->havesubject = getstring(f->node);
  190. if(f->node->c == PRECEDENCE && f->node->next && f->node->next->next){
  191. s = f->node->next->next->s;
  192. if(s && (strcmp(s_to_c(s), "bulk") == 0
  193. || strcmp(s_to_c(s), "Bulk") == 0))
  194. mp->bulk = 1;
  195. }
  196. for(p = f->node; p; p = p->next){
  197. if(p->s){
  198. if(p->addr){
  199. cp = skipequiv(s_to_c(p->s));
  200. s_append(body, cp);
  201. } else
  202. s_append(body, s_to_c(p->s));
  203. }else{
  204. s_putc(body, p->c);
  205. s_terminate(body);
  206. }
  207. if(p->white)
  208. s_append(body, s_to_c(p->white));
  209. cp = p->end+1;
  210. }
  211. s_append(body, "\n");
  212. }
  213. if(*s_to_c(body) == 0){
  214. s_free(body);
  215. return;
  216. }
  217. if(*cp != '\n')
  218. s_append(body, "\n");
  219. s_memappend(body, cp, s_len(mp->body) - (cp - s_to_c(mp->body)));
  220. s_terminate(body);
  221. firstfield = 0;
  222. mp->size += s_len(body) - s_len(mp->body);
  223. s_free(mp->body);
  224. mp->body = body;
  225. }
  226. /* read in a message, interpret the 'From' header */
  227. extern message *
  228. m_read(Biobuf *fp, int rmail, int interactive)
  229. {
  230. message *mp;
  231. Resub subexp[10];
  232. char *line;
  233. int first;
  234. int n;
  235. mp = m_new();
  236. /* parse From lines if remote */
  237. if (rmail) {
  238. /* get remote address */
  239. String *sender=s_new();
  240. if (rfprog == 0)
  241. rfprog = regcomp(REMFROMRE);
  242. first = 1;
  243. while(s_read_line(fp, s_restart(mp->body)) != 0) {
  244. memset(subexp, 0, sizeof(subexp));
  245. if (regexec(rfprog, s_to_c(mp->body), subexp, 10) == 0){
  246. if(first == 0)
  247. break;
  248. if (fprog == 0)
  249. fprog = regcomp(FROMRE);
  250. memset(subexp, 0, sizeof(subexp));
  251. if(regexec(fprog, s_to_c(mp->body), subexp,10) == 0)
  252. break;
  253. s_restart(mp->body);
  254. append_match(subexp, s_restart(sender), SENDERMATCH);
  255. append_match(subexp, s_restart(mp->date), DATEMATCH);
  256. break;
  257. }
  258. append_match(subexp, s_restart(sender), REMSENDERMATCH);
  259. append_match(subexp, s_restart(mp->date), REMDATEMATCH);
  260. if(subexp[REMSYSMATCH].sp!=subexp[REMSYSMATCH].ep){
  261. append_match(subexp, mp->sender, REMSYSMATCH);
  262. s_append(mp->sender, "!");
  263. }
  264. first = 0;
  265. }
  266. s_append(mp->sender, s_to_c(sender));
  267. s_free(sender);
  268. }
  269. if(*s_to_c(mp->sender)=='\0')
  270. default_from(mp);
  271. /* if sender address is unreturnable, treat message as bulk mail */
  272. if(!returnable(s_to_c(mp->sender)))
  273. mp->bulk = 1;
  274. /* get body */
  275. if(interactive && !rmail){
  276. /* user typing on terminal: terminator == '.' or EOF */
  277. for(;;) {
  278. line = s_read_line(fp, mp->body);
  279. if (line == 0)
  280. break;
  281. if (strcmp(".\n", line)==0) {
  282. mp->body->ptr -= 2;
  283. *mp->body->ptr = '\0';
  284. break;
  285. }
  286. }
  287. mp->size = mp->body->ptr - mp->body->base;
  288. } else {
  289. /*
  290. * read up to VMLIMIT bytes (more or less) into main memory.
  291. * if message is longer put the rest in a tmp file.
  292. */
  293. mp->size = mp->body->ptr - mp->body->base;
  294. n = s_read(fp, mp->body, VMLIMIT);
  295. if(n < 0){
  296. perror("m_read");
  297. exit(1);
  298. }
  299. mp->size += n;
  300. if(n == VMLIMIT){
  301. if(m_read_to_file(fp, mp) < 0){
  302. perror("m_read");
  303. exit(1);
  304. }
  305. }
  306. }
  307. /*
  308. * ignore 0 length messages from a terminal
  309. */
  310. if (!rmail && mp->size == 0)
  311. return 0;
  312. rfc822cruft(mp);
  313. return mp;
  314. }
  315. /* return a piece of message starting at `offset' */
  316. extern int
  317. m_get(message *mp, long offset, char **pp)
  318. {
  319. static char buf[4*1024];
  320. /*
  321. * are we past eof?
  322. */
  323. if(offset >= mp->size)
  324. return 0;
  325. /*
  326. * are we in the virtual memory portion?
  327. */
  328. if(offset < s_len(mp->body)){
  329. *pp = mp->body->base + offset;
  330. return mp->body->ptr - mp->body->base - offset;
  331. }
  332. /*
  333. * read it from the temp file
  334. */
  335. offset -= s_len(mp->body);
  336. if(mp->fd < 0)
  337. return -1;
  338. if(seek(mp->fd, offset, 0)<0)
  339. return -1;
  340. *pp = buf;
  341. return read(mp->fd, buf, sizeof buf);
  342. }
  343. /* output the message body without ^From escapes */
  344. static int
  345. m_noescape(message *mp, Biobuf *fp)
  346. {
  347. long offset;
  348. int n;
  349. char *p;
  350. for(offset = 0; offset < mp->size; offset += n){
  351. n = m_get(mp, offset, &p);
  352. if(n <= 0){
  353. Bflush(fp);
  354. return -1;
  355. }
  356. if(Bwrite(fp, p, n) < 0)
  357. return -1;
  358. }
  359. return Bflush(fp);
  360. }
  361. /*
  362. * Output the message body with '^From ' escapes.
  363. * Ensures that any line starting with a 'From ' gets a ' ' stuck
  364. * in front of it.
  365. */
  366. static int
  367. m_escape(message *mp, Biobuf *fp)
  368. {
  369. char *p, *np;
  370. char *end;
  371. long offset;
  372. int m, n;
  373. char *start;
  374. for(offset = 0; offset < mp->size; offset += n){
  375. n = m_get(mp, offset, &start);
  376. if(n < 0){
  377. Bflush(fp);
  378. return -1;
  379. }
  380. p = start;
  381. for(end = p+n; p < end; p += m){
  382. np = memchr(p, '\n', end-p);
  383. if(np == 0){
  384. Bwrite(fp, p, end-p);
  385. break;
  386. }
  387. m = np - p + 1;
  388. if(m > 5 && strncmp(p, "From ", 5) == 0)
  389. Bputc(fp, ' ');
  390. Bwrite(fp, p, m);
  391. }
  392. }
  393. Bflush(fp);
  394. return 0;
  395. }
  396. static int
  397. printfrom(message *mp, Biobuf *fp)
  398. {
  399. String *s;
  400. int rv;
  401. if(!returnable(s_to_c(mp->sender)))
  402. return Bprint(fp, "From: Postmaster\n");
  403. s = username(mp->sender);
  404. if(s) {
  405. s_append(s, " <");
  406. s_append(s, s_to_c(mp->sender));
  407. s_append(s, ">");
  408. } else {
  409. s = s_copy(s_to_c(mp->sender));
  410. }
  411. s = unescapespecial(s);
  412. rv = Bprint(fp, "From: %s\n", s_to_c(s));
  413. s_free(s);
  414. return rv;
  415. }
  416. static char *
  417. rewritezone(char *z)
  418. {
  419. int mindiff;
  420. char s;
  421. Tm *tm;
  422. static char x[7];
  423. tm = localtime(time(0));
  424. mindiff = tm->tzoff/60;
  425. /* if not in my timezone, don't change anything */
  426. if(strcmp(tm->zone, z) != 0)
  427. return z;
  428. if(mindiff < 0){
  429. s = '-';
  430. mindiff = -mindiff;
  431. } else
  432. s = '+';
  433. sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
  434. return x;
  435. }
  436. int
  437. isutf8(String *s)
  438. {
  439. char *p;
  440. for(p = s_to_c(s); *p; p++)
  441. if(*p&0x80)
  442. return 1;
  443. return 0;
  444. }
  445. void
  446. printutf8mime(Biobuf *b)
  447. {
  448. Bprint(b, "MIME-Version: 1.0\n");
  449. Bprint(b, "Content-Type: text/plain; charset=\"UTF-8\"\n");
  450. Bprint(b, "Content-Transfer-Encoding: 8bit\n");
  451. }
  452. /* output a message */
  453. extern int
  454. m_print(message *mp, Biobuf *fp, char *remote, int mbox)
  455. {
  456. String *date, *sender;
  457. char *f[6];
  458. int n;
  459. sender = unescapespecial(s_clone(mp->sender));
  460. if (remote != 0){
  461. if(print_remote_header(fp,s_to_c(sender),s_to_c(mp->date),remote) < 0){
  462. s_free(sender);
  463. return -1;
  464. }
  465. } else {
  466. if(print_header(fp, s_to_c(sender), s_to_c(mp->date)) < 0){
  467. s_free(sender);
  468. return -1;
  469. }
  470. }
  471. s_free(sender);
  472. if(!rmail && !mp->havedate){
  473. /* add a date: line Date: Sun, 19 Apr 1998 12:27:52 -0400 */
  474. date = s_copy(s_to_c(mp->date));
  475. n = getfields(s_to_c(date), f, 6, 1, " \t");
  476. if(n == 6)
  477. Bprint(fp, "Date: %s, %s %s %s %s %s\n", f[0], f[2], f[1],
  478. f[5], f[3], rewritezone(f[4]));
  479. }
  480. if(!rmail && !mp->havemime && isutf8(mp->body))
  481. printutf8mime(fp);
  482. if(mp->to){
  483. /* add the to: line */
  484. if (Bprint(fp, "%s\n", s_to_c(mp->to)) < 0)
  485. return -1;
  486. /* add the from: line */
  487. if (!mp->havefrom && printfrom(mp, fp) < 0)
  488. return -1;
  489. if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
  490. if (Bprint(fp, "\n") < 0)
  491. return -1;
  492. } else if(!rmail){
  493. /* add the from: line */
  494. if (!mp->havefrom && printfrom(mp, fp) < 0)
  495. return -1;
  496. if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
  497. if (Bprint(fp, "\n") < 0)
  498. return -1;
  499. }
  500. if (!mbox)
  501. return m_noescape(mp, fp);
  502. return m_escape(mp, fp);
  503. }
  504. /* print just the message body */
  505. extern int
  506. m_bprint(message *mp, Biobuf *fp)
  507. {
  508. return m_noescape(mp, fp);
  509. }