fetch.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <auth.h>
  5. #include "imap4d.h"
  6. char *fetchPartNames[FPMax] =
  7. {
  8. "",
  9. "HEADER",
  10. "HEADER.FIELDS",
  11. "HEADER.FIELDS.NOT",
  12. "MIME",
  13. "TEXT",
  14. };
  15. /*
  16. * implicitly set the \seen flag. done in a separate pass
  17. * so the .imp file doesn't need to be open while the
  18. * messages are sent to the client.
  19. */
  20. int
  21. fetchSeen(Box *box, Msg *m, int uids, void *vf)
  22. {
  23. Fetch *f;
  24. USED(uids);
  25. if(m->expunged)
  26. return uids;
  27. for(f = vf; f != nil; f = f->next){
  28. switch(f->op){
  29. case FRfc822:
  30. case FRfc822Text:
  31. case FBodySect:
  32. msgSeen(box, m);
  33. goto breakout;
  34. }
  35. }
  36. breakout:
  37. return 1;
  38. }
  39. /*
  40. * fetch messages
  41. *
  42. * imap4 body[] requestes get translated to upas/fs files as follows
  43. * body[id.header] == id/rawheader file + extra \r\n
  44. * body[id.text] == id/rawbody
  45. * body[id.mime] == id/mimeheader + extra \r\n
  46. * body[id] === body[id.header] + body[id.text]
  47. */
  48. int
  49. fetchMsg(Box *box, Msg *m, int uids, void *vf)
  50. {
  51. Tm tm;
  52. Fetch *f;
  53. char *sep;
  54. int todo;
  55. if(m->expunged)
  56. return uids;
  57. todo = 0;
  58. for(f = vf; f != nil; f = f->next){
  59. switch(f->op){
  60. case FFlags:
  61. /*
  62. * flags get sent with status update
  63. */
  64. box->sendFlags = 1;
  65. m->sendFlags = 1;
  66. break;
  67. case FUid:
  68. todo = 1;
  69. break;
  70. case FInternalDate:
  71. case FEnvelope:
  72. case FRfc822:
  73. case FRfc822Head:
  74. case FRfc822Size:
  75. case FRfc822Text:
  76. case FBodySect:
  77. case FBodyPeek:
  78. case FBody:
  79. case FBodyStruct:
  80. todo = 1;
  81. if(!msgStruct(m, 1)){
  82. msgDead(m);
  83. return uids;
  84. }
  85. break;
  86. default:
  87. bye("bad implementation of fetch");
  88. return 0;
  89. }
  90. }
  91. if(m->expunged)
  92. return uids;
  93. if(!todo)
  94. return 1;
  95. /*
  96. * note: it is allowed to send back the responses one at a time
  97. * rather than all together. this is exploited to send flags elsewhere.
  98. */
  99. Bprint(&bout, "* %lud FETCH (", m->seq);
  100. sep = "";
  101. if(uids){
  102. Bprint(&bout, "uid %lud", m->uid);
  103. sep = " ";
  104. }
  105. for(f = vf; f != nil; f = f->next){
  106. switch(f->op){
  107. default:
  108. bye("bad implementation of fetch");
  109. break;
  110. case FFlags:
  111. continue;
  112. case FUid:
  113. if(uids)
  114. continue;
  115. Bprint(&bout, "%suid %lud", sep, m->uid);
  116. break;
  117. case FEnvelope:
  118. Bprint(&bout, "%senvelope ", sep);
  119. fetchEnvelope(m);
  120. break;
  121. case FInternalDate:
  122. Bprint(&bout, "%sinternaldate ", sep);
  123. Bimapdate(&bout, date2tm(&tm, m->unixDate));
  124. break;
  125. case FBody:
  126. Bprint(&bout, "%sbody ", sep);
  127. fetchBodyStruct(m, &m->head, 0);
  128. break;
  129. case FBodyStruct:
  130. Bprint(&bout, "%sbodystructure ", sep);
  131. fetchBodyStruct(m, &m->head, 1);
  132. break;
  133. case FRfc822Size:
  134. Bprint(&bout, "%sRFC822.SIZE %lud", sep, msgSize(m));
  135. break;
  136. case FRfc822:
  137. f->part = FPAll;
  138. Bprint(&bout, "%sRFC822", sep);
  139. fetchBody(m, f);
  140. break;
  141. case FRfc822Head:
  142. f->part = FPHead;
  143. Bprint(&bout, "%srfc822.header", sep);
  144. fetchBody(m, f);
  145. break;
  146. case FRfc822Text:
  147. f->part = FPText;
  148. Bprint(&bout, "%srfc822.text", sep);
  149. fetchBody(m, f);
  150. break;
  151. case FBodySect:
  152. case FBodyPeek:
  153. Bprint(&bout, "%sbody", sep);
  154. fetchBody(fetchSect(m, f), f);
  155. break;
  156. }
  157. sep = " ";
  158. }
  159. Bprint(&bout, ")\r\n");
  160. return 1;
  161. }
  162. /*
  163. * print out section, part, headers;
  164. * find and return message section
  165. */
  166. Msg *
  167. fetchSect(Msg *m, Fetch *f)
  168. {
  169. Bputc(&bout, '[');
  170. BNList(&bout, f->sect, ".");
  171. if(f->part != FPAll){
  172. if(f->sect != nil)
  173. Bputc(&bout, '.');
  174. Bprint(&bout, "%s", fetchPartNames[f->part]);
  175. if(f->hdrs != nil){
  176. Bprint(&bout, " (");
  177. BSList(&bout, f->hdrs, " ");
  178. Bputc(&bout, ')');
  179. }
  180. }
  181. Bprint(&bout, "]");
  182. return findMsgSect(m, f->sect);
  183. }
  184. /*
  185. * actually return the body pieces
  186. */
  187. void
  188. fetchBody(Msg *m, Fetch *f)
  189. {
  190. Pair p;
  191. char *s, *t, *e, buf[BufSize + 2];
  192. ulong n, start, stop, pos;
  193. int fd, nn;
  194. if(m == nil){
  195. fetchBodyStr(f, "", 0);
  196. return;
  197. }
  198. switch(f->part){
  199. case FPHeadFields:
  200. case FPHeadFieldsNot:
  201. n = m->head.size + 3;
  202. s = emalloc(n);
  203. n = selectFields(s, n, m->head.buf, f->hdrs, f->part == FPHeadFields);
  204. fetchBodyStr(f, s, n);
  205. free(s);
  206. return;
  207. case FPHead:
  208. fetchBodyStr(f, m->head.buf, m->head.size);
  209. return;
  210. case FPMime:
  211. fetchBodyStr(f, m->mime.buf, m->mime.size);
  212. return;
  213. case FPAll:
  214. fd = msgFile(m, "rawbody");
  215. if(fd < 0){
  216. msgDead(m);
  217. fetchBodyStr(f, "", 0);
  218. return;
  219. }
  220. p = fetchBodyPart(f, msgSize(m));
  221. start = p.start;
  222. if(start < m->head.size){
  223. stop = p.stop;
  224. if(stop > m->head.size)
  225. stop = m->head.size;
  226. Bwrite(&bout, &m->head.buf[start], stop - start);
  227. start = 0;
  228. stop = p.stop;
  229. if(stop <= m->head.size){
  230. close(fd);
  231. return;
  232. }
  233. }else
  234. start -= m->head.size;
  235. stop = p.stop - m->head.size;
  236. break;
  237. case FPText:
  238. fd = msgFile(m, "rawbody");
  239. if(fd < 0){
  240. msgDead(m);
  241. fetchBodyStr(f, "", 0);
  242. return;
  243. }
  244. p = fetchBodyPart(f, m->size);
  245. start = p.start;
  246. stop = p.stop;
  247. break;
  248. default:
  249. fetchBodyStr(f, "", 0);
  250. return;
  251. }
  252. /*
  253. * read in each block, convert \n without \r to \r\n.
  254. * this means partial fetch requires fetching everything
  255. * through stop, since we don't know how many \r's will be added
  256. */
  257. buf[0] = ' ';
  258. for(pos = 0; pos < stop; ){
  259. n = BufSize;
  260. if(n > stop - pos)
  261. n = stop - pos;
  262. n = read(fd, &buf[1], n);
  263. if(n <= 0){
  264. fetchBodyFill(stop - pos);
  265. break;
  266. }
  267. e = &buf[n + 1];
  268. *e = '\0';
  269. for(s = &buf[1]; s < e && pos < stop; s = t + 1){
  270. t = memchr(s, '\n', e - s);
  271. if(t == nil)
  272. t = e;
  273. n = t - s;
  274. if(pos < start){
  275. if(pos + n <= start){
  276. s = t;
  277. pos += n;
  278. }else{
  279. s += start - pos;
  280. pos = start;
  281. }
  282. n = t - s;
  283. }
  284. nn = n;
  285. if(pos + nn > stop)
  286. nn = stop - pos;
  287. if(Bwrite(&bout, s, nn) != nn)
  288. writeErr();
  289. pos += n;
  290. if(*t == '\n'){
  291. if(t[-1] != '\r'){
  292. if(pos >= start && pos < stop)
  293. Bputc(&bout, '\r');
  294. pos++;
  295. }
  296. if(pos >= start && pos < stop)
  297. Bputc(&bout, '\n');
  298. pos++;
  299. }
  300. }
  301. buf[0] = e[-1];
  302. }
  303. close(fd);
  304. }
  305. /*
  306. * resolve the actual bounds of any partial fetch,
  307. * and print out the bounds & size of string returned
  308. */
  309. Pair
  310. fetchBodyPart(Fetch *f, ulong size)
  311. {
  312. Pair p;
  313. ulong start, stop;
  314. start = 0;
  315. stop = size;
  316. if(f->partial){
  317. start = f->start;
  318. if(start > size)
  319. start = size;
  320. stop = start + f->size;
  321. if(stop > size)
  322. stop = size;
  323. Bprint(&bout, "<%lud>", start);
  324. }
  325. Bprint(&bout, " {%lud}\r\n", stop - start);
  326. p.start = start;
  327. p.stop = stop;
  328. return p;
  329. }
  330. /*
  331. * something went wrong fetching data
  332. * produce fill bytes for what we've committed to produce
  333. */
  334. void
  335. fetchBodyFill(ulong n)
  336. {
  337. while(n-- > 0)
  338. if(Bputc(&bout, ' ') < 0)
  339. writeErr();
  340. }
  341. /*
  342. * return a simple string
  343. */
  344. void
  345. fetchBodyStr(Fetch *f, char *buf, ulong size)
  346. {
  347. Pair p;
  348. p = fetchBodyPart(f, size);
  349. Bwrite(&bout, &buf[p.start], p.stop-p.start);
  350. }
  351. char*
  352. printnlist(NList *sect)
  353. {
  354. static char buf[100];
  355. char *p;
  356. for(p= buf; sect; sect=sect->next){
  357. p += sprint(p, "%ld", sect->n);
  358. if(sect->next)
  359. *p++ = '.';
  360. }
  361. *p = '\0';
  362. return buf;
  363. }
  364. /*
  365. * find the numbered sub-part of the message
  366. */
  367. Msg*
  368. findMsgSect(Msg *m, NList *sect)
  369. {
  370. ulong id;
  371. for(; sect != nil; sect = sect->next){
  372. id = sect->n;
  373. #ifdef HACK
  374. /* HACK to solve extra level of structure not visible from upas/fs */
  375. if(m->kids == 0 && id == 1 && sect->next == nil){
  376. if(m->mime.type->s && strcmp(m->mime.type->s, "message")==0)
  377. if(m->mime.type->t && strcmp(m->mime.type->t, "rfc822")==0)
  378. if(m->head.type->s && strcmp(m->head.type->s, "text")==0)
  379. if(m->head.type->t && strcmp(m->head.type->t, "plain")==0)
  380. break;
  381. }
  382. /* end of HACK */
  383. #endif HACK
  384. for(m = m->kids; m != nil; m = m->next)
  385. if(m->id == id)
  386. break;
  387. if(m == nil)
  388. return nil;
  389. }
  390. return m;
  391. }
  392. void
  393. fetchEnvelope(Msg *m)
  394. {
  395. Tm tm;
  396. Bputc(&bout, '(');
  397. Brfc822date(&bout, date2tm(&tm, m->info[IDate]));
  398. Bputc(&bout, ' ');
  399. Bimapstr(&bout, m->info[ISubject]);
  400. Bputc(&bout, ' ');
  401. Bimapaddr(&bout, m->from);
  402. Bputc(&bout, ' ');
  403. Bimapaddr(&bout, m->sender);
  404. Bputc(&bout, ' ');
  405. Bimapaddr(&bout, m->replyTo);
  406. Bputc(&bout, ' ');
  407. Bimapaddr(&bout, m->to);
  408. Bputc(&bout, ' ');
  409. Bimapaddr(&bout, m->cc);
  410. Bputc(&bout, ' ');
  411. Bimapaddr(&bout, m->bcc);
  412. Bputc(&bout, ' ');
  413. Bimapstr(&bout, m->info[IInReplyTo]);
  414. Bputc(&bout, ' ');
  415. Bimapstr(&bout, m->info[IMessageId]);
  416. Bputc(&bout, ')');
  417. }
  418. void
  419. fetchBodyStruct(Msg *m, Header *h, int extensions)
  420. {
  421. Msg *k;
  422. ulong len;
  423. if(msgIsMulti(h)){
  424. Bputc(&bout, '(');
  425. for(k = m->kids; k != nil; k = k->next)
  426. fetchBodyStruct(k, &k->mime, extensions);
  427. Bputc(&bout, ' ');
  428. Bimapstr(&bout, h->type->t);
  429. if(extensions){
  430. Bputc(&bout, ' ');
  431. BimapMimeParams(&bout, h->type->next);
  432. fetchStructExt(h);
  433. }
  434. Bputc(&bout, ')');
  435. return;
  436. }
  437. Bputc(&bout, '(');
  438. if(h->type != nil){
  439. Bimapstr(&bout, h->type->s);
  440. Bputc(&bout, ' ');
  441. Bimapstr(&bout, h->type->t);
  442. Bputc(&bout, ' ');
  443. BimapMimeParams(&bout, h->type->next);
  444. }else
  445. Bprint(&bout, "\"text\" \"plain\" NIL");
  446. Bputc(&bout, ' ');
  447. if(h->id != nil)
  448. Bimapstr(&bout, h->id->s);
  449. else
  450. Bprint(&bout, "NIL");
  451. Bputc(&bout, ' ');
  452. if(h->description != nil)
  453. Bimapstr(&bout, h->description->s);
  454. else
  455. Bprint(&bout, "NIL");
  456. Bputc(&bout, ' ');
  457. if(h->encoding != nil)
  458. Bimapstr(&bout, h->encoding->s);
  459. else
  460. Bprint(&bout, "NIL");
  461. /*
  462. * this is so strange: return lengths for a body[text] response,
  463. * except in the case of a multipart message, when return lengths for a body[] response
  464. */
  465. len = m->size;
  466. if(h == &m->mime)
  467. len += m->head.size;
  468. Bprint(&bout, " %lud", len);
  469. len = m->lines;
  470. if(h == &m->mime)
  471. len += m->head.lines;
  472. if(h->type == nil || cistrcmp(h->type->s, "text") == 0){
  473. Bprint(&bout, " %lud", len);
  474. }else if(msgIsRfc822(h)){
  475. Bputc(&bout, ' ');
  476. k = m;
  477. if(h != &m->mime)
  478. k = m->kids;
  479. if(k == nil)
  480. Bprint(&bout, "(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (\"text\" \"plain\" NIL NIL NIL NIL 0 0) 0");
  481. else{
  482. fetchEnvelope(k);
  483. Bputc(&bout, ' ');
  484. fetchBodyStruct(k, &k->head, extensions);
  485. Bprint(&bout, " %lud", len);
  486. }
  487. }
  488. if(extensions){
  489. Bputc(&bout, ' ');
  490. /*
  491. * don't have the md5 laying around,
  492. * since the header & body have added newlines.
  493. */
  494. Bprint(&bout, "NIL");
  495. fetchStructExt(h);
  496. }
  497. Bputc(&bout, ')');
  498. }
  499. /*
  500. * common part of bodystructure extensions
  501. */
  502. void
  503. fetchStructExt(Header *h)
  504. {
  505. Bputc(&bout, ' ');
  506. if(h->disposition != nil){
  507. Bputc(&bout, '(');
  508. Bimapstr(&bout, h->disposition->s);
  509. Bputc(&bout, ' ');
  510. BimapMimeParams(&bout, h->disposition->next);
  511. Bputc(&bout, ')');
  512. }else
  513. Bprint(&bout, "NIL");
  514. Bputc(&bout, ' ');
  515. if(h->language != nil){
  516. if(h->language->next != nil)
  517. BimapMimeParams(&bout, h->language->next);
  518. else
  519. Bimapstr(&bout, h->language->s);
  520. }else
  521. Bprint(&bout, "NIL");
  522. }
  523. int
  524. BimapMimeParams(Biobuf *b, MimeHdr *mh)
  525. {
  526. char *sep;
  527. int n;
  528. if(mh == nil)
  529. return Bprint(b, "NIL");
  530. n = Bputc(b, '(');
  531. sep = "";
  532. for(; mh != nil; mh = mh->next){
  533. n += Bprint(b, sep);
  534. n += Bimapstr(b, mh->s);
  535. n += Bputc(b, ' ');
  536. n += Bimapstr(b, mh->t);
  537. sep = " ";
  538. }
  539. n += Bputc(b, ')');
  540. return n;
  541. }
  542. /*
  543. * print a list of addresses;
  544. * each address is printed as '(' personalName AtDomainList mboxName hostName ')'
  545. * the AtDomainList is always NIL
  546. */
  547. int
  548. Bimapaddr(Biobuf *b, MAddr *a)
  549. {
  550. char *host, *sep;
  551. int n;
  552. if(a == nil)
  553. return Bprint(b, "NIL");
  554. n = Bputc(b, '(');
  555. sep = "";
  556. for(; a != nil; a = a->next){
  557. n += Bprint(b, "%s(", sep);
  558. n += Bimapstr(b, a->personal);
  559. n += Bprint(b," NIL ");
  560. n += Bimapstr(b, a->box);
  561. n += Bputc(b, ' ');
  562. /*
  563. * can't send NIL as hostName, since that is code for a group
  564. */
  565. host = a->host;
  566. if(host == nil)
  567. host = "";
  568. n += Bimapstr(b, host);
  569. n += Bputc(b, ')');
  570. sep = " ";
  571. }
  572. n += Bputc(b, ')');
  573. return n;
  574. }