xref: /plan9/sys/src/cmd/ip/imap4d/fetch.c (revision becaf2ab37b54217d3c714aa13bc7d74eeebce41)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <auth.h>
57dd7cddfSDavid du Colombier #include "imap4d.h"
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier char *fetchPartNames[FPMax] =
87dd7cddfSDavid du Colombier {
97dd7cddfSDavid du Colombier 	"",
107dd7cddfSDavid du Colombier 	"HEADER",
117dd7cddfSDavid du Colombier 	"HEADER.FIELDS",
127dd7cddfSDavid du Colombier 	"HEADER.FIELDS.NOT",
137dd7cddfSDavid du Colombier 	"MIME",
147dd7cddfSDavid du Colombier 	"TEXT",
157dd7cddfSDavid du Colombier };
167dd7cddfSDavid du Colombier 
177dd7cddfSDavid du Colombier /*
187dd7cddfSDavid du Colombier  * implicitly set the \seen flag.  done in a separate pass
197dd7cddfSDavid du Colombier  * so the .imp file doesn't need to be open while the
207dd7cddfSDavid du Colombier  * messages are sent to the client.
217dd7cddfSDavid du Colombier  */
227dd7cddfSDavid du Colombier int
237dd7cddfSDavid du Colombier fetchSeen(Box *box, Msg *m, int uids, void *vf)
247dd7cddfSDavid du Colombier {
257dd7cddfSDavid du Colombier 	Fetch *f;
267dd7cddfSDavid du Colombier 
277dd7cddfSDavid du Colombier 	USED(uids);
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier 	if(m->expunged)
307dd7cddfSDavid du Colombier 		return uids;
317dd7cddfSDavid du Colombier 	for(f = vf; f != nil; f = f->next){
327dd7cddfSDavid du Colombier 		switch(f->op){
337dd7cddfSDavid du Colombier 		case FRfc822:
347dd7cddfSDavid du Colombier 		case FRfc822Text:
357dd7cddfSDavid du Colombier 		case FBodySect:
367dd7cddfSDavid du Colombier 			msgSeen(box, m);
377dd7cddfSDavid du Colombier 			goto breakout;
387dd7cddfSDavid du Colombier 		}
397dd7cddfSDavid du Colombier 	}
407dd7cddfSDavid du Colombier breakout:
417dd7cddfSDavid du Colombier 
427dd7cddfSDavid du Colombier 	return 1;
437dd7cddfSDavid du Colombier }
447dd7cddfSDavid du Colombier 
457dd7cddfSDavid du Colombier /*
467dd7cddfSDavid du Colombier  * fetch messages
477dd7cddfSDavid du Colombier  *
487dd7cddfSDavid du Colombier  * imap4 body[] requestes get translated to upas/fs files as follows
497dd7cddfSDavid du Colombier  *	body[id.header] == id/rawheader file + extra \r\n
507dd7cddfSDavid du Colombier  *	body[id.text] == id/rawbody
517dd7cddfSDavid du Colombier  *	body[id.mime] == id/mimeheader + extra \r\n
527dd7cddfSDavid du Colombier  *	body[id] === body[id.header] + body[id.text]
537dd7cddfSDavid du Colombier */
547dd7cddfSDavid du Colombier int
55*becaf2abSDavid du Colombier fetchMsg(Box *, Msg *m, int uids, void *vf)
567dd7cddfSDavid du Colombier {
577dd7cddfSDavid du Colombier 	Tm tm;
587dd7cddfSDavid du Colombier 	Fetch *f;
597dd7cddfSDavid du Colombier 	char *sep;
607dd7cddfSDavid du Colombier 	int todo;
617dd7cddfSDavid du Colombier 
627dd7cddfSDavid du Colombier 	if(m->expunged)
637dd7cddfSDavid du Colombier 		return uids;
647dd7cddfSDavid du Colombier 
657dd7cddfSDavid du Colombier 	todo = 0;
667dd7cddfSDavid du Colombier 	for(f = vf; f != nil; f = f->next){
677dd7cddfSDavid du Colombier 		switch(f->op){
687dd7cddfSDavid du Colombier 		case FFlags:
697dd7cddfSDavid du Colombier 			break;
707dd7cddfSDavid du Colombier 		case FUid:
717dd7cddfSDavid du Colombier 			todo = 1;
727dd7cddfSDavid du Colombier 			break;
737dd7cddfSDavid du Colombier 		case FInternalDate:
747dd7cddfSDavid du Colombier 		case FEnvelope:
757dd7cddfSDavid du Colombier 		case FRfc822:
767dd7cddfSDavid du Colombier 		case FRfc822Head:
777dd7cddfSDavid du Colombier 		case FRfc822Size:
787dd7cddfSDavid du Colombier 		case FRfc822Text:
797dd7cddfSDavid du Colombier 		case FBodySect:
807dd7cddfSDavid du Colombier 		case FBodyPeek:
817dd7cddfSDavid du Colombier 		case FBody:
827dd7cddfSDavid du Colombier 		case FBodyStruct:
837dd7cddfSDavid du Colombier 			todo = 1;
847dd7cddfSDavid du Colombier 			if(!msgStruct(m, 1)){
857dd7cddfSDavid du Colombier 				msgDead(m);
867dd7cddfSDavid du Colombier 				return uids;
877dd7cddfSDavid du Colombier 			}
887dd7cddfSDavid du Colombier 			break;
897dd7cddfSDavid du Colombier 		default:
907dd7cddfSDavid du Colombier 			bye("bad implementation of fetch");
917dd7cddfSDavid du Colombier 			return 0;
927dd7cddfSDavid du Colombier 		}
937dd7cddfSDavid du Colombier 	}
947dd7cddfSDavid du Colombier 
957dd7cddfSDavid du Colombier 	if(m->expunged)
967dd7cddfSDavid du Colombier 		return uids;
977dd7cddfSDavid du Colombier 	if(!todo)
987dd7cddfSDavid du Colombier 		return 1;
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier 	/*
1017dd7cddfSDavid du Colombier 	 * note: it is allowed to send back the responses one at a time
1027dd7cddfSDavid du Colombier 	 * rather than all together.  this is exploited to send flags elsewhere.
1037dd7cddfSDavid du Colombier 	 */
104f121929fSDavid du Colombier 	Bprint(&bout, "* %lud FETCH (", m->seq);
1057dd7cddfSDavid du Colombier 	sep = "";
1067dd7cddfSDavid du Colombier 	if(uids){
1077dd7cddfSDavid du Colombier 		Bprint(&bout, "uid %lud", m->uid);
1087dd7cddfSDavid du Colombier 		sep = " ";
1097dd7cddfSDavid du Colombier 	}
1107dd7cddfSDavid du Colombier 	for(f = vf; f != nil; f = f->next){
1117dd7cddfSDavid du Colombier 		switch(f->op){
1127dd7cddfSDavid du Colombier 		default:
1137dd7cddfSDavid du Colombier 			bye("bad implementation of fetch");
1147dd7cddfSDavid du Colombier 			break;
1157dd7cddfSDavid du Colombier 		case FFlags:
116*becaf2abSDavid du Colombier 			Bprint(&bout, "%sflags (", sep);
117*becaf2abSDavid du Colombier 			writeFlags(&bout, m, 1);
118*becaf2abSDavid du Colombier 			Bprint(&bout, ")");
119*becaf2abSDavid du Colombier 			break;
1207dd7cddfSDavid du Colombier 		case FUid:
1217dd7cddfSDavid du Colombier 			if(uids)
1227dd7cddfSDavid du Colombier 				continue;
1237dd7cddfSDavid du Colombier 			Bprint(&bout, "%suid %lud", sep, m->uid);
1247dd7cddfSDavid du Colombier 			break;
1257dd7cddfSDavid du Colombier 		case FEnvelope:
1267dd7cddfSDavid du Colombier 			Bprint(&bout, "%senvelope ", sep);
1277dd7cddfSDavid du Colombier 			fetchEnvelope(m);
1287dd7cddfSDavid du Colombier 			break;
1297dd7cddfSDavid du Colombier 		case FInternalDate:
1307dd7cddfSDavid du Colombier 			Bprint(&bout, "%sinternaldate ", sep);
1317dd7cddfSDavid du Colombier 			Bimapdate(&bout, date2tm(&tm, m->unixDate));
1327dd7cddfSDavid du Colombier 			break;
1337dd7cddfSDavid du Colombier 		case FBody:
1347dd7cddfSDavid du Colombier 			Bprint(&bout, "%sbody ", sep);
1357dd7cddfSDavid du Colombier 			fetchBodyStruct(m, &m->head, 0);
1367dd7cddfSDavid du Colombier 			break;
1377dd7cddfSDavid du Colombier 		case FBodyStruct:
1387dd7cddfSDavid du Colombier 			Bprint(&bout, "%sbodystructure ", sep);
1397dd7cddfSDavid du Colombier 			fetchBodyStruct(m, &m->head, 1);
1407dd7cddfSDavid du Colombier 			break;
1417dd7cddfSDavid du Colombier 		case FRfc822Size:
142f121929fSDavid du Colombier 			Bprint(&bout, "%sRFC822.SIZE %lud", sep, msgSize(m));
1437dd7cddfSDavid du Colombier 			break;
1447dd7cddfSDavid du Colombier 		case FRfc822:
1457dd7cddfSDavid du Colombier 			f->part = FPAll;
146f121929fSDavid du Colombier 			Bprint(&bout, "%sRFC822", sep);
1477dd7cddfSDavid du Colombier 			fetchBody(m, f);
1487dd7cddfSDavid du Colombier 			break;
1497dd7cddfSDavid du Colombier 		case FRfc822Head:
1507dd7cddfSDavid du Colombier 			f->part = FPHead;
1517dd7cddfSDavid du Colombier 			Bprint(&bout, "%srfc822.header", sep);
1527dd7cddfSDavid du Colombier 			fetchBody(m, f);
1537dd7cddfSDavid du Colombier 			break;
1547dd7cddfSDavid du Colombier 		case FRfc822Text:
1557dd7cddfSDavid du Colombier 			f->part = FPText;
1567dd7cddfSDavid du Colombier 			Bprint(&bout, "%srfc822.text", sep);
1577dd7cddfSDavid du Colombier 			fetchBody(m, f);
1587dd7cddfSDavid du Colombier 			break;
1597dd7cddfSDavid du Colombier 		case FBodySect:
1607dd7cddfSDavid du Colombier 		case FBodyPeek:
1617dd7cddfSDavid du Colombier 			Bprint(&bout, "%sbody", sep);
1627dd7cddfSDavid du Colombier 			fetchBody(fetchSect(m, f), f);
1637dd7cddfSDavid du Colombier 			break;
1647dd7cddfSDavid du Colombier 		}
1657dd7cddfSDavid du Colombier 		sep = " ";
1667dd7cddfSDavid du Colombier 	}
1677dd7cddfSDavid du Colombier 	Bprint(&bout, ")\r\n");
1687dd7cddfSDavid du Colombier 
1697dd7cddfSDavid du Colombier 	return 1;
1707dd7cddfSDavid du Colombier }
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier /*
1737dd7cddfSDavid du Colombier  * print out section, part, headers;
1747dd7cddfSDavid du Colombier  * find and return message section
1757dd7cddfSDavid du Colombier  */
1767dd7cddfSDavid du Colombier Msg *
1777dd7cddfSDavid du Colombier fetchSect(Msg *m, Fetch *f)
1787dd7cddfSDavid du Colombier {
1797dd7cddfSDavid du Colombier 	Bputc(&bout, '[');
1807dd7cddfSDavid du Colombier 	BNList(&bout, f->sect, ".");
1817dd7cddfSDavid du Colombier 	if(f->part != FPAll){
1827dd7cddfSDavid du Colombier 		if(f->sect != nil)
1837dd7cddfSDavid du Colombier 			Bputc(&bout, '.');
1847dd7cddfSDavid du Colombier 		Bprint(&bout, "%s", fetchPartNames[f->part]);
1857dd7cddfSDavid du Colombier 		if(f->hdrs != nil){
1867dd7cddfSDavid du Colombier 			Bprint(&bout, " (");
1877dd7cddfSDavid du Colombier 			BSList(&bout, f->hdrs, " ");
1887dd7cddfSDavid du Colombier 			Bputc(&bout, ')');
1897dd7cddfSDavid du Colombier 		}
1907dd7cddfSDavid du Colombier 	}
1917dd7cddfSDavid du Colombier 	Bprint(&bout, "]");
1927dd7cddfSDavid du Colombier 	return findMsgSect(m, f->sect);
1937dd7cddfSDavid du Colombier }
1947dd7cddfSDavid du Colombier 
1957dd7cddfSDavid du Colombier /*
1967dd7cddfSDavid du Colombier  * actually return the body pieces
1977dd7cddfSDavid du Colombier  */
1987dd7cddfSDavid du Colombier void
1997dd7cddfSDavid du Colombier fetchBody(Msg *m, Fetch *f)
2007dd7cddfSDavid du Colombier {
2017dd7cddfSDavid du Colombier 	Pair p;
2027dd7cddfSDavid du Colombier 	char *s, *t, *e, buf[BufSize + 2];
2037dd7cddfSDavid du Colombier 	ulong n, start, stop, pos;
2047dd7cddfSDavid du Colombier 	int fd, nn;
2057dd7cddfSDavid du Colombier 
2067dd7cddfSDavid du Colombier 	if(m == nil){
2077dd7cddfSDavid du Colombier 		fetchBodyStr(f, "", 0);
2087dd7cddfSDavid du Colombier 		return;
2097dd7cddfSDavid du Colombier 	}
2107dd7cddfSDavid du Colombier 	switch(f->part){
2117dd7cddfSDavid du Colombier 	case FPHeadFields:
2127dd7cddfSDavid du Colombier 	case FPHeadFieldsNot:
2137dd7cddfSDavid du Colombier 		n = m->head.size + 3;
2147dd7cddfSDavid du Colombier 		s = emalloc(n);
2157dd7cddfSDavid du Colombier 		n = selectFields(s, n, m->head.buf, f->hdrs, f->part == FPHeadFields);
2167dd7cddfSDavid du Colombier 		fetchBodyStr(f, s, n);
2177dd7cddfSDavid du Colombier 		free(s);
2187dd7cddfSDavid du Colombier 		return;
2197dd7cddfSDavid du Colombier 	case FPHead:
2207dd7cddfSDavid du Colombier 		fetchBodyStr(f, m->head.buf, m->head.size);
2217dd7cddfSDavid du Colombier 		return;
2227dd7cddfSDavid du Colombier 	case FPMime:
2237dd7cddfSDavid du Colombier 		fetchBodyStr(f, m->mime.buf, m->mime.size);
2247dd7cddfSDavid du Colombier 		return;
2257dd7cddfSDavid du Colombier 	case FPAll:
2267dd7cddfSDavid du Colombier 		fd = msgFile(m, "rawbody");
2277dd7cddfSDavid du Colombier 		if(fd < 0){
2287dd7cddfSDavid du Colombier 			msgDead(m);
2297dd7cddfSDavid du Colombier 			fetchBodyStr(f, "", 0);
2307dd7cddfSDavid du Colombier 			return;
2317dd7cddfSDavid du Colombier 		}
2327dd7cddfSDavid du Colombier 		p = fetchBodyPart(f, msgSize(m));
2337dd7cddfSDavid du Colombier 		start = p.start;
2347dd7cddfSDavid du Colombier 		if(start < m->head.size){
2357dd7cddfSDavid du Colombier 			stop = p.stop;
2367dd7cddfSDavid du Colombier 			if(stop > m->head.size)
2377dd7cddfSDavid du Colombier 				stop = m->head.size;
2387dd7cddfSDavid du Colombier 			Bwrite(&bout, &m->head.buf[start], stop - start);
2397dd7cddfSDavid du Colombier 			start = 0;
2407dd7cddfSDavid du Colombier 			stop = p.stop;
2417dd7cddfSDavid du Colombier 			if(stop <= m->head.size){
2427dd7cddfSDavid du Colombier 				close(fd);
2437dd7cddfSDavid du Colombier 				return;
2447dd7cddfSDavid du Colombier 			}
2457dd7cddfSDavid du Colombier 		}else
2467dd7cddfSDavid du Colombier 			start -= m->head.size;
2477dd7cddfSDavid du Colombier 		stop = p.stop - m->head.size;
2487dd7cddfSDavid du Colombier 		break;
2497dd7cddfSDavid du Colombier 	case FPText:
2507dd7cddfSDavid du Colombier 		fd = msgFile(m, "rawbody");
2517dd7cddfSDavid du Colombier 		if(fd < 0){
2527dd7cddfSDavid du Colombier 			msgDead(m);
2537dd7cddfSDavid du Colombier 			fetchBodyStr(f, "", 0);
2547dd7cddfSDavid du Colombier 			return;
2557dd7cddfSDavid du Colombier 		}
2567dd7cddfSDavid du Colombier 		p = fetchBodyPart(f, m->size);
2577dd7cddfSDavid du Colombier 		start = p.start;
2587dd7cddfSDavid du Colombier 		stop = p.stop;
2597dd7cddfSDavid du Colombier 		break;
2607dd7cddfSDavid du Colombier 	default:
2617dd7cddfSDavid du Colombier 		fetchBodyStr(f, "", 0);
2627dd7cddfSDavid du Colombier 		return;
2637dd7cddfSDavid du Colombier 	}
2647dd7cddfSDavid du Colombier 
2657dd7cddfSDavid du Colombier 	/*
2667dd7cddfSDavid du Colombier 	 * read in each block, convert \n without \r to \r\n.
2677dd7cddfSDavid du Colombier 	 * this means partial fetch requires fetching everything
2687dd7cddfSDavid du Colombier 	 * through stop, since we don't know how many \r's will be added
2697dd7cddfSDavid du Colombier 	 */
2707dd7cddfSDavid du Colombier 	buf[0] = ' ';
2717dd7cddfSDavid du Colombier 	for(pos = 0; pos < stop; ){
2727dd7cddfSDavid du Colombier 		n = BufSize;
2737dd7cddfSDavid du Colombier 		if(n > stop - pos)
2747dd7cddfSDavid du Colombier 			n = stop - pos;
2757dd7cddfSDavid du Colombier 		n = read(fd, &buf[1], n);
2767dd7cddfSDavid du Colombier 		if(n <= 0){
2777dd7cddfSDavid du Colombier 			fetchBodyFill(stop - pos);
2787dd7cddfSDavid du Colombier 			break;
2797dd7cddfSDavid du Colombier 		}
2807dd7cddfSDavid du Colombier 		e = &buf[n + 1];
2817dd7cddfSDavid du Colombier 		*e = '\0';
2827dd7cddfSDavid du Colombier 		for(s = &buf[1]; s < e && pos < stop; s = t + 1){
2837dd7cddfSDavid du Colombier 			t = memchr(s, '\n', e - s);
2847dd7cddfSDavid du Colombier 			if(t == nil)
2857dd7cddfSDavid du Colombier 				t = e;
2867dd7cddfSDavid du Colombier 			n = t - s;
2877dd7cddfSDavid du Colombier 			if(pos < start){
2887dd7cddfSDavid du Colombier 				if(pos + n <= start){
2897dd7cddfSDavid du Colombier 					s = t;
2907dd7cddfSDavid du Colombier 					pos += n;
2917dd7cddfSDavid du Colombier 				}else{
2927dd7cddfSDavid du Colombier 					s += start - pos;
2937dd7cddfSDavid du Colombier 					pos = start;
2947dd7cddfSDavid du Colombier 				}
2957dd7cddfSDavid du Colombier 				n = t - s;
2967dd7cddfSDavid du Colombier 			}
2977dd7cddfSDavid du Colombier 			nn = n;
2987dd7cddfSDavid du Colombier 			if(pos + nn > stop)
2997dd7cddfSDavid du Colombier 				nn = stop - pos;
3007dd7cddfSDavid du Colombier 			if(Bwrite(&bout, s, nn) != nn)
3017dd7cddfSDavid du Colombier 				writeErr();
3027dd7cddfSDavid du Colombier 			pos += n;
3037dd7cddfSDavid du Colombier 			if(*t == '\n'){
3047dd7cddfSDavid du Colombier 				if(t[-1] != '\r'){
3057dd7cddfSDavid du Colombier 					if(pos >= start && pos < stop)
3067dd7cddfSDavid du Colombier 						Bputc(&bout, '\r');
3077dd7cddfSDavid du Colombier 					pos++;
3087dd7cddfSDavid du Colombier 				}
3097dd7cddfSDavid du Colombier 				if(pos >= start && pos < stop)
3107dd7cddfSDavid du Colombier 					Bputc(&bout, '\n');
3117dd7cddfSDavid du Colombier 				pos++;
3127dd7cddfSDavid du Colombier 			}
3137dd7cddfSDavid du Colombier 		}
3147dd7cddfSDavid du Colombier 		buf[0] = e[-1];
3157dd7cddfSDavid du Colombier 	}
3167dd7cddfSDavid du Colombier 	close(fd);
3177dd7cddfSDavid du Colombier }
3187dd7cddfSDavid du Colombier 
3197dd7cddfSDavid du Colombier /*
3207dd7cddfSDavid du Colombier  * resolve the actual bounds of any partial fetch,
3217dd7cddfSDavid du Colombier  * and print out the bounds & size of string returned
3227dd7cddfSDavid du Colombier  */
3237dd7cddfSDavid du Colombier Pair
3247dd7cddfSDavid du Colombier fetchBodyPart(Fetch *f, ulong size)
3257dd7cddfSDavid du Colombier {
3267dd7cddfSDavid du Colombier 	Pair p;
3277dd7cddfSDavid du Colombier 	ulong start, stop;
3287dd7cddfSDavid du Colombier 
3297dd7cddfSDavid du Colombier 	start = 0;
3307dd7cddfSDavid du Colombier 	stop = size;
3317dd7cddfSDavid du Colombier 	if(f->partial){
3327dd7cddfSDavid du Colombier 		start = f->start;
3337dd7cddfSDavid du Colombier 		if(start > size)
3347dd7cddfSDavid du Colombier 			start = size;
3357dd7cddfSDavid du Colombier 		stop = start + f->size;
3367dd7cddfSDavid du Colombier 		if(stop > size)
3377dd7cddfSDavid du Colombier 			stop = size;
3387dd7cddfSDavid du Colombier 		Bprint(&bout, "<%lud>", start);
3397dd7cddfSDavid du Colombier 	}
3407dd7cddfSDavid du Colombier 	Bprint(&bout, " {%lud}\r\n", stop - start);
3417dd7cddfSDavid du Colombier 	p.start = start;
3427dd7cddfSDavid du Colombier 	p.stop = stop;
3437dd7cddfSDavid du Colombier 	return p;
3447dd7cddfSDavid du Colombier }
3457dd7cddfSDavid du Colombier 
3467dd7cddfSDavid du Colombier /*
3477dd7cddfSDavid du Colombier  * something went wrong fetching data
3487dd7cddfSDavid du Colombier  * produce fill bytes for what we've committed to produce
3497dd7cddfSDavid du Colombier  */
3507dd7cddfSDavid du Colombier void
3517dd7cddfSDavid du Colombier fetchBodyFill(ulong n)
3527dd7cddfSDavid du Colombier {
3537dd7cddfSDavid du Colombier 	while(n-- > 0)
3547dd7cddfSDavid du Colombier 		if(Bputc(&bout, ' ') < 0)
3557dd7cddfSDavid du Colombier 			writeErr();
3567dd7cddfSDavid du Colombier }
3577dd7cddfSDavid du Colombier 
3587dd7cddfSDavid du Colombier /*
3597dd7cddfSDavid du Colombier  * return a simple string
3607dd7cddfSDavid du Colombier  */
3617dd7cddfSDavid du Colombier void
3627dd7cddfSDavid du Colombier fetchBodyStr(Fetch *f, char *buf, ulong size)
3637dd7cddfSDavid du Colombier {
3647dd7cddfSDavid du Colombier 	Pair p;
3657dd7cddfSDavid du Colombier 
3667dd7cddfSDavid du Colombier 	p = fetchBodyPart(f, size);
3677dd7cddfSDavid du Colombier 	Bwrite(&bout, &buf[p.start], p.stop-p.start);
3687dd7cddfSDavid du Colombier }
3697dd7cddfSDavid du Colombier 
3709a747e4fSDavid du Colombier char*
3719a747e4fSDavid du Colombier printnlist(NList *sect)
3729a747e4fSDavid du Colombier {
3739a747e4fSDavid du Colombier 	static char buf[100];
3749a747e4fSDavid du Colombier 	char *p;
3759a747e4fSDavid du Colombier 
3769a747e4fSDavid du Colombier 	for(p= buf; sect; sect=sect->next){
3779a747e4fSDavid du Colombier 		p += sprint(p, "%ld", sect->n);
3789a747e4fSDavid du Colombier 		if(sect->next)
3799a747e4fSDavid du Colombier 			*p++ = '.';
3809a747e4fSDavid du Colombier 	}
3819a747e4fSDavid du Colombier 	*p = '\0';
3829a747e4fSDavid du Colombier 	return buf;
3839a747e4fSDavid du Colombier }
3849a747e4fSDavid du Colombier 
3857dd7cddfSDavid du Colombier /*
3867dd7cddfSDavid du Colombier  * find the numbered sub-part of the message
3877dd7cddfSDavid du Colombier  */
3887dd7cddfSDavid du Colombier Msg*
3897dd7cddfSDavid du Colombier findMsgSect(Msg *m, NList *sect)
3907dd7cddfSDavid du Colombier {
3917dd7cddfSDavid du Colombier 	ulong id;
3927dd7cddfSDavid du Colombier 
3937dd7cddfSDavid du Colombier 	for(; sect != nil; sect = sect->next){
3947dd7cddfSDavid du Colombier 		id = sect->n;
3959a747e4fSDavid du Colombier #ifdef HACK
3969a747e4fSDavid du Colombier 		/* HACK to solve extra level of structure not visible from upas/fs  */
3979a747e4fSDavid du Colombier 		if(m->kids == 0 && id == 1 && sect->next == nil){
3989a747e4fSDavid du Colombier 			if(m->mime.type->s && strcmp(m->mime.type->s, "message")==0)
3999a747e4fSDavid du Colombier 			if(m->mime.type->t && strcmp(m->mime.type->t, "rfc822")==0)
4009a747e4fSDavid du Colombier 			if(m->head.type->s && strcmp(m->head.type->s, "text")==0)
4019a747e4fSDavid du Colombier 			if(m->head.type->t && strcmp(m->head.type->t, "plain")==0)
4029a747e4fSDavid du Colombier 				break;
4039a747e4fSDavid du Colombier 		}
4049a747e4fSDavid du Colombier 		/* end of HACK */
4059a747e4fSDavid du Colombier #endif HACK
4067dd7cddfSDavid du Colombier 		for(m = m->kids; m != nil; m = m->next)
4077dd7cddfSDavid du Colombier 			if(m->id == id)
4087dd7cddfSDavid du Colombier 				break;
4097dd7cddfSDavid du Colombier 		if(m == nil)
4107dd7cddfSDavid du Colombier 			return nil;
4117dd7cddfSDavid du Colombier 	}
4127dd7cddfSDavid du Colombier 	return m;
4137dd7cddfSDavid du Colombier }
4147dd7cddfSDavid du Colombier 
4157dd7cddfSDavid du Colombier void
4167dd7cddfSDavid du Colombier fetchEnvelope(Msg *m)
4177dd7cddfSDavid du Colombier {
4187dd7cddfSDavid du Colombier 	Tm tm;
4197dd7cddfSDavid du Colombier 
4207dd7cddfSDavid du Colombier 	Bputc(&bout, '(');
4217dd7cddfSDavid du Colombier 	Brfc822date(&bout, date2tm(&tm, m->info[IDate]));
4227dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4237dd7cddfSDavid du Colombier 	Bimapstr(&bout, m->info[ISubject]);
4247dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4257dd7cddfSDavid du Colombier 	Bimapaddr(&bout, m->from);
4267dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4277dd7cddfSDavid du Colombier 	Bimapaddr(&bout, m->sender);
4287dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4297dd7cddfSDavid du Colombier 	Bimapaddr(&bout, m->replyTo);
4307dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4317dd7cddfSDavid du Colombier 	Bimapaddr(&bout, m->to);
4327dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4337dd7cddfSDavid du Colombier 	Bimapaddr(&bout, m->cc);
4347dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4357dd7cddfSDavid du Colombier 	Bimapaddr(&bout, m->bcc);
4367dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4377dd7cddfSDavid du Colombier 	Bimapstr(&bout, m->info[IInReplyTo]);
4387dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4397dd7cddfSDavid du Colombier 	Bimapstr(&bout, m->info[IMessageId]);
4407dd7cddfSDavid du Colombier 	Bputc(&bout, ')');
4417dd7cddfSDavid du Colombier }
4427dd7cddfSDavid du Colombier 
4437dd7cddfSDavid du Colombier void
4447dd7cddfSDavid du Colombier fetchBodyStruct(Msg *m, Header *h, int extensions)
4457dd7cddfSDavid du Colombier {
4467dd7cddfSDavid du Colombier 	Msg *k;
4477dd7cddfSDavid du Colombier 	ulong len;
4487dd7cddfSDavid du Colombier 
4497dd7cddfSDavid du Colombier 	if(msgIsMulti(h)){
4507dd7cddfSDavid du Colombier 		Bputc(&bout, '(');
4517dd7cddfSDavid du Colombier 		for(k = m->kids; k != nil; k = k->next)
4527dd7cddfSDavid du Colombier 			fetchBodyStruct(k, &k->mime, extensions);
4537dd7cddfSDavid du Colombier 
4547dd7cddfSDavid du Colombier 		Bputc(&bout, ' ');
4557dd7cddfSDavid du Colombier 		Bimapstr(&bout, h->type->t);
4567dd7cddfSDavid du Colombier 
4577dd7cddfSDavid du Colombier 		if(extensions){
4587dd7cddfSDavid du Colombier 			Bputc(&bout, ' ');
4597dd7cddfSDavid du Colombier 			BimapMimeParams(&bout, h->type->next);
4607dd7cddfSDavid du Colombier 			fetchStructExt(h);
4617dd7cddfSDavid du Colombier 		}
4627dd7cddfSDavid du Colombier 
4637dd7cddfSDavid du Colombier 		Bputc(&bout, ')');
4647dd7cddfSDavid du Colombier 		return;
4657dd7cddfSDavid du Colombier 	}
4667dd7cddfSDavid du Colombier 
4677dd7cddfSDavid du Colombier 	Bputc(&bout, '(');
4687dd7cddfSDavid du Colombier 	if(h->type != nil){
4697dd7cddfSDavid du Colombier 		Bimapstr(&bout, h->type->s);
4707dd7cddfSDavid du Colombier 		Bputc(&bout, ' ');
4717dd7cddfSDavid du Colombier 		Bimapstr(&bout, h->type->t);
4727dd7cddfSDavid du Colombier 		Bputc(&bout, ' ');
4737dd7cddfSDavid du Colombier 		BimapMimeParams(&bout, h->type->next);
4747dd7cddfSDavid du Colombier 	}else
4757dd7cddfSDavid du Colombier 		Bprint(&bout, "\"text\" \"plain\" NIL");
4767dd7cddfSDavid du Colombier 
4777dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4787dd7cddfSDavid du Colombier 	if(h->id != nil)
4797dd7cddfSDavid du Colombier 		Bimapstr(&bout, h->id->s);
4807dd7cddfSDavid du Colombier 	else
4817dd7cddfSDavid du Colombier 		Bprint(&bout, "NIL");
4827dd7cddfSDavid du Colombier 
4837dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4847dd7cddfSDavid du Colombier 	if(h->description != nil)
4857dd7cddfSDavid du Colombier 		Bimapstr(&bout, h->description->s);
4867dd7cddfSDavid du Colombier 	else
4877dd7cddfSDavid du Colombier 		Bprint(&bout, "NIL");
4887dd7cddfSDavid du Colombier 
4897dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
4907dd7cddfSDavid du Colombier 	if(h->encoding != nil)
4917dd7cddfSDavid du Colombier 		Bimapstr(&bout, h->encoding->s);
4927dd7cddfSDavid du Colombier 	else
4937dd7cddfSDavid du Colombier 		Bprint(&bout, "NIL");
4947dd7cddfSDavid du Colombier 
4957dd7cddfSDavid du Colombier 	/*
4967dd7cddfSDavid du Colombier 	 * this is so strange: return lengths for a body[text] response,
4979a747e4fSDavid du Colombier 	 * except in the case of a multipart message, when return lengths for a body[] response
4987dd7cddfSDavid du Colombier 	 */
4997dd7cddfSDavid du Colombier 	len = m->size;
5007dd7cddfSDavid du Colombier 	if(h == &m->mime)
5017dd7cddfSDavid du Colombier 		len += m->head.size;
5027dd7cddfSDavid du Colombier 	Bprint(&bout, " %lud", len);
5037dd7cddfSDavid du Colombier 
5047dd7cddfSDavid du Colombier 	len = m->lines;
5057dd7cddfSDavid du Colombier 	if(h == &m->mime)
5067dd7cddfSDavid du Colombier 		len += m->head.lines;
5077dd7cddfSDavid du Colombier 
5087dd7cddfSDavid du Colombier 	if(h->type == nil || cistrcmp(h->type->s, "text") == 0){
5097dd7cddfSDavid du Colombier 		Bprint(&bout, " %lud", len);
5107dd7cddfSDavid du Colombier 	}else if(msgIsRfc822(h)){
5117dd7cddfSDavid du Colombier 		Bputc(&bout, ' ');
5127dd7cddfSDavid du Colombier 		k = m;
5137dd7cddfSDavid du Colombier 		if(h != &m->mime)
5147dd7cddfSDavid du Colombier 			k = m->kids;
5157dd7cddfSDavid du Colombier 		if(k == nil)
5167dd7cddfSDavid du Colombier 			Bprint(&bout, "(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (\"text\" \"plain\" NIL NIL NIL NIL 0 0) 0");
5177dd7cddfSDavid du Colombier 		else{
5187dd7cddfSDavid du Colombier 			fetchEnvelope(k);
5197dd7cddfSDavid du Colombier 			Bputc(&bout, ' ');
5207dd7cddfSDavid du Colombier 			fetchBodyStruct(k, &k->head, extensions);
5217dd7cddfSDavid du Colombier 			Bprint(&bout, " %lud", len);
5227dd7cddfSDavid du Colombier 		}
5237dd7cddfSDavid du Colombier 	}
5247dd7cddfSDavid du Colombier 
5257dd7cddfSDavid du Colombier 	if(extensions){
5267dd7cddfSDavid du Colombier 		Bputc(&bout, ' ');
5277dd7cddfSDavid du Colombier 
5287dd7cddfSDavid du Colombier 		/*
5297dd7cddfSDavid du Colombier 		 * don't have the md5 laying around,
5307dd7cddfSDavid du Colombier 		 * since the header & body have added newlines.
5317dd7cddfSDavid du Colombier 		 */
5327dd7cddfSDavid du Colombier 		Bprint(&bout, "NIL");
5337dd7cddfSDavid du Colombier 
5347dd7cddfSDavid du Colombier 		fetchStructExt(h);
5357dd7cddfSDavid du Colombier 	}
5367dd7cddfSDavid du Colombier 	Bputc(&bout, ')');
5377dd7cddfSDavid du Colombier }
5387dd7cddfSDavid du Colombier 
5397dd7cddfSDavid du Colombier /*
5407dd7cddfSDavid du Colombier  * common part of bodystructure extensions
5417dd7cddfSDavid du Colombier  */
5427dd7cddfSDavid du Colombier void
5437dd7cddfSDavid du Colombier fetchStructExt(Header *h)
5447dd7cddfSDavid du Colombier {
5457dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
5467dd7cddfSDavid du Colombier 	if(h->disposition != nil){
5477dd7cddfSDavid du Colombier 		Bputc(&bout, '(');
5487dd7cddfSDavid du Colombier 		Bimapstr(&bout, h->disposition->s);
5497dd7cddfSDavid du Colombier 		Bputc(&bout, ' ');
5507dd7cddfSDavid du Colombier 		BimapMimeParams(&bout, h->disposition->next);
5517dd7cddfSDavid du Colombier 		Bputc(&bout, ')');
5527dd7cddfSDavid du Colombier 	}else
5537dd7cddfSDavid du Colombier 		Bprint(&bout, "NIL");
5547dd7cddfSDavid du Colombier 	Bputc(&bout, ' ');
5557dd7cddfSDavid du Colombier 	if(h->language != nil){
5567dd7cddfSDavid du Colombier 		if(h->language->next != nil)
5577dd7cddfSDavid du Colombier 			BimapMimeParams(&bout, h->language->next);
5587dd7cddfSDavid du Colombier 		else
5597dd7cddfSDavid du Colombier 			Bimapstr(&bout, h->language->s);
5607dd7cddfSDavid du Colombier 	}else
5617dd7cddfSDavid du Colombier 		Bprint(&bout, "NIL");
5627dd7cddfSDavid du Colombier }
5637dd7cddfSDavid du Colombier 
5647dd7cddfSDavid du Colombier int
5657dd7cddfSDavid du Colombier BimapMimeParams(Biobuf *b, MimeHdr *mh)
5667dd7cddfSDavid du Colombier {
5677dd7cddfSDavid du Colombier 	char *sep;
5687dd7cddfSDavid du Colombier 	int n;
5697dd7cddfSDavid du Colombier 
5707dd7cddfSDavid du Colombier 	if(mh == nil)
5717dd7cddfSDavid du Colombier 		return Bprint(b, "NIL");
5727dd7cddfSDavid du Colombier 
5737dd7cddfSDavid du Colombier 	n = Bputc(b, '(');
5747dd7cddfSDavid du Colombier 
5757dd7cddfSDavid du Colombier 	sep = "";
5767dd7cddfSDavid du Colombier 	for(; mh != nil; mh = mh->next){
5777dd7cddfSDavid du Colombier 		n += Bprint(b, sep);
5787dd7cddfSDavid du Colombier 		n += Bimapstr(b, mh->s);
5797dd7cddfSDavid du Colombier 		n += Bputc(b, ' ');
5807dd7cddfSDavid du Colombier 		n += Bimapstr(b, mh->t);
5817dd7cddfSDavid du Colombier 		sep = " ";
5827dd7cddfSDavid du Colombier 	}
5837dd7cddfSDavid du Colombier 
5847dd7cddfSDavid du Colombier 	n += Bputc(b, ')');
5857dd7cddfSDavid du Colombier 	return n;
5867dd7cddfSDavid du Colombier }
5877dd7cddfSDavid du Colombier 
5887dd7cddfSDavid du Colombier /*
5897dd7cddfSDavid du Colombier  * print a list of addresses;
5907dd7cddfSDavid du Colombier  * each address is printed as '(' personalName AtDomainList mboxName hostName ')'
5917dd7cddfSDavid du Colombier  * the AtDomainList is always NIL
5927dd7cddfSDavid du Colombier  */
5937dd7cddfSDavid du Colombier int
5947dd7cddfSDavid du Colombier Bimapaddr(Biobuf *b, MAddr *a)
5957dd7cddfSDavid du Colombier {
5967dd7cddfSDavid du Colombier 	char *host, *sep;
5977dd7cddfSDavid du Colombier 	int n;
5987dd7cddfSDavid du Colombier 
5997dd7cddfSDavid du Colombier 	if(a == nil)
6007dd7cddfSDavid du Colombier 		return Bprint(b, "NIL");
6017dd7cddfSDavid du Colombier 
6027dd7cddfSDavid du Colombier 	n = Bputc(b, '(');
6037dd7cddfSDavid du Colombier 	sep = "";
6047dd7cddfSDavid du Colombier 	for(; a != nil; a = a->next){
6057dd7cddfSDavid du Colombier 		n += Bprint(b, "%s(", sep);
6067dd7cddfSDavid du Colombier 		n += Bimapstr(b, a->personal);
6077dd7cddfSDavid du Colombier 		n += Bprint(b," NIL ");
6087dd7cddfSDavid du Colombier 		n += Bimapstr(b, a->box);
6097dd7cddfSDavid du Colombier 		n += Bputc(b, ' ');
6107dd7cddfSDavid du Colombier 
6117dd7cddfSDavid du Colombier 		/*
6127dd7cddfSDavid du Colombier 		 * can't send NIL as hostName, since that is code for a group
6137dd7cddfSDavid du Colombier 		 */
6147dd7cddfSDavid du Colombier 		host = a->host;
6157dd7cddfSDavid du Colombier 		if(host == nil)
6167dd7cddfSDavid du Colombier 			host = "";
6177dd7cddfSDavid du Colombier 		n += Bimapstr(b, host);
6187dd7cddfSDavid du Colombier 
6197dd7cddfSDavid du Colombier 		n += Bputc(b, ')');
6207dd7cddfSDavid du Colombier 		sep = " ";
6217dd7cddfSDavid du Colombier 	}
6227dd7cddfSDavid du Colombier 	n += Bputc(b, ')');
6237dd7cddfSDavid du Colombier 	return n;
6247dd7cddfSDavid du Colombier }
625