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