mbox.c 26 KB

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