mbox.c 28 KB

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