fetch.c 11 KB

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