mbox.c 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483
  1. #include "common.h"
  2. #include <ctype.h>
  3. #include <plumb.h>
  4. #include <libsec.h>
  5. #include "dat.h"
  6. typedef struct Header Header;
  7. struct Header {
  8. char *type;
  9. void (*f)(Message*, Header*, char*);
  10. int len;
  11. };
  12. /* headers */
  13. static void ctype(Message*, Header*, char*);
  14. static void cencoding(Message*, Header*, char*);
  15. static void cdisposition(Message*, Header*, char*);
  16. static void date822(Message*, Header*, char*);
  17. static void from822(Message*, Header*, char*);
  18. static void to822(Message*, Header*, char*);
  19. static void sender822(Message*, Header*, char*);
  20. static void replyto822(Message*, Header*, char*);
  21. static void subject822(Message*, Header*, char*);
  22. static void inreplyto822(Message*, Header*, char*);
  23. static void cc822(Message*, Header*, char*);
  24. static void bcc822(Message*, Header*, char*);
  25. static void messageid822(Message*, Header*, char*);
  26. static void mimeversion(Message*, Header*, char*);
  27. static void nullsqueeze(Message*);
  28. enum
  29. {
  30. Mhead= 11, /* offset of first mime header */
  31. };
  32. Header head[] =
  33. {
  34. { "date:", date822, },
  35. { "from:", from822, },
  36. { "to:", to822, },
  37. { "sender:", sender822, },
  38. { "reply-to:", replyto822, },
  39. { "subject:", subject822, },
  40. { "cc:", cc822, },
  41. { "bcc:", bcc822, },
  42. { "in-reply-to:", inreplyto822, },
  43. { "mime-version:", mimeversion, },
  44. { "message-id:", messageid822, },
  45. [Mhead] { "content-type:", ctype, },
  46. { "content-transfer-encoding:", cencoding, },
  47. { "content-disposition:", cdisposition, },
  48. { 0, },
  49. };
  50. static void fatal(char *fmt, ...);
  51. static void initquoted(void);
  52. static void startheader(Message*);
  53. static void startbody(Message*);
  54. static char* skipwhite(char*);
  55. static char* skiptosemi(char*);
  56. static char* getstring(char*, String*, int);
  57. static void setfilename(Message*, char*);
  58. static char* lowercase(char*);
  59. static int is8bit(Message*);
  60. static int headerline(char**, String*);
  61. static void initheaders(void);
  62. static void parseattachments(Message*, Mailbox*);
  63. int debug;
  64. char *Enotme = "path not served by this file server";
  65. enum
  66. {
  67. Chunksize = 1024,
  68. };
  69. Mailboxinit *boxinit[] = {
  70. imap4mbox,
  71. pop3mbox,
  72. plan9mbox,
  73. };
  74. char*
  75. syncmbox(Mailbox *mb, int doplumb)
  76. {
  77. return (*mb->sync)(mb, doplumb);
  78. }
  79. /* create a new mailbox */
  80. char*
  81. newmbox(char *path, char *name, int std)
  82. {
  83. Mailbox *mb, **l;
  84. char *p, *rv;
  85. int i;
  86. initheaders();
  87. mb = emalloc(sizeof(*mb));
  88. strncpy(mb->path, path, sizeof(mb->path)-1);
  89. if(name == nil){
  90. p = strrchr(path, '/');
  91. if(p == nil)
  92. p = path;
  93. else
  94. p++;
  95. if(*p == 0){
  96. free(mb);
  97. return "bad mbox name";
  98. }
  99. strncpy(mb->name, p, sizeof(mb->name)-1);
  100. } else {
  101. strncpy(mb->name, name, sizeof(mb->name)-1);
  102. }
  103. rv = nil;
  104. // check for a mailbox type
  105. for(i=0; i<nelem(boxinit); i++)
  106. if((rv = (*boxinit[i])(mb, path)) != Enotme)
  107. break;
  108. if(i == nelem(boxinit)){
  109. free(mb);
  110. return "bad path";
  111. }
  112. // on error, give up
  113. if(rv){
  114. free(mb);
  115. return rv;
  116. }
  117. // make sure name isn't taken
  118. qlock(&mbllock);
  119. for(l = &mbl; *l != nil; l = &(*l)->next){
  120. if(strcmp((*l)->name, mb->name) == 0){
  121. if(strcmp(path, (*l)->path) == 0)
  122. rv = nil;
  123. else
  124. rv = "mbox name in use";
  125. if(mb->close)
  126. (*mb->close)(mb);
  127. free(mb);
  128. qunlock(&mbllock);
  129. return rv;
  130. }
  131. }
  132. // always try locking
  133. mb->dolock = 1;
  134. mb->refs = 1;
  135. mb->next = nil;
  136. mb->id = newid();
  137. mb->root = newmessage(nil);
  138. mb->std = std;
  139. *l = mb;
  140. qunlock(&mbllock);
  141. qlock(mb);
  142. if(mb->ctl){
  143. henter(PATH(mb->id, Qmbox), "ctl",
  144. (Qid){PATH(mb->id, Qmboxctl), 0, QTFILE}, nil, mb);
  145. }
  146. rv = syncmbox(mb, 0);
  147. qunlock(mb);
  148. return rv;
  149. }
  150. // close the named mailbox
  151. void
  152. freembox(char *name)
  153. {
  154. Mailbox **l, *mb;
  155. qlock(&mbllock);
  156. for(l=&mbl; *l != nil; l=&(*l)->next){
  157. if(strcmp(name, (*l)->name) == 0){
  158. mb = *l;
  159. *l = mb->next;
  160. mboxdecref(mb);
  161. break;
  162. }
  163. }
  164. hfree(PATH(0, Qtop), name);
  165. qunlock(&mbllock);
  166. }
  167. static void
  168. initheaders(void)
  169. {
  170. Header *h;
  171. static int already;
  172. if(already)
  173. return;
  174. already = 1;
  175. for(h = head; h->type != nil; h++)
  176. h->len = strlen(h->type);
  177. }
  178. /*
  179. * parse a Unix style header
  180. */
  181. void
  182. parseunix(Message *m)
  183. {
  184. char *p;
  185. String *h;
  186. h = s_new();
  187. for(p = m->start + 5; *p && *p != '\r' && *p != '\n'; p++)
  188. s_putc(h, *p);
  189. s_terminate(h);
  190. s_restart(h);
  191. m->unixfrom = s_parse(h, s_reset(m->unixfrom));
  192. m->unixdate = s_append(s_reset(m->unixdate), h->ptr);
  193. s_free(h);
  194. }
  195. /*
  196. * parse a message
  197. */
  198. void
  199. parseheaders(Message *m, int justmime, Mailbox *mb, int addfrom)
  200. {
  201. String *hl;
  202. Header *h;
  203. char *p, *q;
  204. int i;
  205. if(m->whole == m->whole->whole){
  206. henter(PATH(mb->id, Qmbox), m->name,
  207. (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
  208. } else {
  209. henter(PATH(m->whole->id, Qdir), m->name,
  210. (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
  211. }
  212. for(i = 0; i < Qmax; i++)
  213. henter(PATH(m->id, Qdir), dirtab[i],
  214. (Qid){PATH(m->id, i), 0, QTFILE}, m, mb);
  215. // parse mime headers
  216. p = m->header;
  217. hl = s_new();
  218. while(headerline(&p, hl)){
  219. if(justmime)
  220. h = &head[Mhead];
  221. else
  222. h = head;
  223. for(; h->type; h++){
  224. if(cistrncmp(s_to_c(hl), h->type, h->len) == 0){
  225. (*h->f)(m, h, s_to_c(hl));
  226. break;
  227. }
  228. }
  229. s_reset(hl);
  230. }
  231. s_free(hl);
  232. // the blank line isn't really part of the body or header
  233. if(justmime){
  234. m->mhend = p;
  235. m->hend = m->header;
  236. } else {
  237. m->hend = p;
  238. }
  239. if(*p == '\n')
  240. p++;
  241. m->rbody = m->body = p;
  242. // if type is text, get any nulls out of the body. This is
  243. // for the two seans and imap clients that get confused.
  244. if(strncmp(s_to_c(m->type), "text/", 5) == 0)
  245. nullsqueeze(m);
  246. //
  247. // cobble together Unix-style from line
  248. // for local mailbox messages, we end up recreating the
  249. // original header.
  250. // for pop3 messages, the best we can do is
  251. // use the From: information and the RFC822 date.
  252. //
  253. if(m->unixdate == nil || strcmp(s_to_c(m->unixdate), "???") == 0
  254. || strcmp(s_to_c(m->unixdate), "Thu Jan 1 00:00:00 GMT 1970") == 0){
  255. if(m->unixdate){
  256. s_free(m->unixdate);
  257. m->unixdate = nil;
  258. }
  259. // look for the date in the first Received: line.
  260. // it's likely to be the right time zone (it's
  261. // the local system) and in a convenient format.
  262. if(cistrncmp(m->header, "received:", 9)==0){
  263. if((q = strchr(m->header, ';')) != nil){
  264. p = q;
  265. while((p = strchr(p, '\n')) != nil){
  266. if(p[1] != ' ' && p[1] != '\t' && p[1] != '\n')
  267. break;
  268. p++;
  269. }
  270. if(p){
  271. *p = '\0';
  272. m->unixdate = date822tounix(q+1);
  273. *p = '\n';
  274. }
  275. }
  276. }
  277. // fall back on the rfc822 date
  278. if(m->unixdate==nil && m->date822)
  279. m->unixdate = date822tounix(s_to_c(m->date822));
  280. }
  281. if(m->unixheader != nil)
  282. s_free(m->unixheader);
  283. // only fake header for top-level messages for pop3 and imap4
  284. // clients (those protocols don't include the unix header).
  285. // adding the unix header all the time screws up mime-attached
  286. // rfc822 messages.
  287. if(!addfrom && !m->unixfrom){
  288. m->unixheader = nil;
  289. return;
  290. }
  291. m->unixheader = s_copy("From ");
  292. if(m->unixfrom && strcmp(s_to_c(m->unixfrom), "???") != 0)
  293. s_append(m->unixheader, s_to_c(m->unixfrom));
  294. else if(m->from822)
  295. s_append(m->unixheader, s_to_c(m->from822));
  296. else
  297. s_append(m->unixheader, "???");
  298. s_append(m->unixheader, " ");
  299. if(m->unixdate)
  300. s_append(m->unixheader, s_to_c(m->unixdate));
  301. else
  302. s_append(m->unixheader, "Thu Jan 1 00:00:00 GMT 1970");
  303. s_append(m->unixheader, "\n");
  304. }
  305. String*
  306. promote(String **sp)
  307. {
  308. String *s;
  309. if(*sp != nil)
  310. s = s_clone(*sp);
  311. else
  312. s = nil;
  313. return s;
  314. }
  315. void
  316. parsebody(Message *m, Mailbox *mb)
  317. {
  318. Message *nm;
  319. // recurse
  320. if(strncmp(s_to_c(m->type), "multipart/", 10) == 0){
  321. parseattachments(m, mb);
  322. } else if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
  323. decode(m);
  324. parseattachments(m, mb);
  325. nm = m->part;
  326. // promote headers
  327. if(m->replyto822 == nil && m->from822 == nil && m->sender822 == nil){
  328. m->from822 = promote(&nm->from822);
  329. m->to822 = promote(&nm->to822);
  330. m->date822 = promote(&nm->date822);
  331. m->sender822 = promote(&nm->sender822);
  332. m->replyto822 = promote(&nm->replyto822);
  333. m->subject822 = promote(&nm->subject822);
  334. m->unixdate = promote(&nm->unixdate);
  335. }
  336. }
  337. }
  338. void
  339. parse(Message *m, int justmime, Mailbox *mb, int addfrom)
  340. {
  341. parseheaders(m, justmime, mb, addfrom);
  342. parsebody(m, mb);
  343. }
  344. static void
  345. parseattachments(Message *m, Mailbox *mb)
  346. {
  347. Message *nm, **l;
  348. char *p, *x;
  349. // if there's a boundary, recurse...
  350. if(m->boundary != nil){
  351. p = m->body;
  352. nm = nil;
  353. l = &m->part;
  354. for(;;){
  355. x = strstr(p, s_to_c(m->boundary));
  356. /* no boundary, we're done */
  357. if(x == nil){
  358. if(nm != nil)
  359. nm->rbend = nm->bend = nm->end = m->bend;
  360. break;
  361. }
  362. /* boundary must be at the start of a line */
  363. if(x != m->body && *(x-1) != '\n'){
  364. p = x+1;
  365. continue;
  366. }
  367. if(nm != nil)
  368. nm->rbend = nm->bend = nm->end = x;
  369. x += strlen(s_to_c(m->boundary));
  370. /* is this the last part? ignore anything after it */
  371. if(strncmp(x, "--", 2) == 0)
  372. break;
  373. p = strchr(x, '\n');
  374. if(p == nil)
  375. break;
  376. nm = newmessage(m);
  377. nm->start = nm->header = nm->body = nm->rbody = ++p;
  378. nm->mheader = nm->header;
  379. *l = nm;
  380. l = &nm->next;
  381. }
  382. for(nm = m->part; nm != nil; nm = nm->next)
  383. parse(nm, 1, mb, 0);
  384. return;
  385. }
  386. // if we've got an rfc822 message, recurse...
  387. if(strcmp(s_to_c(m->type), "message/rfc822") == 0){
  388. nm = newmessage(m);
  389. m->part = nm;
  390. nm->start = nm->header = nm->body = nm->rbody = m->body;
  391. nm->end = nm->bend = nm->rbend = m->bend;
  392. parse(nm, 0, mb, 0);
  393. }
  394. }
  395. /*
  396. * pick up a header line
  397. */
  398. static int
  399. headerline(char **pp, String *hl)
  400. {
  401. char *p, *x;
  402. s_reset(hl);
  403. p = *pp;
  404. x = strpbrk(p, ":\n");
  405. if(x == nil || *x == '\n')
  406. return 0;
  407. for(;;){
  408. x = strchr(p, '\n');
  409. if(x == nil)
  410. x = p + strlen(p);
  411. s_nappend(hl, p, x-p);
  412. p = x;
  413. if(*p != '\n' || *++p != ' ' && *p != '\t')
  414. break;
  415. while(*p == ' ' || *p == '\t')
  416. p++;
  417. s_putc(hl, ' ');
  418. }
  419. *pp = p;
  420. return 1;
  421. }
  422. static String*
  423. addr822(char *p)
  424. {
  425. String *s, *list;
  426. int incomment, addrdone, inanticomment, quoted;
  427. int n;
  428. int c;
  429. list = s_new();
  430. s = s_new();
  431. quoted = incomment = addrdone = inanticomment = 0;
  432. n = 0;
  433. for(; *p; p++){
  434. c = *p;
  435. // whitespace is ignored
  436. if(!quoted && isspace(c) || c == '\r')
  437. continue;
  438. // strings are always treated as atoms
  439. if(!quoted && c == '"'){
  440. if(!addrdone && !incomment)
  441. s_putc(s, c);
  442. for(p++; *p; p++){
  443. if(!addrdone && !incomment)
  444. s_putc(s, *p);
  445. if(!quoted && *p == '"')
  446. break;
  447. if(*p == '\\')
  448. quoted = 1;
  449. else
  450. quoted = 0;
  451. }
  452. if(*p == 0)
  453. break;
  454. quoted = 0;
  455. continue;
  456. }
  457. // ignore everything in an expicit comment
  458. if(!quoted && c == '('){
  459. incomment = 1;
  460. continue;
  461. }
  462. if(incomment){
  463. if(!quoted && c == ')')
  464. incomment = 0;
  465. quoted = 0;
  466. continue;
  467. }
  468. // anticomments makes everything outside of them comments
  469. if(!quoted && c == '<' && !inanticomment){
  470. inanticomment = 1;
  471. s = s_reset(s);
  472. continue;
  473. }
  474. if(!quoted && c == '>' && inanticomment){
  475. addrdone = 1;
  476. inanticomment = 0;
  477. continue;
  478. }
  479. // commas separate addresses
  480. if(!quoted && c == ',' && !inanticomment){
  481. s_terminate(s);
  482. addrdone = 0;
  483. if(n++ != 0)
  484. s_append(list, " ");
  485. s_append(list, s_to_c(s));
  486. s = s_reset(s);
  487. continue;
  488. }
  489. // what's left is part of the address
  490. s_putc(s, c);
  491. // quoted characters are recognized only as characters
  492. if(c == '\\')
  493. quoted = 1;
  494. else
  495. quoted = 0;
  496. }
  497. if(*s_to_c(s) != 0){
  498. s_terminate(s);
  499. if(n++ != 0)
  500. s_append(list, " ");
  501. s_append(list, s_to_c(s));
  502. }
  503. s_free(s);
  504. if(n == 0){
  505. s_free(list);
  506. return nil;
  507. }
  508. return list;
  509. }
  510. static void
  511. to822(Message *m, Header *h, char *p)
  512. {
  513. p += strlen(h->type);
  514. s_free(m->to822);
  515. m->to822 = addr822(p);
  516. }
  517. static void
  518. cc822(Message *m, Header *h, char *p)
  519. {
  520. p += strlen(h->type);
  521. s_free(m->cc822);
  522. m->cc822 = addr822(p);
  523. }
  524. static void
  525. bcc822(Message *m, Header *h, char *p)
  526. {
  527. p += strlen(h->type);
  528. s_free(m->bcc822);
  529. m->bcc822 = addr822(p);
  530. }
  531. static void
  532. from822(Message *m, Header *h, char *p)
  533. {
  534. p += strlen(h->type);
  535. s_free(m->from822);
  536. m->from822 = addr822(p);
  537. }
  538. static void
  539. sender822(Message *m, Header *h, char *p)
  540. {
  541. p += strlen(h->type);
  542. s_free(m->sender822);
  543. m->sender822 = addr822(p);
  544. }
  545. static void
  546. replyto822(Message *m, Header *h, char *p)
  547. {
  548. p += strlen(h->type);
  549. s_free(m->replyto822);
  550. m->replyto822 = addr822(p);
  551. }
  552. static void
  553. mimeversion(Message *m, Header *h, char *p)
  554. {
  555. p += strlen(h->type);
  556. s_free(m->mimeversion);
  557. m->mimeversion = addr822(p);
  558. }
  559. static void
  560. killtrailingwhite(char *p)
  561. {
  562. char *e;
  563. e = p + strlen(p) - 1;
  564. while(e > p && isspace(*e))
  565. *e-- = 0;
  566. }
  567. static void
  568. date822(Message *m, Header *h, char *p)
  569. {
  570. p += strlen(h->type);
  571. p = skipwhite(p);
  572. s_free(m->date822);
  573. m->date822 = s_copy(p);
  574. p = s_to_c(m->date822);
  575. killtrailingwhite(p);
  576. }
  577. static void
  578. subject822(Message *m, Header *h, char *p)
  579. {
  580. p += strlen(h->type);
  581. p = skipwhite(p);
  582. s_free(m->subject822);
  583. m->subject822 = s_copy(p);
  584. p = s_to_c(m->subject822);
  585. killtrailingwhite(p);
  586. }
  587. static void
  588. inreplyto822(Message *m, Header *h, char *p)
  589. {
  590. p += strlen(h->type);
  591. p = skipwhite(p);
  592. s_free(m->inreplyto822);
  593. m->inreplyto822 = s_copy(p);
  594. p = s_to_c(m->inreplyto822);
  595. killtrailingwhite(p);
  596. }
  597. static void
  598. messageid822(Message *m, Header *h, char *p)
  599. {
  600. p += strlen(h->type);
  601. p = skipwhite(p);
  602. s_free(m->messageid822);
  603. m->messageid822 = s_copy(p);
  604. p = s_to_c(m->messageid822);
  605. killtrailingwhite(p);
  606. }
  607. static int
  608. isattribute(char **pp, char *attr)
  609. {
  610. char *p;
  611. int n;
  612. n = strlen(attr);
  613. p = *pp;
  614. if(cistrncmp(p, attr, n) != 0)
  615. return 0;
  616. p += n;
  617. while(*p == ' ')
  618. p++;
  619. if(*p++ != '=')
  620. return 0;
  621. while(*p == ' ')
  622. p++;
  623. *pp = p;
  624. return 1;
  625. }
  626. static void
  627. ctype(Message *m, Header *h, char *p)
  628. {
  629. String *s;
  630. p += h->len;
  631. p = skipwhite(p);
  632. p = getstring(p, m->type, 1);
  633. while(*p){
  634. if(isattribute(&p, "boundary")){
  635. s = s_new();
  636. p = getstring(p, s, 0);
  637. m->boundary = s_reset(m->boundary);
  638. s_append(m->boundary, "--");
  639. s_append(m->boundary, s_to_c(s));
  640. s_free(s);
  641. } else if(cistrncmp(p, "multipart", 9) == 0){
  642. /*
  643. * the first unbounded part of a multipart message,
  644. * the preamble, is not displayed or saved
  645. */
  646. } else if(isattribute(&p, "name")){
  647. if(m->filename == nil)
  648. setfilename(m, p);
  649. } else if(isattribute(&p, "charset")){
  650. p = getstring(p, s_reset(m->charset), 0);
  651. }
  652. p = skiptosemi(p);
  653. }
  654. }
  655. static void
  656. cencoding(Message *m, Header *h, char *p)
  657. {
  658. p += h->len;
  659. p = skipwhite(p);
  660. if(cistrncmp(p, "base64", 6) == 0)
  661. m->encoding = Ebase64;
  662. else if(cistrncmp(p, "quoted-printable", 16) == 0)
  663. m->encoding = Equoted;
  664. }
  665. static void
  666. cdisposition(Message *m, Header *h, char *p)
  667. {
  668. p += h->len;
  669. p = skipwhite(p);
  670. while(*p){
  671. if(cistrncmp(p, "inline", 6) == 0){
  672. m->disposition = Dinline;
  673. } else if(cistrncmp(p, "attachment", 10) == 0){
  674. m->disposition = Dfile;
  675. } else if(cistrncmp(p, "filename=", 9) == 0){
  676. p += 9;
  677. setfilename(m, p);
  678. }
  679. p = skiptosemi(p);
  680. }
  681. }
  682. ulong msgallocd, msgfreed;
  683. Message*
  684. newmessage(Message *parent)
  685. {
  686. static int id;
  687. Message *m;
  688. msgallocd++;
  689. m = emalloc(sizeof(*m));
  690. memset(m, 0, sizeof(*m));
  691. m->disposition = Dnone;
  692. m->type = s_copy("text/plain");
  693. m->charset = s_copy("iso-8859-1");
  694. m->id = newid();
  695. if(parent)
  696. sprint(m->name, "%d", ++(parent->subname));
  697. if(parent == nil)
  698. parent = m;
  699. m->whole = parent;
  700. m->hlen = -1;
  701. return m;
  702. }
  703. // delete a message from a mailbox
  704. void
  705. delmessage(Mailbox *mb, Message *m)
  706. {
  707. Message **l;
  708. int i;
  709. mb->vers++;
  710. msgfreed++;
  711. if(m->whole != m){
  712. // unchain from parent
  713. for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
  714. ;
  715. if(*l != nil)
  716. *l = m->next;
  717. // clear out of name lookup hash table
  718. if(m->whole->whole == m->whole)
  719. hfree(PATH(mb->id, Qmbox), m->name);
  720. else
  721. hfree(PATH(m->whole->id, Qdir), m->name);
  722. for(i = 0; i < Qmax; i++)
  723. hfree(PATH(m->id, Qdir), dirtab[i]);
  724. }
  725. /* recurse through sub-parts */
  726. while(m->part)
  727. delmessage(mb, m->part);
  728. /* free memory */
  729. if(m->mallocd)
  730. free(m->start);
  731. if(m->hallocd)
  732. free(m->header);
  733. if(m->ballocd)
  734. free(m->body);
  735. s_free(m->unixfrom);
  736. s_free(m->unixdate);
  737. s_free(m->unixheader);
  738. s_free(m->from822);
  739. s_free(m->sender822);
  740. s_free(m->to822);
  741. s_free(m->bcc822);
  742. s_free(m->cc822);
  743. s_free(m->replyto822);
  744. s_free(m->date822);
  745. s_free(m->inreplyto822);
  746. s_free(m->subject822);
  747. s_free(m->messageid822);
  748. s_free(m->addrs);
  749. s_free(m->mimeversion);
  750. s_free(m->sdigest);
  751. s_free(m->boundary);
  752. s_free(m->type);
  753. s_free(m->charset);
  754. s_free(m->filename);
  755. free(m);
  756. }
  757. // mark messages (identified by path) for deletion
  758. void
  759. delmessages(int ac, char **av)
  760. {
  761. Mailbox *mb;
  762. Message *m;
  763. int i, needwrite;
  764. qlock(&mbllock);
  765. for(mb = mbl; mb != nil; mb = mb->next)
  766. if(strcmp(av[0], mb->name) == 0){
  767. qlock(mb);
  768. break;
  769. }
  770. qunlock(&mbllock);
  771. if(mb == nil)
  772. return;
  773. needwrite = 0;
  774. for(i = 1; i < ac; i++){
  775. for(m = mb->root->part; m != nil; m = m->next)
  776. if(strcmp(m->name, av[i]) == 0){
  777. if(!m->deleted){
  778. mailplumb(mb, m, 1);
  779. needwrite = 1;
  780. m->deleted = 1;
  781. logmsg("deleting", m);
  782. }
  783. break;
  784. }
  785. }
  786. if(needwrite)
  787. syncmbox(mb, 1);
  788. qunlock(mb);
  789. }
  790. /*
  791. * the following are called with the mailbox qlocked
  792. */
  793. void
  794. msgincref(Message *m)
  795. {
  796. m->refs++;
  797. }
  798. void
  799. msgdecref(Mailbox *mb, Message *m)
  800. {
  801. m->refs--;
  802. if(m->refs == 0 && m->deleted)
  803. syncmbox(mb, 1);
  804. }
  805. /*
  806. * the following are called with mbllock'd
  807. */
  808. void
  809. mboxincref(Mailbox *mb)
  810. {
  811. assert(mb->refs > 0);
  812. mb->refs++;
  813. }
  814. void
  815. mboxdecref(Mailbox *mb)
  816. {
  817. assert(mb->refs > 0);
  818. qlock(mb);
  819. mb->refs--;
  820. if(mb->refs == 0){
  821. delmessage(mb, mb->root);
  822. if(mb->ctl)
  823. hfree(PATH(mb->id, Qmbox), "ctl");
  824. if(mb->close)
  825. (*mb->close)(mb);
  826. free(mb);
  827. } else
  828. qunlock(mb);
  829. }
  830. int
  831. cistrncmp(char *a, char *b, int n)
  832. {
  833. while(n-- > 0){
  834. if(tolower(*a++) != tolower(*b++))
  835. return -1;
  836. }
  837. return 0;
  838. }
  839. int
  840. cistrcmp(char *a, char *b)
  841. {
  842. for(;;){
  843. if(tolower(*a) != tolower(*b++))
  844. return -1;
  845. if(*a++ == 0)
  846. break;
  847. }
  848. return 0;
  849. }
  850. static char*
  851. skipwhite(char *p)
  852. {
  853. while(isspace(*p))
  854. p++;
  855. return p;
  856. }
  857. static char*
  858. skiptosemi(char *p)
  859. {
  860. while(*p && *p != ';')
  861. p++;
  862. while(*p == ';' || isspace(*p))
  863. p++;
  864. return p;
  865. }
  866. static char*
  867. getstring(char *p, String *s, int dolower)
  868. {
  869. s = s_reset(s);
  870. p = skipwhite(p);
  871. if(*p == '"'){
  872. p++;
  873. for(;*p && *p != '"'; p++)
  874. if(dolower)
  875. s_putc(s, tolower(*p));
  876. else
  877. s_putc(s, *p);
  878. if(*p == '"')
  879. p++;
  880. s_terminate(s);
  881. return p;
  882. }
  883. for(; *p && !isspace(*p) && *p != ';'; p++)
  884. if(dolower)
  885. s_putc(s, tolower(*p));
  886. else
  887. s_putc(s, *p);
  888. s_terminate(s);
  889. return p;
  890. }
  891. static void
  892. setfilename(Message *m, char *p)
  893. {
  894. m->filename = s_reset(m->filename);
  895. getstring(p, m->filename, 0);
  896. for(p = s_to_c(m->filename); *p; p++)
  897. if(*p == ' ' || *p == '\t' || *p == ';')
  898. *p = '_';
  899. }
  900. //
  901. // undecode message body
  902. //
  903. void
  904. decode(Message *m)
  905. {
  906. int i, len;
  907. char *x;
  908. if(m->decoded)
  909. return;
  910. switch(m->encoding){
  911. case Ebase64:
  912. len = m->bend - m->body;
  913. i = (len*3)/4+1; // room for max chars + null
  914. x = emalloc(i);
  915. len = dec64((uchar*)x, i, m->body, len);
  916. if(m->ballocd)
  917. free(m->body);
  918. m->body = x;
  919. m->bend = x + len;
  920. m->ballocd = 1;
  921. break;
  922. case Equoted:
  923. len = m->bend - m->body;
  924. x = emalloc(len+2); // room for null and possible extra nl
  925. len = decquoted(x, m->body, m->bend, 0);
  926. if(m->ballocd)
  927. free(m->body);
  928. m->body = x;
  929. m->bend = x + len;
  930. m->ballocd = 1;
  931. break;
  932. default:
  933. break;
  934. }
  935. m->decoded = 1;
  936. }
  937. // convert latin1 to utf
  938. void
  939. convert(Message *m)
  940. {
  941. int len;
  942. char *x;
  943. // don't convert if we're not a leaf, not text, or already converted
  944. if(m->converted)
  945. return;
  946. if(m->part != nil)
  947. return;
  948. if(cistrncmp(s_to_c(m->type), "text", 4) != 0)
  949. return;
  950. len = xtoutf(s_to_c(m->charset), &x, m->body, m->bend);
  951. if(len > 0){
  952. if(m->ballocd)
  953. free(m->body);
  954. m->body = x;
  955. m->bend = x + len;
  956. m->ballocd = 1;
  957. }
  958. m->converted = 1;
  959. }
  960. static int
  961. hex2int(int x)
  962. {
  963. if(x >= '0' && x <= '9')
  964. return x - '0';
  965. if(x >= 'A' && x <= 'F')
  966. return (x - 'A') + 10;
  967. if(x >= 'a' && x <= 'f')
  968. return (x - 'a') + 10;
  969. return 0;
  970. }
  971. // underscores are translated in 2047 headers (uscores=1)
  972. // but not in the body (uscores=0)
  973. static char*
  974. decquotedline(char *out, char *in, char *e, int uscores)
  975. {
  976. int c, soft;
  977. /* dump trailing white space */
  978. while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
  979. e--;
  980. /* trailing '=' means no newline */
  981. if(*e == '='){
  982. soft = 1;
  983. e--;
  984. } else
  985. soft = 0;
  986. while(in <= e){
  987. c = (*in++) & 0xff;
  988. switch(c){
  989. case '_':
  990. if(uscores){
  991. *out++ = ' ';
  992. break;
  993. }
  994. default:
  995. *out++ = c;
  996. break;
  997. case '=':
  998. c = hex2int(*in++)<<4;
  999. c |= hex2int(*in++);
  1000. *out++ = c;
  1001. break;
  1002. }
  1003. }
  1004. if(!soft)
  1005. *out++ = '\n';
  1006. *out = 0;
  1007. return out;
  1008. }
  1009. int
  1010. decquoted(char *out, char *in, char *e, int uscores)
  1011. {
  1012. char *p, *nl;
  1013. p = out;
  1014. while((nl = strchr(in, '\n')) != nil && nl < e){
  1015. p = decquotedline(p, in, nl, uscores);
  1016. in = nl + 1;
  1017. }
  1018. if(in < e)
  1019. p = decquotedline(p, in, e-1, uscores);
  1020. // make sure we end with a new line
  1021. if(*(p-1) != '\n'){
  1022. *p++ = '\n';
  1023. *p = 0;
  1024. }
  1025. return p - out;
  1026. }
  1027. static char*
  1028. lowercase(char *p)
  1029. {
  1030. char *op;
  1031. int c;
  1032. for(op = p; c = *p; p++)
  1033. if(isupper(c))
  1034. *p = tolower(c);
  1035. return op;
  1036. }
  1037. // translate latin1 directly since it fits neatly in utf
  1038. static int
  1039. latin1toutf(char **out, char *in, char *e)
  1040. {
  1041. int n;
  1042. char *p;
  1043. Rune r;
  1044. n = 0;
  1045. for(p = in; p < e; p++)
  1046. if(*p & 0x80)
  1047. n++;
  1048. if(n == 0)
  1049. return 0;
  1050. n += e-in;
  1051. *out = p = malloc(n+1);
  1052. if(p == nil)
  1053. return 0;
  1054. for(; in < e; in++){
  1055. r = (uchar)*in;
  1056. p += runetochar(p, &r);
  1057. }
  1058. *p = 0;
  1059. return p - *out;
  1060. }
  1061. // translate any thing using the tcs program
  1062. int
  1063. xtoutf(char *charset, char **out, char *in, char *e)
  1064. {
  1065. char *av[4];
  1066. int totcs[2];
  1067. int fromtcs[2];
  1068. int n, len, sofar;
  1069. char *p;
  1070. // might not need to convert
  1071. if(cistrcmp(charset, "us-ascii") == 0 || cistrcmp(charset, "utf-8") == 0)
  1072. return 0;
  1073. if(cistrcmp(charset, "iso-8859-1") == 0)
  1074. return latin1toutf(out, in, e);
  1075. len = e-in+1;
  1076. sofar = 0;
  1077. *out = p = malloc(len+1);
  1078. if(p == nil)
  1079. return 0;
  1080. av[0] = charset;
  1081. av[1] = "-f";
  1082. av[2] = charset;
  1083. av[3] = 0;
  1084. if(pipe(totcs) < 0)
  1085. goto error;
  1086. if(pipe(fromtcs) < 0){
  1087. close(totcs[0]); close(totcs[1]);
  1088. goto error;
  1089. }
  1090. switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
  1091. case -1:
  1092. close(fromtcs[0]); close(fromtcs[1]);
  1093. close(totcs[0]); close(totcs[1]);
  1094. goto error;
  1095. case 0:
  1096. close(fromtcs[0]); close(totcs[1]);
  1097. dup(fromtcs[1], 1);
  1098. dup(totcs[0], 0);
  1099. close(fromtcs[1]); close(totcs[0]);
  1100. dup(open("/dev/null", OWRITE), 2);
  1101. exec("/bin/tcs", av);
  1102. _exits(0);
  1103. default:
  1104. close(fromtcs[1]); close(totcs[0]);
  1105. switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
  1106. case -1:
  1107. close(fromtcs[0]); close(totcs[1]);
  1108. goto error;
  1109. case 0:
  1110. close(fromtcs[0]);
  1111. while(in < e){
  1112. n = write(totcs[1], in, e-in);
  1113. if(n <= 0)
  1114. break;
  1115. in += n;
  1116. }
  1117. close(totcs[1]);
  1118. _exits(0);
  1119. default:
  1120. close(totcs[1]);
  1121. for(;;){
  1122. n = read(fromtcs[0], &p[sofar], len-sofar);
  1123. if(n <= 0)
  1124. break;
  1125. sofar += n;
  1126. p[sofar] = 0;
  1127. if(sofar == len){
  1128. len += 1024;
  1129. p = realloc(p, len+1);
  1130. if(p == nil)
  1131. goto error;
  1132. *out = p;
  1133. }
  1134. }
  1135. close(fromtcs[0]);
  1136. break;
  1137. }
  1138. break;
  1139. }
  1140. if(sofar == 0)
  1141. goto error;
  1142. return sofar;
  1143. error:
  1144. free(*out);
  1145. *out = nil;
  1146. return 0;
  1147. }
  1148. void *
  1149. emalloc(ulong n)
  1150. {
  1151. void *p;
  1152. p = mallocz(n, 1);
  1153. if(!p){
  1154. fprint(2, "%s: out of memory alloc %lud\n", argv0, n);
  1155. exits("out of memory");
  1156. }
  1157. setmalloctag(p, getcallerpc(&n));
  1158. return p;
  1159. }
  1160. void *
  1161. erealloc(void *p, ulong n)
  1162. {
  1163. if(n == 0)
  1164. n = 1;
  1165. p = realloc(p, n);
  1166. if(!p){
  1167. fprint(2, "%s: out of memory realloc %lud\n", argv0, n);
  1168. exits("out of memory");
  1169. }
  1170. setrealloctag(p, getcallerpc(&p));
  1171. return p;
  1172. }
  1173. void
  1174. mailplumb(Mailbox *mb, Message *m, int delete)
  1175. {
  1176. Plumbmsg p;
  1177. Plumbattr a[7];
  1178. char buf[256];
  1179. int ai;
  1180. char lenstr[10], *from, *subject, *date;
  1181. static int fd = -1;
  1182. if(m->subject822 == nil)
  1183. subject = "";
  1184. else
  1185. subject = s_to_c(m->subject822);
  1186. if(m->from822 != nil)
  1187. from = s_to_c(m->from822);
  1188. else if(m->unixfrom != nil)
  1189. from = s_to_c(m->unixfrom);
  1190. else
  1191. from = "";
  1192. if(m->unixdate != nil)
  1193. date = s_to_c(m->unixdate);
  1194. else
  1195. date = "";
  1196. sprint(lenstr, "%ld", m->end-m->start);
  1197. if(biffing && !delete)
  1198. print("[ %s / %s / %s ]\n", from, subject, lenstr);
  1199. if(!plumbing)
  1200. return;
  1201. if(fd < 0)
  1202. fd = plumbopen("send", OWRITE);
  1203. if(fd < 0)
  1204. return;
  1205. p.src = "mailfs";
  1206. p.dst = "seemail";
  1207. p.wdir = "/mail/fs";
  1208. p.type = "text";
  1209. ai = 0;
  1210. a[ai].name = "filetype";
  1211. a[ai].value = "mail";
  1212. a[++ai].name = "sender";
  1213. a[ai].value = from;
  1214. a[ai-1].next = &a[ai];
  1215. a[++ai].name = "length";
  1216. a[ai].value = lenstr;
  1217. a[ai-1].next = &a[ai];
  1218. a[++ai].name = "mailtype";
  1219. a[ai].value = delete?"delete":"new";
  1220. a[ai-1].next = &a[ai];
  1221. a[++ai].name = "date";
  1222. a[ai].value = date;
  1223. a[ai-1].next = &a[ai];
  1224. if(m->sdigest){
  1225. a[++ai].name = "digest";
  1226. a[ai].value = s_to_c(m->sdigest);
  1227. a[ai-1].next = &a[ai];
  1228. }
  1229. a[ai].next = nil;
  1230. p.attr = a;
  1231. snprint(buf, sizeof(buf), "%s/%s/%s",
  1232. mntpt, mb->name, m->name);
  1233. p.ndata = strlen(buf);
  1234. p.data = buf;
  1235. plumbsend(fd, &p);
  1236. }
  1237. //
  1238. // count the number of lines in the body (for imap4)
  1239. //
  1240. void
  1241. countlines(Message *m)
  1242. {
  1243. int i;
  1244. char *p;
  1245. i = 0;
  1246. for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p+1, '\n'))
  1247. i++;
  1248. sprint(m->lines, "%d", i);
  1249. }
  1250. char *LOG = "fs";
  1251. void
  1252. logmsg(char *s, Message *m)
  1253. {
  1254. int pid;
  1255. if(!logging)
  1256. return;
  1257. pid = getpid();
  1258. if(m == nil)
  1259. syslog(0, LOG, "%s.%d: %s", user, pid, s);
  1260. else
  1261. syslog(0, LOG, "%s.%d: %s msg from %s digest %s",
  1262. user, pid, s,
  1263. m->from822 ? s_to_c(m->from822) : "?",
  1264. s_to_c(m->sdigest));
  1265. }
  1266. /*
  1267. * squeeze nulls out of the body
  1268. */
  1269. static void
  1270. nullsqueeze(Message *m)
  1271. {
  1272. char *p, *q;
  1273. q = memchr(m->body, 0, m->end-m->body);
  1274. if(q == nil)
  1275. return;
  1276. for(p = m->body; q < m->end; q++){
  1277. if(*q == 0)
  1278. continue;
  1279. *p++ = *q;
  1280. }
  1281. m->bend = m->rbend = m->end = p;
  1282. }
  1283. //
  1284. // convert an RFC822 date into a Unix style date
  1285. // for when the Unix From line isn't there (e.g. POP3).
  1286. // enough client programs depend on having a Unix date
  1287. // that it's easiest to write this conversion code once, right here.
  1288. //
  1289. // people don't follow RFC822 particularly closely,
  1290. // so we use strtotm, which is a bunch of heuristics.
  1291. //
  1292. extern int strtotm(char*, Tm*);
  1293. String*
  1294. date822tounix(char *s)
  1295. {
  1296. char *p, *q;
  1297. Tm tm;
  1298. if(strtotm(s, &tm) < 0)
  1299. return nil;
  1300. p = asctime(&tm);
  1301. if(q = strchr(p, '\n'))
  1302. *q = '\0';
  1303. return s_copy(p);
  1304. }