mbox.c 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513
  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. /* returns nil iff there are no addressees */
  423. static String*
  424. addr822(char *p)
  425. {
  426. String *s, *list;
  427. int incomment, addrdone, inanticomment, quoted;
  428. int n;
  429. int c;
  430. list = s_new();
  431. s = s_new();
  432. quoted = incomment = addrdone = inanticomment = 0;
  433. n = 0;
  434. for(; *p; p++){
  435. c = *p;
  436. // whitespace is ignored
  437. if(!quoted && isspace(c) || c == '\r')
  438. continue;
  439. // strings are always treated as atoms
  440. if(!quoted && c == '"'){
  441. if(!addrdone && !incomment)
  442. s_putc(s, c);
  443. for(p++; *p; p++){
  444. if(!addrdone && !incomment)
  445. s_putc(s, *p);
  446. if(!quoted && *p == '"')
  447. break;
  448. if(*p == '\\')
  449. quoted = 1;
  450. else
  451. quoted = 0;
  452. }
  453. if(*p == 0)
  454. break;
  455. quoted = 0;
  456. continue;
  457. }
  458. // ignore everything in an expicit comment
  459. if(!quoted && c == '('){
  460. incomment = 1;
  461. continue;
  462. }
  463. if(incomment){
  464. if(!quoted && c == ')')
  465. incomment = 0;
  466. quoted = 0;
  467. continue;
  468. }
  469. // anticomments makes everything outside of them comments
  470. if(!quoted && c == '<' && !inanticomment){
  471. inanticomment = 1;
  472. s = s_reset(s);
  473. continue;
  474. }
  475. if(!quoted && c == '>' && inanticomment){
  476. addrdone = 1;
  477. inanticomment = 0;
  478. continue;
  479. }
  480. // commas separate addresses
  481. if(!quoted && c == ',' && !inanticomment){
  482. s_terminate(s);
  483. addrdone = 0;
  484. if(n++ != 0)
  485. s_append(list, " ");
  486. s_append(list, s_to_c(s));
  487. s = s_reset(s);
  488. continue;
  489. }
  490. // what's left is part of the address
  491. s_putc(s, c);
  492. // quoted characters are recognized only as characters
  493. if(c == '\\')
  494. quoted = 1;
  495. else
  496. quoted = 0;
  497. }
  498. if(*s_to_c(s) != 0){
  499. s_terminate(s);
  500. if(n++ != 0)
  501. s_append(list, " ");
  502. s_append(list, s_to_c(s));
  503. }
  504. s_free(s);
  505. if(n == 0){ /* no addressees given, just the keyword */
  506. s_free(list);
  507. return nil;
  508. }
  509. return list;
  510. }
  511. /*
  512. * per rfc2822 §4.5.3, permit multiple to, cc and bcc headers by
  513. * concatenating their values.
  514. */
  515. static void
  516. to822(Message *m, Header *h, char *p)
  517. {
  518. String *s;
  519. p += strlen(h->type);
  520. s = addr822(p);
  521. if (m->to822 == nil)
  522. m->to822 = s;
  523. else if (s != nil) {
  524. s_append(m->to822, " ");
  525. s_append(m->to822, s_to_c(s));
  526. s_free(s);
  527. }
  528. }
  529. static void
  530. cc822(Message *m, Header *h, char *p)
  531. {
  532. String *s;
  533. p += strlen(h->type);
  534. s = addr822(p);
  535. if (m->cc822 == nil)
  536. m->cc822 = s;
  537. else if (s != nil) {
  538. s_append(m->cc822, " ");
  539. s_append(m->cc822, s_to_c(s));
  540. s_free(s);
  541. }
  542. }
  543. static void
  544. bcc822(Message *m, Header *h, char *p)
  545. {
  546. String *s;
  547. p += strlen(h->type);
  548. s = addr822(p);
  549. if (m->bcc822 == nil)
  550. m->bcc822 = s;
  551. else if (s != nil) {
  552. s_append(m->bcc822, " ");
  553. s_append(m->bcc822, s_to_c(s));
  554. s_free(s);
  555. }
  556. }
  557. static void
  558. from822(Message *m, Header *h, char *p)
  559. {
  560. p += strlen(h->type);
  561. s_free(m->from822);
  562. m->from822 = addr822(p);
  563. }
  564. static void
  565. sender822(Message *m, Header *h, char *p)
  566. {
  567. p += strlen(h->type);
  568. s_free(m->sender822);
  569. m->sender822 = addr822(p);
  570. }
  571. static void
  572. replyto822(Message *m, Header *h, char *p)
  573. {
  574. p += strlen(h->type);
  575. s_free(m->replyto822);
  576. m->replyto822 = addr822(p);
  577. }
  578. static void
  579. mimeversion(Message *m, Header *h, char *p)
  580. {
  581. p += strlen(h->type);
  582. s_free(m->mimeversion);
  583. m->mimeversion = addr822(p);
  584. }
  585. static void
  586. killtrailingwhite(char *p)
  587. {
  588. char *e;
  589. e = p + strlen(p) - 1;
  590. while(e > p && isspace(*e))
  591. *e-- = 0;
  592. }
  593. static void
  594. date822(Message *m, Header *h, char *p)
  595. {
  596. p += strlen(h->type);
  597. p = skipwhite(p);
  598. s_free(m->date822);
  599. m->date822 = s_copy(p);
  600. p = s_to_c(m->date822);
  601. killtrailingwhite(p);
  602. }
  603. static void
  604. subject822(Message *m, Header *h, char *p)
  605. {
  606. p += strlen(h->type);
  607. p = skipwhite(p);
  608. s_free(m->subject822);
  609. m->subject822 = s_copy(p);
  610. p = s_to_c(m->subject822);
  611. killtrailingwhite(p);
  612. }
  613. static void
  614. inreplyto822(Message *m, Header *h, char *p)
  615. {
  616. p += strlen(h->type);
  617. p = skipwhite(p);
  618. s_free(m->inreplyto822);
  619. m->inreplyto822 = s_copy(p);
  620. p = s_to_c(m->inreplyto822);
  621. killtrailingwhite(p);
  622. }
  623. static void
  624. messageid822(Message *m, Header *h, char *p)
  625. {
  626. p += strlen(h->type);
  627. p = skipwhite(p);
  628. s_free(m->messageid822);
  629. m->messageid822 = s_copy(p);
  630. p = s_to_c(m->messageid822);
  631. killtrailingwhite(p);
  632. }
  633. static int
  634. isattribute(char **pp, char *attr)
  635. {
  636. char *p;
  637. int n;
  638. n = strlen(attr);
  639. p = *pp;
  640. if(cistrncmp(p, attr, n) != 0)
  641. return 0;
  642. p += n;
  643. while(*p == ' ')
  644. p++;
  645. if(*p++ != '=')
  646. return 0;
  647. while(*p == ' ')
  648. p++;
  649. *pp = p;
  650. return 1;
  651. }
  652. static void
  653. ctype(Message *m, Header *h, char *p)
  654. {
  655. String *s;
  656. p += h->len;
  657. p = skipwhite(p);
  658. p = getstring(p, m->type, 1);
  659. while(*p){
  660. if(isattribute(&p, "boundary")){
  661. s = s_new();
  662. p = getstring(p, s, 0);
  663. m->boundary = s_reset(m->boundary);
  664. s_append(m->boundary, "--");
  665. s_append(m->boundary, s_to_c(s));
  666. s_free(s);
  667. } else if(cistrncmp(p, "multipart", 9) == 0){
  668. /*
  669. * the first unbounded part of a multipart message,
  670. * the preamble, is not displayed or saved
  671. */
  672. } else if(isattribute(&p, "name")){
  673. if(m->filename == nil)
  674. setfilename(m, p);
  675. } else if(isattribute(&p, "charset")){
  676. p = getstring(p, s_reset(m->charset), 0);
  677. }
  678. p = skiptosemi(p);
  679. }
  680. }
  681. static void
  682. cencoding(Message *m, Header *h, char *p)
  683. {
  684. p += h->len;
  685. p = skipwhite(p);
  686. if(cistrncmp(p, "base64", 6) == 0)
  687. m->encoding = Ebase64;
  688. else if(cistrncmp(p, "quoted-printable", 16) == 0)
  689. m->encoding = Equoted;
  690. }
  691. static void
  692. cdisposition(Message *m, Header *h, char *p)
  693. {
  694. p += h->len;
  695. p = skipwhite(p);
  696. while(*p){
  697. if(cistrncmp(p, "inline", 6) == 0){
  698. m->disposition = Dinline;
  699. } else if(cistrncmp(p, "attachment", 10) == 0){
  700. m->disposition = Dfile;
  701. } else if(cistrncmp(p, "filename=", 9) == 0){
  702. p += 9;
  703. setfilename(m, p);
  704. }
  705. p = skiptosemi(p);
  706. }
  707. }
  708. ulong msgallocd, msgfreed;
  709. Message*
  710. newmessage(Message *parent)
  711. {
  712. static int id;
  713. Message *m;
  714. msgallocd++;
  715. m = emalloc(sizeof(*m));
  716. memset(m, 0, sizeof(*m));
  717. m->disposition = Dnone;
  718. m->type = s_copy("text/plain");
  719. m->charset = s_copy("iso-8859-1");
  720. m->id = newid();
  721. if(parent)
  722. sprint(m->name, "%d", ++(parent->subname));
  723. if(parent == nil)
  724. parent = m;
  725. m->whole = parent;
  726. m->hlen = -1;
  727. return m;
  728. }
  729. // delete a message from a mailbox
  730. void
  731. delmessage(Mailbox *mb, Message *m)
  732. {
  733. Message **l;
  734. int i;
  735. mb->vers++;
  736. msgfreed++;
  737. if(m->whole != m){
  738. // unchain from parent
  739. for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
  740. ;
  741. if(*l != nil)
  742. *l = m->next;
  743. // clear out of name lookup hash table
  744. if(m->whole->whole == m->whole)
  745. hfree(PATH(mb->id, Qmbox), m->name);
  746. else
  747. hfree(PATH(m->whole->id, Qdir), m->name);
  748. for(i = 0; i < Qmax; i++)
  749. hfree(PATH(m->id, Qdir), dirtab[i]);
  750. }
  751. /* recurse through sub-parts */
  752. while(m->part)
  753. delmessage(mb, m->part);
  754. /* free memory */
  755. if(m->mallocd)
  756. free(m->start);
  757. if(m->hallocd)
  758. free(m->header);
  759. if(m->ballocd)
  760. free(m->body);
  761. s_free(m->unixfrom);
  762. s_free(m->unixdate);
  763. s_free(m->unixheader);
  764. s_free(m->from822);
  765. s_free(m->sender822);
  766. s_free(m->to822);
  767. s_free(m->bcc822);
  768. s_free(m->cc822);
  769. s_free(m->replyto822);
  770. s_free(m->date822);
  771. s_free(m->inreplyto822);
  772. s_free(m->subject822);
  773. s_free(m->messageid822);
  774. s_free(m->addrs);
  775. s_free(m->mimeversion);
  776. s_free(m->sdigest);
  777. s_free(m->boundary);
  778. s_free(m->type);
  779. s_free(m->charset);
  780. s_free(m->filename);
  781. free(m);
  782. }
  783. // mark messages (identified by path) for deletion
  784. void
  785. delmessages(int ac, char **av)
  786. {
  787. Mailbox *mb;
  788. Message *m;
  789. int i, needwrite;
  790. qlock(&mbllock);
  791. for(mb = mbl; mb != nil; mb = mb->next)
  792. if(strcmp(av[0], mb->name) == 0){
  793. qlock(mb);
  794. break;
  795. }
  796. qunlock(&mbllock);
  797. if(mb == nil)
  798. return;
  799. needwrite = 0;
  800. for(i = 1; i < ac; i++){
  801. for(m = mb->root->part; m != nil; m = m->next)
  802. if(strcmp(m->name, av[i]) == 0){
  803. if(!m->deleted){
  804. mailplumb(mb, m, 1);
  805. needwrite = 1;
  806. m->deleted = 1;
  807. logmsg("deleting", m);
  808. }
  809. break;
  810. }
  811. }
  812. if(needwrite)
  813. syncmbox(mb, 1);
  814. qunlock(mb);
  815. }
  816. /*
  817. * the following are called with the mailbox qlocked
  818. */
  819. void
  820. msgincref(Message *m)
  821. {
  822. m->refs++;
  823. }
  824. void
  825. msgdecref(Mailbox *mb, Message *m)
  826. {
  827. m->refs--;
  828. if(m->refs == 0 && m->deleted)
  829. syncmbox(mb, 1);
  830. }
  831. /*
  832. * the following are called with mbllock'd
  833. */
  834. void
  835. mboxincref(Mailbox *mb)
  836. {
  837. assert(mb->refs > 0);
  838. mb->refs++;
  839. }
  840. void
  841. mboxdecref(Mailbox *mb)
  842. {
  843. assert(mb->refs > 0);
  844. qlock(mb);
  845. mb->refs--;
  846. if(mb->refs == 0){
  847. delmessage(mb, mb->root);
  848. if(mb->ctl)
  849. hfree(PATH(mb->id, Qmbox), "ctl");
  850. if(mb->close)
  851. (*mb->close)(mb);
  852. free(mb);
  853. } else
  854. qunlock(mb);
  855. }
  856. int
  857. cistrncmp(char *a, char *b, int n)
  858. {
  859. while(n-- > 0){
  860. if(tolower(*a++) != tolower(*b++))
  861. return -1;
  862. }
  863. return 0;
  864. }
  865. int
  866. cistrcmp(char *a, char *b)
  867. {
  868. for(;;){
  869. if(tolower(*a) != tolower(*b++))
  870. return -1;
  871. if(*a++ == 0)
  872. break;
  873. }
  874. return 0;
  875. }
  876. static char*
  877. skipwhite(char *p)
  878. {
  879. while(isspace(*p))
  880. p++;
  881. return p;
  882. }
  883. static char*
  884. skiptosemi(char *p)
  885. {
  886. while(*p && *p != ';')
  887. p++;
  888. while(*p == ';' || isspace(*p))
  889. p++;
  890. return p;
  891. }
  892. static char*
  893. getstring(char *p, String *s, int dolower)
  894. {
  895. s = s_reset(s);
  896. p = skipwhite(p);
  897. if(*p == '"'){
  898. p++;
  899. for(;*p && *p != '"'; p++)
  900. if(dolower)
  901. s_putc(s, tolower(*p));
  902. else
  903. s_putc(s, *p);
  904. if(*p == '"')
  905. p++;
  906. s_terminate(s);
  907. return p;
  908. }
  909. for(; *p && !isspace(*p) && *p != ';'; p++)
  910. if(dolower)
  911. s_putc(s, tolower(*p));
  912. else
  913. s_putc(s, *p);
  914. s_terminate(s);
  915. return p;
  916. }
  917. static void
  918. setfilename(Message *m, char *p)
  919. {
  920. m->filename = s_reset(m->filename);
  921. getstring(p, m->filename, 0);
  922. for(p = s_to_c(m->filename); *p; p++)
  923. if(*p == ' ' || *p == '\t' || *p == ';')
  924. *p = '_';
  925. }
  926. //
  927. // undecode message body
  928. //
  929. void
  930. decode(Message *m)
  931. {
  932. int i, len;
  933. char *x;
  934. if(m->decoded)
  935. return;
  936. switch(m->encoding){
  937. case Ebase64:
  938. len = m->bend - m->body;
  939. i = (len*3)/4+1; // room for max chars + null
  940. x = emalloc(i);
  941. len = dec64((uchar*)x, i, m->body, len);
  942. if(m->ballocd)
  943. free(m->body);
  944. m->body = x;
  945. m->bend = x + len;
  946. m->ballocd = 1;
  947. break;
  948. case Equoted:
  949. len = m->bend - m->body;
  950. x = emalloc(len+2); // room for null and possible extra nl
  951. len = decquoted(x, m->body, m->bend, 0);
  952. if(m->ballocd)
  953. free(m->body);
  954. m->body = x;
  955. m->bend = x + len;
  956. m->ballocd = 1;
  957. break;
  958. default:
  959. break;
  960. }
  961. m->decoded = 1;
  962. }
  963. // convert latin1 to utf
  964. void
  965. convert(Message *m)
  966. {
  967. int len;
  968. char *x;
  969. // don't convert if we're not a leaf, not text, or already converted
  970. if(m->converted)
  971. return;
  972. if(m->part != nil)
  973. return;
  974. if(cistrncmp(s_to_c(m->type), "text", 4) != 0)
  975. return;
  976. len = xtoutf(s_to_c(m->charset), &x, m->body, m->bend);
  977. if(len > 0){
  978. if(m->ballocd)
  979. free(m->body);
  980. m->body = x;
  981. m->bend = x + len;
  982. m->ballocd = 1;
  983. }
  984. m->converted = 1;
  985. }
  986. static int
  987. hex2int(int x)
  988. {
  989. if(x >= '0' && x <= '9')
  990. return x - '0';
  991. if(x >= 'A' && x <= 'F')
  992. return (x - 'A') + 10;
  993. if(x >= 'a' && x <= 'f')
  994. return (x - 'a') + 10;
  995. return 0;
  996. }
  997. // underscores are translated in 2047 headers (uscores=1)
  998. // but not in the body (uscores=0)
  999. static char*
  1000. decquotedline(char *out, char *in, char *e, int uscores)
  1001. {
  1002. int c, soft;
  1003. /* dump trailing white space */
  1004. while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
  1005. e--;
  1006. /* trailing '=' means no newline */
  1007. if(*e == '='){
  1008. soft = 1;
  1009. e--;
  1010. } else
  1011. soft = 0;
  1012. while(in <= e){
  1013. c = (*in++) & 0xff;
  1014. switch(c){
  1015. case '_':
  1016. if(uscores){
  1017. *out++ = ' ';
  1018. break;
  1019. }
  1020. default:
  1021. *out++ = c;
  1022. break;
  1023. case '=':
  1024. c = hex2int(*in++)<<4;
  1025. c |= hex2int(*in++);
  1026. *out++ = c;
  1027. break;
  1028. }
  1029. }
  1030. if(!soft)
  1031. *out++ = '\n';
  1032. *out = 0;
  1033. return out;
  1034. }
  1035. int
  1036. decquoted(char *out, char *in, char *e, int uscores)
  1037. {
  1038. char *p, *nl;
  1039. p = out;
  1040. while((nl = strchr(in, '\n')) != nil && nl < e){
  1041. p = decquotedline(p, in, nl, uscores);
  1042. in = nl + 1;
  1043. }
  1044. if(in < e)
  1045. p = decquotedline(p, in, e-1, uscores);
  1046. // make sure we end with a new line
  1047. if(*(p-1) != '\n'){
  1048. *p++ = '\n';
  1049. *p = 0;
  1050. }
  1051. return p - out;
  1052. }
  1053. static char*
  1054. lowercase(char *p)
  1055. {
  1056. char *op;
  1057. int c;
  1058. for(op = p; c = *p; p++)
  1059. if(isupper(c))
  1060. *p = tolower(c);
  1061. return op;
  1062. }
  1063. // translate latin1 directly since it fits neatly in utf
  1064. static int
  1065. latin1toutf(char **out, char *in, char *e)
  1066. {
  1067. int n;
  1068. char *p;
  1069. Rune r;
  1070. n = 0;
  1071. for(p = in; p < e; p++)
  1072. if(*p & 0x80)
  1073. n++;
  1074. if(n == 0)
  1075. return 0;
  1076. n += e-in;
  1077. *out = p = malloc(n+1);
  1078. if(p == nil)
  1079. return 0;
  1080. for(; in < e; in++){
  1081. r = (uchar)*in;
  1082. p += runetochar(p, &r);
  1083. }
  1084. *p = 0;
  1085. return p - *out;
  1086. }
  1087. // translate any thing using the tcs program
  1088. int
  1089. xtoutf(char *charset, char **out, char *in, char *e)
  1090. {
  1091. char *av[4];
  1092. int totcs[2];
  1093. int fromtcs[2];
  1094. int n, len, sofar;
  1095. char *p;
  1096. // might not need to convert
  1097. if(cistrcmp(charset, "us-ascii") == 0 || cistrcmp(charset, "utf-8") == 0)
  1098. return 0;
  1099. if(cistrcmp(charset, "iso-8859-1") == 0)
  1100. return latin1toutf(out, in, e);
  1101. len = e-in+1;
  1102. sofar = 0;
  1103. *out = p = malloc(len+1);
  1104. if(p == nil)
  1105. return 0;
  1106. av[0] = charset;
  1107. av[1] = "-f";
  1108. av[2] = charset;
  1109. av[3] = 0;
  1110. if(pipe(totcs) < 0)
  1111. goto error;
  1112. if(pipe(fromtcs) < 0){
  1113. close(totcs[0]); close(totcs[1]);
  1114. goto error;
  1115. }
  1116. switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
  1117. case -1:
  1118. close(fromtcs[0]); close(fromtcs[1]);
  1119. close(totcs[0]); close(totcs[1]);
  1120. goto error;
  1121. case 0:
  1122. close(fromtcs[0]); close(totcs[1]);
  1123. dup(fromtcs[1], 1);
  1124. dup(totcs[0], 0);
  1125. close(fromtcs[1]); close(totcs[0]);
  1126. dup(open("/dev/null", OWRITE), 2);
  1127. exec("/bin/tcs", av);
  1128. _exits(0);
  1129. default:
  1130. close(fromtcs[1]); close(totcs[0]);
  1131. switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
  1132. case -1:
  1133. close(fromtcs[0]); close(totcs[1]);
  1134. goto error;
  1135. case 0:
  1136. close(fromtcs[0]);
  1137. while(in < e){
  1138. n = write(totcs[1], in, e-in);
  1139. if(n <= 0)
  1140. break;
  1141. in += n;
  1142. }
  1143. close(totcs[1]);
  1144. _exits(0);
  1145. default:
  1146. close(totcs[1]);
  1147. for(;;){
  1148. n = read(fromtcs[0], &p[sofar], len-sofar);
  1149. if(n <= 0)
  1150. break;
  1151. sofar += n;
  1152. p[sofar] = 0;
  1153. if(sofar == len){
  1154. len += 1024;
  1155. p = realloc(p, len+1);
  1156. if(p == nil)
  1157. goto error;
  1158. *out = p;
  1159. }
  1160. }
  1161. close(fromtcs[0]);
  1162. break;
  1163. }
  1164. break;
  1165. }
  1166. if(sofar == 0)
  1167. goto error;
  1168. return sofar;
  1169. error:
  1170. free(*out);
  1171. *out = nil;
  1172. return 0;
  1173. }
  1174. void *
  1175. emalloc(ulong n)
  1176. {
  1177. void *p;
  1178. p = mallocz(n, 1);
  1179. if(!p){
  1180. fprint(2, "%s: out of memory alloc %lud\n", argv0, n);
  1181. exits("out of memory");
  1182. }
  1183. setmalloctag(p, getcallerpc(&n));
  1184. return p;
  1185. }
  1186. void *
  1187. erealloc(void *p, ulong n)
  1188. {
  1189. if(n == 0)
  1190. n = 1;
  1191. p = realloc(p, n);
  1192. if(!p){
  1193. fprint(2, "%s: out of memory realloc %lud\n", argv0, n);
  1194. exits("out of memory");
  1195. }
  1196. setrealloctag(p, getcallerpc(&p));
  1197. return p;
  1198. }
  1199. void
  1200. mailplumb(Mailbox *mb, Message *m, int delete)
  1201. {
  1202. Plumbmsg p;
  1203. Plumbattr a[7];
  1204. char buf[256];
  1205. int ai;
  1206. char lenstr[10], *from, *subject, *date;
  1207. static int fd = -1;
  1208. if(m->subject822 == nil)
  1209. subject = "";
  1210. else
  1211. subject = s_to_c(m->subject822);
  1212. if(m->from822 != nil)
  1213. from = s_to_c(m->from822);
  1214. else if(m->unixfrom != nil)
  1215. from = s_to_c(m->unixfrom);
  1216. else
  1217. from = "";
  1218. if(m->unixdate != nil)
  1219. date = s_to_c(m->unixdate);
  1220. else
  1221. date = "";
  1222. sprint(lenstr, "%ld", m->end-m->start);
  1223. if(biffing && !delete)
  1224. print("[ %s / %s / %s ]\n", from, subject, lenstr);
  1225. if(!plumbing)
  1226. return;
  1227. if(fd < 0)
  1228. fd = plumbopen("send", OWRITE);
  1229. if(fd < 0)
  1230. return;
  1231. p.src = "mailfs";
  1232. p.dst = "seemail";
  1233. p.wdir = "/mail/fs";
  1234. p.type = "text";
  1235. ai = 0;
  1236. a[ai].name = "filetype";
  1237. a[ai].value = "mail";
  1238. a[++ai].name = "sender";
  1239. a[ai].value = from;
  1240. a[ai-1].next = &a[ai];
  1241. a[++ai].name = "length";
  1242. a[ai].value = lenstr;
  1243. a[ai-1].next = &a[ai];
  1244. a[++ai].name = "mailtype";
  1245. a[ai].value = delete?"delete":"new";
  1246. a[ai-1].next = &a[ai];
  1247. a[++ai].name = "date";
  1248. a[ai].value = date;
  1249. a[ai-1].next = &a[ai];
  1250. if(m->sdigest){
  1251. a[++ai].name = "digest";
  1252. a[ai].value = s_to_c(m->sdigest);
  1253. a[ai-1].next = &a[ai];
  1254. }
  1255. a[ai].next = nil;
  1256. p.attr = a;
  1257. snprint(buf, sizeof(buf), "%s/%s/%s",
  1258. mntpt, mb->name, m->name);
  1259. p.ndata = strlen(buf);
  1260. p.data = buf;
  1261. plumbsend(fd, &p);
  1262. }
  1263. //
  1264. // count the number of lines in the body (for imap4)
  1265. //
  1266. void
  1267. countlines(Message *m)
  1268. {
  1269. int i;
  1270. char *p;
  1271. i = 0;
  1272. for(p = strchr(m->rbody, '\n'); p != nil && p < m->rbend; p = strchr(p+1, '\n'))
  1273. i++;
  1274. sprint(m->lines, "%d", i);
  1275. }
  1276. char *LOG = "fs";
  1277. void
  1278. logmsg(char *s, Message *m)
  1279. {
  1280. int pid;
  1281. if(!logging)
  1282. return;
  1283. pid = getpid();
  1284. if(m == nil)
  1285. syslog(0, LOG, "%s.%d: %s", user, pid, s);
  1286. else
  1287. syslog(0, LOG, "%s.%d: %s msg from %s digest %s",
  1288. user, pid, s,
  1289. m->from822 ? s_to_c(m->from822) : "?",
  1290. s_to_c(m->sdigest));
  1291. }
  1292. /*
  1293. * squeeze nulls out of the body
  1294. */
  1295. static void
  1296. nullsqueeze(Message *m)
  1297. {
  1298. char *p, *q;
  1299. q = memchr(m->body, 0, m->end-m->body);
  1300. if(q == nil)
  1301. return;
  1302. for(p = m->body; q < m->end; q++){
  1303. if(*q == 0)
  1304. continue;
  1305. *p++ = *q;
  1306. }
  1307. m->bend = m->rbend = m->end = p;
  1308. }
  1309. //
  1310. // convert an RFC822 date into a Unix style date
  1311. // for when the Unix From line isn't there (e.g. POP3).
  1312. // enough client programs depend on having a Unix date
  1313. // that it's easiest to write this conversion code once, right here.
  1314. //
  1315. // people don't follow RFC822 particularly closely,
  1316. // so we use strtotm, which is a bunch of heuristics.
  1317. //
  1318. extern int strtotm(char*, Tm*);
  1319. String*
  1320. date822tounix(char *s)
  1321. {
  1322. char *p, *q;
  1323. Tm tm;
  1324. if(strtotm(s, &tm) < 0)
  1325. return nil;
  1326. p = asctime(&tm);
  1327. if(q = strchr(p, '\n'))
  1328. *q = '\0';
  1329. return s_copy(p);
  1330. }