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