message.c 11 KB

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