Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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 *
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
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
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
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
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*
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*
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
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
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
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
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
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
}