mbox.c 28 KB

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