xref: /plan9/acme/mail/src/mesg.c (revision 8ccc32ef0b9b7222ff49b683668eb07a18989e02)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
47dd7cddfSDavid du Colombier #include <thread.h>
57dd7cddfSDavid du Colombier #include <ctype.h>
67dd7cddfSDavid du Colombier #include <plumb.h>
77dd7cddfSDavid du Colombier #include "dat.h"
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier enum
107dd7cddfSDavid du Colombier {
117dd7cddfSDavid du Colombier 	DIRCHUNK = 32*sizeof(Dir)
127dd7cddfSDavid du Colombier };
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier char	regexchars[] = "\\/[].+?()*^$";
157dd7cddfSDavid du Colombier char	deleted[] = "(deleted)-";
167dd7cddfSDavid du Colombier char	deletedrx[] = "\\(deleted\\)-";
177dd7cddfSDavid du Colombier char	deletedrx01[] = "(\\(deleted\\)-)?";
187dd7cddfSDavid du Colombier char	deletedaddr[] = "-#0;/^\\(deleted\\)-/";
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier struct{
217dd7cddfSDavid du Colombier 	char	*type;
227dd7cddfSDavid du Colombier 	char	*port;
237dd7cddfSDavid du Colombier 	char *suffix;
247dd7cddfSDavid du Colombier } ports[] = {
2514cc0f53SDavid du Colombier 	"text/",			"edit",		".txt",
2614cc0f53SDavid du Colombier 	/* text must be first for plumbport() */
279a747e4fSDavid du Colombier 	"image/gif",			"image",	".gif",
289a747e4fSDavid du Colombier 	"image/jpeg",			"image",	".jpg",
299a747e4fSDavid du Colombier 	"image/jpeg",			"image",	".jpeg",
30dc72095aSDavid du Colombier 	"image/png",			"image",	".png",
3114cc0f53SDavid du Colombier 	"image/tiff",			"image",	".tif",
329a747e4fSDavid du Colombier 	"application/postscript",	"postscript",	".ps",
339a747e4fSDavid du Colombier 	"application/pdf",		"postscript",	".pdf",
349a747e4fSDavid du Colombier 	"application/msword",		"msword",	".doc",
359a747e4fSDavid du Colombier 	"application/rtf",		"msword",	".rtf",
3614cc0f53SDavid du Colombier 	"audio/x-wav",			"wav",		".wav",
377dd7cddfSDavid du Colombier 	nil,	nil
387dd7cddfSDavid du Colombier };
397dd7cddfSDavid du Colombier 
407dd7cddfSDavid du Colombier char *goodtypes[] = {
417dd7cddfSDavid du Colombier 	"text",
427dd7cddfSDavid du Colombier 	"text/plain",
437dd7cddfSDavid du Colombier 	"message/rfc822",
447dd7cddfSDavid du Colombier 	"text/richtext",
457dd7cddfSDavid du Colombier 	"text/tab-separated-values",
46*8ccc32efSDavid du Colombier 	"text/calendar",
477dd7cddfSDavid du Colombier 	"application/octet-stream",
487dd7cddfSDavid du Colombier 	nil,
497dd7cddfSDavid du Colombier };
507dd7cddfSDavid du Colombier 
517dd7cddfSDavid du Colombier struct{
527dd7cddfSDavid du Colombier 	char *type;
537dd7cddfSDavid du Colombier 	char	*ext;
547dd7cddfSDavid du Colombier } exts[] = {
557dd7cddfSDavid du Colombier 	"image/gif",	".gif",
567dd7cddfSDavid du Colombier 	"image/jpeg",	".jpg",
577dd7cddfSDavid du Colombier 	nil, nil
587dd7cddfSDavid du Colombier };
597dd7cddfSDavid du Colombier 
609a747e4fSDavid du Colombier char *okheaders[] =
619a747e4fSDavid du Colombier {
629a747e4fSDavid du Colombier 	"From:",
639a747e4fSDavid du Colombier 	"Date:",
649a747e4fSDavid du Colombier 	"To:",
659a747e4fSDavid du Colombier 	"CC:",
669a747e4fSDavid du Colombier 	"Subject:",
679a747e4fSDavid du Colombier 	nil
689a747e4fSDavid du Colombier };
699a747e4fSDavid du Colombier 
70c27b0bc9SDavid du Colombier char *extraheaders[] =
71c27b0bc9SDavid du Colombier {
72c4845253SDavid du Colombier 	"Resent-From:",
73c4845253SDavid du Colombier 	"Resent-To:",
74c4845253SDavid du Colombier 	"Sort:",
75c27b0bc9SDavid du Colombier 	nil,
76c27b0bc9SDavid du Colombier };
77c27b0bc9SDavid du Colombier 
787dd7cddfSDavid du Colombier char*
line(char * data,char ** pp)797dd7cddfSDavid du Colombier line(char *data, char **pp)
807dd7cddfSDavid du Colombier {
817dd7cddfSDavid du Colombier 	char *p, *q;
827dd7cddfSDavid du Colombier 
837dd7cddfSDavid du Colombier 	for(p=data; *p!='\0' && *p!='\n'; p++)
847dd7cddfSDavid du Colombier 		;
857dd7cddfSDavid du Colombier 	if(*p == '\n')
867dd7cddfSDavid du Colombier 		*pp = p+1;
877dd7cddfSDavid du Colombier 	else
887dd7cddfSDavid du Colombier 		*pp = p;
897dd7cddfSDavid du Colombier 	q = emalloc(p-data + 1);
907dd7cddfSDavid du Colombier 	memmove(q, data, p-data);
917dd7cddfSDavid du Colombier 	return q;
927dd7cddfSDavid du Colombier }
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier void
scanheaders(Message * m,char * dir)957dd7cddfSDavid du Colombier scanheaders(Message *m, char *dir)
967dd7cddfSDavid du Colombier {
979a747e4fSDavid du Colombier 	char *s, *t, *u, *f;
987dd7cddfSDavid du Colombier 
999a747e4fSDavid du Colombier 	s = f = readfile(dir, "header", nil);
1007dd7cddfSDavid du Colombier 	if(s != nil)
1017dd7cddfSDavid du Colombier 		while(*s){
1027dd7cddfSDavid du Colombier 			t = line(s, &s);
1037dd7cddfSDavid du Colombier 			if(strncmp(t, "From: ", 6) == 0){
1047dd7cddfSDavid du Colombier 				m->fromcolon = estrdup(t+6);
1057dd7cddfSDavid du Colombier 				/* remove all quotes; they're ugly and irregular */
1067dd7cddfSDavid du Colombier 				for(u=m->fromcolon; *u; u++)
1077dd7cddfSDavid du Colombier 					if(*u == '"')
1087dd7cddfSDavid du Colombier 						memmove(u, u+1, strlen(u));
1097dd7cddfSDavid du Colombier 			}
1107dd7cddfSDavid du Colombier 			if(strncmp(t, "Subject: ", 9) == 0)
1117dd7cddfSDavid du Colombier 				m->subject = estrdup(t+9);
1127dd7cddfSDavid du Colombier 			free(t);
1137dd7cddfSDavid du Colombier 		}
1147dd7cddfSDavid du Colombier 	if(m->fromcolon == nil)
1157dd7cddfSDavid du Colombier 		m->fromcolon = estrdup(m->from);
1169a747e4fSDavid du Colombier 	free(f);
1177dd7cddfSDavid du Colombier }
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier int
loadinfo(Message * m,char * dir)1207dd7cddfSDavid du Colombier loadinfo(Message *m, char *dir)
1217dd7cddfSDavid du Colombier {
1227dd7cddfSDavid du Colombier 	int n;
1237dd7cddfSDavid du Colombier 	char *data, *p, *s;
1247dd7cddfSDavid du Colombier 
1257dd7cddfSDavid du Colombier 	data = readfile(dir, "info", &n);
1267dd7cddfSDavid du Colombier 	if(data == nil)
1277dd7cddfSDavid du Colombier 		return 0;
1287dd7cddfSDavid du Colombier 	m->from = line(data, &p);
1297dd7cddfSDavid du Colombier 	scanheaders(m, dir);	/* depends on m->from being set */
1307dd7cddfSDavid du Colombier 	m->to = line(p, &p);
1317dd7cddfSDavid du Colombier 	m->cc = line(p, &p);
1327dd7cddfSDavid du Colombier 	m->replyto = line(p, &p);
1337dd7cddfSDavid du Colombier 	m->date = line(p, &p);
1347dd7cddfSDavid du Colombier 	s = line(p, &p);
1357dd7cddfSDavid du Colombier 	if(m->subject == nil)
1367dd7cddfSDavid du Colombier 		m->subject = s;
1377dd7cddfSDavid du Colombier 	else
1387dd7cddfSDavid du Colombier 		free(s);
1397dd7cddfSDavid du Colombier 	m->type = line(p, &p);
1407dd7cddfSDavid du Colombier 	m->disposition = line(p, &p);
1417dd7cddfSDavid du Colombier 	m->filename = line(p, &p);
1427dd7cddfSDavid du Colombier 	m->digest = line(p, &p);
1437dd7cddfSDavid du Colombier 	free(data);
1447dd7cddfSDavid du Colombier 	return 1;
1457dd7cddfSDavid du Colombier }
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier int
isnumeric(char * s)1487dd7cddfSDavid du Colombier isnumeric(char *s)
1497dd7cddfSDavid du Colombier {
1507dd7cddfSDavid du Colombier 	while(*s){
1517dd7cddfSDavid du Colombier 		if(!isdigit(*s))
1527dd7cddfSDavid du Colombier 			return 0;
1537dd7cddfSDavid du Colombier 		s++;
1547dd7cddfSDavid du Colombier 	}
1557dd7cddfSDavid du Colombier 	return 1;
1567dd7cddfSDavid du Colombier }
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier Dir*
loaddir(char * name,int * np)1599a747e4fSDavid du Colombier loaddir(char *name, int *np)
1607dd7cddfSDavid du Colombier {
1619a747e4fSDavid du Colombier 	int fd;
1627dd7cddfSDavid du Colombier 	Dir *dp;
1637dd7cddfSDavid du Colombier 
1647dd7cddfSDavid du Colombier 	fd = open(name, OREAD);
1657dd7cddfSDavid du Colombier 	if(fd < 0)
1669a747e4fSDavid du Colombier 		return nil;
1679a747e4fSDavid du Colombier 	*np = dirreadall(fd, &dp);
1687dd7cddfSDavid du Colombier 	close(fd);
1697dd7cddfSDavid du Colombier 	return dp;
1707dd7cddfSDavid du Colombier }
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier void
readmbox(Message * mbox,char * dir,char * subdir)1737dd7cddfSDavid du Colombier readmbox(Message *mbox, char *dir, char *subdir)
1747dd7cddfSDavid du Colombier {
1757dd7cddfSDavid du Colombier 	char *name;
1767dd7cddfSDavid du Colombier 	Dir *d, *dirp;
1779a747e4fSDavid du Colombier 	int i, n;
1787dd7cddfSDavid du Colombier 
1797dd7cddfSDavid du Colombier 	name = estrstrdup(dir, subdir);
1809a747e4fSDavid du Colombier 	dirp = loaddir(name, &n);
1819a747e4fSDavid du Colombier 	mbox->recursed = 1;
1829a747e4fSDavid du Colombier 	if(dirp)
1839a747e4fSDavid du Colombier 		for(i=0; i<n; i++){
1849a747e4fSDavid du Colombier 			d = &dirp[i];
1857dd7cddfSDavid du Colombier 			if(isnumeric(d->name))
1867dd7cddfSDavid du Colombier 				mesgadd(mbox, name, d, nil);
1879a747e4fSDavid du Colombier 		}
1887dd7cddfSDavid du Colombier 	free(dirp);
1897dd7cddfSDavid du Colombier 	free(name);
1907dd7cddfSDavid du Colombier }
1917dd7cddfSDavid du Colombier 
1927dd7cddfSDavid du Colombier /* add message to box, in increasing numerical order */
1937dd7cddfSDavid du Colombier int
mesgadd(Message * mbox,char * dir,Dir * d,char * digest)1947dd7cddfSDavid du Colombier mesgadd(Message *mbox, char *dir, Dir *d, char *digest)
1957dd7cddfSDavid du Colombier {
1967dd7cddfSDavid du Colombier 	Message *m;
1977dd7cddfSDavid du Colombier 	char *name;
1987dd7cddfSDavid du Colombier 	int loaded;
1997dd7cddfSDavid du Colombier 
2007dd7cddfSDavid du Colombier 	m = emalloc(sizeof(Message));
2017dd7cddfSDavid du Colombier 	m->name = estrstrdup(d->name, "/");
2027dd7cddfSDavid du Colombier 	m->next = nil;
2037dd7cddfSDavid du Colombier 	m->prev = mbox->tail;
2049a747e4fSDavid du Colombier 	m->level= mbox->level+1;
2059a747e4fSDavid du Colombier 	m->recursed = 0;
2067dd7cddfSDavid du Colombier 	name = estrstrdup(dir, m->name);
2077dd7cddfSDavid du Colombier 	loaded = loadinfo(m, name);
2087dd7cddfSDavid du Colombier 	free(name);
2097dd7cddfSDavid du Colombier 	/* if two upas/fs are running, we can get misled, so check digest before accepting message */
2107dd7cddfSDavid du Colombier 	if(loaded==0 || (digest!=nil && m->digest!=nil && strcmp(digest, m->digest)!=0)){
2117dd7cddfSDavid du Colombier 		mesgfreeparts(m);
2127dd7cddfSDavid du Colombier 		free(m);
2137dd7cddfSDavid du Colombier 		return 0;
2147dd7cddfSDavid du Colombier 	}
2157dd7cddfSDavid du Colombier 	if(mbox->tail != nil)
2167dd7cddfSDavid du Colombier 		mbox->tail->next = m;
2177dd7cddfSDavid du Colombier 	mbox->tail = m;
2187dd7cddfSDavid du Colombier 	if(mbox->head == nil)
2197dd7cddfSDavid du Colombier 		mbox->head = m;
2209a747e4fSDavid du Colombier 
2219a747e4fSDavid du Colombier 	if (m->level != 1){
2229a747e4fSDavid du Colombier 		m->recursed = 1;
2237dd7cddfSDavid du Colombier 		readmbox(m, dir, m->name);
2249a747e4fSDavid du Colombier 	}
2257dd7cddfSDavid du Colombier 	return 1;
2267dd7cddfSDavid du Colombier }
2277dd7cddfSDavid du Colombier 
2287dd7cddfSDavid du Colombier int
thisyear(char * year)2297dd7cddfSDavid du Colombier thisyear(char *year)
2307dd7cddfSDavid du Colombier {
2317dd7cddfSDavid du Colombier 	static char now[10];
2327dd7cddfSDavid du Colombier 	char *s;
2337dd7cddfSDavid du Colombier 
2347dd7cddfSDavid du Colombier 	if(now[0] == '\0'){
2357dd7cddfSDavid du Colombier 		s = ctime(time(nil));
2367dd7cddfSDavid du Colombier 		strcpy(now, s+24);
2377dd7cddfSDavid du Colombier 	}
2387dd7cddfSDavid du Colombier 	return strncmp(year, now, 4) == 0;
2397dd7cddfSDavid du Colombier }
2407dd7cddfSDavid du Colombier 
2417dd7cddfSDavid du Colombier char*
stripdate(char * as)2427dd7cddfSDavid du Colombier stripdate(char *as)
2437dd7cddfSDavid du Colombier {
2447dd7cddfSDavid du Colombier 	int n;
2457dd7cddfSDavid du Colombier 	char *s, *fld[10];
2467dd7cddfSDavid du Colombier 
2477dd7cddfSDavid du Colombier 	as = estrdup(as);
2487dd7cddfSDavid du Colombier 	s = estrdup(as);
2497dd7cddfSDavid du Colombier 	n = tokenize(s, fld, 10);
2507dd7cddfSDavid du Colombier 	if(n > 5){
2517dd7cddfSDavid du Colombier 		sprint(as, "%.3s ", fld[0]);	/* day */
2527dd7cddfSDavid du Colombier 		/* some dates have 19 Apr, some Apr 19 */
2537dd7cddfSDavid du Colombier 		if(strlen(fld[1])<4 && isnumeric(fld[1]))
2547dd7cddfSDavid du Colombier 			sprint(as+strlen(as), "%.3s %.3s ", fld[1], fld[2]);	/* date, month */
2557dd7cddfSDavid du Colombier 		else
2567dd7cddfSDavid du Colombier 			sprint(as+strlen(as), "%.3s %.3s ", fld[2], fld[1]);	/* date, month */
2577dd7cddfSDavid du Colombier 		/* do we use time or year?  depends on whether year matches this one */
2587dd7cddfSDavid du Colombier 		if(thisyear(fld[5])){
2597dd7cddfSDavid du Colombier 			if(strchr(fld[3], ':') != nil)
2607dd7cddfSDavid du Colombier 				sprint(as+strlen(as), "%.5s ", fld[3]);	/* time */
2617dd7cddfSDavid du Colombier 			else if(strchr(fld[4], ':') != nil)
2627dd7cddfSDavid du Colombier 				sprint(as+strlen(as), "%.5s ", fld[4]);	/* time */
2637dd7cddfSDavid du Colombier 		}else
2647dd7cddfSDavid du Colombier 			sprint(as+strlen(as), "%.4s ", fld[5]);	/* year */
2657dd7cddfSDavid du Colombier 	}
2667dd7cddfSDavid du Colombier 	free(s);
2677dd7cddfSDavid du Colombier 	return as;
2687dd7cddfSDavid du Colombier }
2697dd7cddfSDavid du Colombier 
2707dd7cddfSDavid du Colombier char*
readfile(char * dir,char * name,int * np)2717dd7cddfSDavid du Colombier readfile(char *dir, char *name, int *np)
2727dd7cddfSDavid du Colombier {
2737dd7cddfSDavid du Colombier 	char *file, *data;
2749a747e4fSDavid du Colombier 	int fd, len;
2759a747e4fSDavid du Colombier 	Dir *d;
2767dd7cddfSDavid du Colombier 
2777dd7cddfSDavid du Colombier 	if(np != nil)
2787dd7cddfSDavid du Colombier 		*np = 0;
2797dd7cddfSDavid du Colombier 	file = estrstrdup(dir, name);
2807dd7cddfSDavid du Colombier 	fd = open(file, OREAD);
2817dd7cddfSDavid du Colombier 	if(fd < 0)
2827dd7cddfSDavid du Colombier 		return nil;
2839a747e4fSDavid du Colombier 	d = dirfstat(fd);
2847dd7cddfSDavid du Colombier 	free(file);
2859a747e4fSDavid du Colombier 	len = 0;
2869a747e4fSDavid du Colombier 	if(d != nil)
2879a747e4fSDavid du Colombier 		len = d->length;
2889a747e4fSDavid du Colombier 	free(d);
2899a747e4fSDavid du Colombier 	data = emalloc(len+1);
2909a747e4fSDavid du Colombier 	read(fd, data, len);
2917dd7cddfSDavid du Colombier 	close(fd);
2927dd7cddfSDavid du Colombier 	if(np != nil)
2939a747e4fSDavid du Colombier 		*np = len;
2947dd7cddfSDavid du Colombier 	return data;
2957dd7cddfSDavid du Colombier }
2967dd7cddfSDavid du Colombier 
2977dd7cddfSDavid du Colombier char*
info(Message * m,int ind,int ogf)2983ff48bf5SDavid du Colombier info(Message *m, int ind, int ogf)
2997dd7cddfSDavid du Colombier {
3007dd7cddfSDavid du Colombier 	char *i;
3013ff48bf5SDavid du Colombier 	int j, len, lens;
3023ff48bf5SDavid du Colombier 	char *p;
3033ff48bf5SDavid du Colombier 	char fmt[80], s[80];
3043ff48bf5SDavid du Colombier 
3053ff48bf5SDavid du Colombier 	if (ogf)
3063ff48bf5SDavid du Colombier 		p=m->to;
3073ff48bf5SDavid du Colombier 	else
3083ff48bf5SDavid du Colombier 		p=m->fromcolon;
3097dd7cddfSDavid du Colombier 
3109a747e4fSDavid du Colombier 	if(ind==0 && shortmenu){
3113ff48bf5SDavid du Colombier 		len = 30;
3123ff48bf5SDavid du Colombier 		lens = 30;
3133ff48bf5SDavid du Colombier 		if(shortmenu > 1){
3143ff48bf5SDavid du Colombier 			len = 10;
3153ff48bf5SDavid du Colombier 			lens = 25;
3163ff48bf5SDavid du Colombier 		}
3179a747e4fSDavid du Colombier 		if(ind==0 && m->subject[0]=='\0'){
3183ff48bf5SDavid du Colombier 			snprint(fmt, sizeof fmt, " %%-%d.%ds", len, len);
3193ff48bf5SDavid du Colombier 			snprint(s, sizeof s, fmt, p);
3209a747e4fSDavid du Colombier 		}else{
3213ff48bf5SDavid du Colombier 			snprint(fmt, sizeof fmt, " %%-%d.%ds  %%-%d.%ds", len, len, lens, lens);
3223ff48bf5SDavid du Colombier 			snprint(s, sizeof s, fmt, p, m->subject);
3239a747e4fSDavid du Colombier 		}
3249a747e4fSDavid du Colombier 		i = estrdup(s);
3259a747e4fSDavid du Colombier 
3269a747e4fSDavid du Colombier 		return i;
3279a747e4fSDavid du Colombier 	}
3289a747e4fSDavid du Colombier 
3297dd7cddfSDavid du Colombier 	i = estrdup("");
3303ff48bf5SDavid du Colombier 	i = eappend(i, "\t", p);
3317dd7cddfSDavid du Colombier 	i = egrow(i, "\t", stripdate(m->date));
3327dd7cddfSDavid du Colombier 	if(ind == 0){
3337dd7cddfSDavid du Colombier 		if(strcmp(m->type, "text")!=0 && strncmp(m->type, "text/", 5)!=0 &&
3347dd7cddfSDavid du Colombier 		   strncmp(m->type, "multipart/", 10)!=0)
3357dd7cddfSDavid du Colombier 			i = egrow(i, "\t(", estrstrdup(m->type, ")"));
3367dd7cddfSDavid du Colombier 	}else if(strncmp(m->type, "multipart/", 10) != 0)
3377dd7cddfSDavid du Colombier 		i = egrow(i, "\t(", estrstrdup(m->type, ")"));
3387dd7cddfSDavid du Colombier 	if(m->subject[0] != '\0'){
3397dd7cddfSDavid du Colombier 		i = eappend(i, "\n", nil);
3407dd7cddfSDavid du Colombier 		for(j=0; j<ind; j++)
3417dd7cddfSDavid du Colombier 			i = eappend(i, "\t", nil);
3427dd7cddfSDavid du Colombier 		i = eappend(i, "\t", m->subject);
3437dd7cddfSDavid du Colombier 	}
3447dd7cddfSDavid du Colombier 	return i;
3457dd7cddfSDavid du Colombier }
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier void
mesgmenu0(Window * w,Message * mbox,char * realdir,char * dir,int ind,Biobuf * fd,int onlyone,int dotail)3489a747e4fSDavid du Colombier mesgmenu0(Window *w, Message *mbox, char *realdir, char *dir, int ind, Biobuf *fd, int onlyone, int dotail)
3497dd7cddfSDavid du Colombier {
3507dd7cddfSDavid du Colombier 	int i;
3517dd7cddfSDavid du Colombier 	Message *m;
3527dd7cddfSDavid du Colombier 	char *name, *tmp;
3533ff48bf5SDavid du Colombier 	int ogf=0;
3543ff48bf5SDavid du Colombier 
355c27b0bc9SDavid du Colombier 	if(strstr(realdir, "outgoing") != nil)
3563ff48bf5SDavid du Colombier 		ogf=1;
3577dd7cddfSDavid du Colombier 
3587dd7cddfSDavid du Colombier 	/* show mail box in reverse order, pieces in forward order */
3597dd7cddfSDavid du Colombier 	if(ind > 0)
3607dd7cddfSDavid du Colombier 		m = mbox->head;
3617dd7cddfSDavid du Colombier 	else
3627dd7cddfSDavid du Colombier 		m = mbox->tail;
3637dd7cddfSDavid du Colombier 	while(m != nil){
3647dd7cddfSDavid du Colombier 		for(i=0; i<ind; i++)
3657dd7cddfSDavid du Colombier 			Bprint(fd, "\t");
3667dd7cddfSDavid du Colombier 		if(ind != 0)
3677dd7cddfSDavid du Colombier 			Bprint(fd, "  ");
3687dd7cddfSDavid du Colombier 		name = estrstrdup(dir, m->name);
3693ff48bf5SDavid du Colombier 		tmp = info(m, ind, ogf);
3707dd7cddfSDavid du Colombier 		Bprint(fd, "%s%s\n", name, tmp);
3717dd7cddfSDavid du Colombier 		free(tmp);
3729a747e4fSDavid du Colombier 		if(dotail && m->tail)
3739a747e4fSDavid du Colombier 			mesgmenu0(w, m, realdir, name, ind+1, fd, 0, dotail);
3747dd7cddfSDavid du Colombier 		free(name);
3757dd7cddfSDavid du Colombier 		if(ind)
3767dd7cddfSDavid du Colombier 			m = m->next;
3777dd7cddfSDavid du Colombier 		else
3787dd7cddfSDavid du Colombier 			m = m->prev;
3797dd7cddfSDavid du Colombier 		if(onlyone)
3807dd7cddfSDavid du Colombier 			m = nil;
3817dd7cddfSDavid du Colombier 	}
3827dd7cddfSDavid du Colombier }
3837dd7cddfSDavid du Colombier 
3847dd7cddfSDavid du Colombier void
mesgmenu(Window * w,Message * mbox)3857dd7cddfSDavid du Colombier mesgmenu(Window *w, Message *mbox)
3867dd7cddfSDavid du Colombier {
3877dd7cddfSDavid du Colombier 	winopenbody(w, OWRITE);
3889a747e4fSDavid du Colombier 	mesgmenu0(w, mbox, mbox->name, "", 0, w->body, 0, !shortmenu);
3897dd7cddfSDavid du Colombier 	winclosebody(w);
3907dd7cddfSDavid du Colombier }
3917dd7cddfSDavid du Colombier 
3927dd7cddfSDavid du Colombier /* one new message has arrived, as mbox->tail */
3937dd7cddfSDavid du Colombier void
mesgmenunew(Window * w,Message * mbox)3947dd7cddfSDavid du Colombier mesgmenunew(Window *w, Message *mbox)
3957dd7cddfSDavid du Colombier {
3967dd7cddfSDavid du Colombier 	Biobuf *b;
3977dd7cddfSDavid du Colombier 
3987dd7cddfSDavid du Colombier 	winselect(w, "0", 0);
3997dd7cddfSDavid du Colombier 	w->data = winopenfile(w, "data");
4007dd7cddfSDavid du Colombier 	b = emalloc(sizeof(Biobuf));
4017dd7cddfSDavid du Colombier 	Binit(b, w->data, OWRITE);
4029a747e4fSDavid du Colombier 	mesgmenu0(w, mbox, mbox->name, "", 0, b, 1, !shortmenu);
4037dd7cddfSDavid du Colombier 	Bterm(b);
4049a747e4fSDavid du Colombier 	free(b);
4057dd7cddfSDavid du Colombier 	if(!mbox->dirty)
4067dd7cddfSDavid du Colombier 		winclean(w);
4077dd7cddfSDavid du Colombier 	/* select tag line plus following indented lines, but not final newline (it's distinctive) */
4087dd7cddfSDavid du Colombier 	winselect(w, "0/.*\\n((\t.*\\n)*\t.*)?/", 1);
4097dd7cddfSDavid du Colombier 	close(w->addr);
4107dd7cddfSDavid du Colombier 	close(w->data);
4117dd7cddfSDavid du Colombier 	w->addr = -1;
4127dd7cddfSDavid du Colombier 	w->data = -1;
4137dd7cddfSDavid du Colombier }
4147dd7cddfSDavid du Colombier 
4157dd7cddfSDavid du Colombier char*
name2regexp(char * prefix,char * s)4167dd7cddfSDavid du Colombier name2regexp(char *prefix, char *s)
4177dd7cddfSDavid du Colombier {
4187dd7cddfSDavid du Colombier 	char *buf, *p, *q;
4197dd7cddfSDavid du Colombier 
4207dd7cddfSDavid du Colombier 	buf = emalloc(strlen(prefix)+2*strlen(s)+50);	/* leave room to append more */
4217dd7cddfSDavid du Colombier 	p = buf;
4227dd7cddfSDavid du Colombier 	*p++ = '0';
4237dd7cddfSDavid du Colombier 	*p++ = '/';
4247dd7cddfSDavid du Colombier 	*p++ = '^';
4257dd7cddfSDavid du Colombier 	strcpy(p, prefix);
4267dd7cddfSDavid du Colombier 	p += strlen(prefix);
4277dd7cddfSDavid du Colombier 	for(q=s; *q!='\0'; q++){
4287dd7cddfSDavid du Colombier 		if(strchr(regexchars, *q) != nil)
4297dd7cddfSDavid du Colombier 			*p++ = '\\';
4307dd7cddfSDavid du Colombier 		*p++ = *q;
4317dd7cddfSDavid du Colombier 	}
4327dd7cddfSDavid du Colombier 	*p++ = '/';
4337dd7cddfSDavid du Colombier 	*p = '\0';
4347dd7cddfSDavid du Colombier 	return buf;
4357dd7cddfSDavid du Colombier }
4367dd7cddfSDavid du Colombier 
4377dd7cddfSDavid du Colombier void
mesgmenumarkdel(Window * w,Message * mbox,Message * m,int writeback)4387dd7cddfSDavid du Colombier mesgmenumarkdel(Window *w, Message *mbox, Message *m, int writeback)
4397dd7cddfSDavid du Colombier {
4407dd7cddfSDavid du Colombier 	char *buf;
4417dd7cddfSDavid du Colombier 
4427dd7cddfSDavid du Colombier 
4437dd7cddfSDavid du Colombier 	if(m->deleted)
4447dd7cddfSDavid du Colombier 		return;
4457dd7cddfSDavid du Colombier 	m->writebackdel = writeback;
4467dd7cddfSDavid du Colombier 	if(w->data < 0)
4477dd7cddfSDavid du Colombier 		w->data = winopenfile(w, "data");
4487dd7cddfSDavid du Colombier 	buf = name2regexp("", m->name);
4497dd7cddfSDavid du Colombier 	strcat(buf, "-#0");
4507dd7cddfSDavid du Colombier 	if(winselect(w, buf, 1))
4517dd7cddfSDavid du Colombier 		write(w->data, deleted, 10);
4527dd7cddfSDavid du Colombier 	free(buf);
4537dd7cddfSDavid du Colombier 	close(w->data);
4547dd7cddfSDavid du Colombier 	close(w->addr);
4557dd7cddfSDavid du Colombier 	w->addr = w->data = -1;
4567dd7cddfSDavid du Colombier 	mbox->dirty = 1;
4577dd7cddfSDavid du Colombier 	m->deleted = 1;
4587dd7cddfSDavid du Colombier }
4597dd7cddfSDavid du Colombier 
4607dd7cddfSDavid du Colombier void
mesgmenumarkundel(Window * w,Message *,Message * m)4617dd7cddfSDavid du Colombier mesgmenumarkundel(Window *w, Message*, Message *m)
4627dd7cddfSDavid du Colombier {
4637dd7cddfSDavid du Colombier 	char *buf;
4647dd7cddfSDavid du Colombier 
4657dd7cddfSDavid du Colombier 	if(m->deleted == 0)
4667dd7cddfSDavid du Colombier 		return;
4677dd7cddfSDavid du Colombier 	if(w->data < 0)
4687dd7cddfSDavid du Colombier 		w->data = winopenfile(w, "data");
4697dd7cddfSDavid du Colombier 	buf = name2regexp(deletedrx, m->name);
4707dd7cddfSDavid du Colombier 	if(winselect(w, buf, 1))
4717dd7cddfSDavid du Colombier 		if(winsetaddr(w, deletedaddr, 1))
4727dd7cddfSDavid du Colombier 			write(w->data, "", 0);
4737dd7cddfSDavid du Colombier 	free(buf);
4747dd7cddfSDavid du Colombier 	close(w->data);
4757dd7cddfSDavid du Colombier 	close(w->addr);
4767dd7cddfSDavid du Colombier 	w->addr = w->data = -1;
4777dd7cddfSDavid du Colombier 	m->deleted = 0;
4787dd7cddfSDavid du Colombier }
4797dd7cddfSDavid du Colombier 
4807dd7cddfSDavid du Colombier void
mesgmenudel(Window * w,Message * mbox,Message * m)4817dd7cddfSDavid du Colombier mesgmenudel(Window *w, Message *mbox, Message *m)
4827dd7cddfSDavid du Colombier {
4837dd7cddfSDavid du Colombier 	char *buf;
4847dd7cddfSDavid du Colombier 
4857dd7cddfSDavid du Colombier 	if(w->data < 0)
4867dd7cddfSDavid du Colombier 		w->data = winopenfile(w, "data");
4877dd7cddfSDavid du Colombier 	buf = name2regexp(deletedrx, m->name);
4887dd7cddfSDavid du Colombier 	if(winsetaddr(w, buf, 1) && winsetaddr(w, ".,./.*\\n(\t.*\\n)*/", 1))
4897dd7cddfSDavid du Colombier 		write(w->data, "", 0);
4907dd7cddfSDavid du Colombier 	free(buf);
4917dd7cddfSDavid du Colombier 	close(w->data);
4927dd7cddfSDavid du Colombier 	close(w->addr);
4937dd7cddfSDavid du Colombier 	w->addr = w->data = -1;
4947dd7cddfSDavid du Colombier 	mbox->dirty = 1;
4957dd7cddfSDavid du Colombier 	m->deleted = 1;
4967dd7cddfSDavid du Colombier }
4977dd7cddfSDavid du Colombier 
4987dd7cddfSDavid du Colombier void
mesgmenumark(Window * w,char * which,char * mark)4997dd7cddfSDavid du Colombier mesgmenumark(Window *w, char *which, char *mark)
5007dd7cddfSDavid du Colombier {
5017dd7cddfSDavid du Colombier 	char *buf;
5027dd7cddfSDavid du Colombier 
5037dd7cddfSDavid du Colombier 	if(w->data < 0)
5047dd7cddfSDavid du Colombier 		w->data = winopenfile(w, "data");
5057dd7cddfSDavid du Colombier 	buf = name2regexp(deletedrx01, which);
5067dd7cddfSDavid du Colombier 	if(winsetaddr(w, buf, 1) && winsetaddr(w, "+0-#1", 1))	/* go to end of line */
5077dd7cddfSDavid du Colombier 		write(w->data, mark, strlen(mark));
5087dd7cddfSDavid du Colombier 	free(buf);
5097dd7cddfSDavid du Colombier 	close(w->data);
5107dd7cddfSDavid du Colombier 	close(w->addr);
5117dd7cddfSDavid du Colombier 	w->addr = w->data = -1;
5127dd7cddfSDavid du Colombier 	if(!mbox.dirty)
5137dd7cddfSDavid du Colombier 		winclean(w);
5147dd7cddfSDavid du Colombier }
5157dd7cddfSDavid du Colombier 
5167dd7cddfSDavid du Colombier void
mesgfreeparts(Message * m)5177dd7cddfSDavid du Colombier mesgfreeparts(Message *m)
5187dd7cddfSDavid du Colombier {
5197dd7cddfSDavid du Colombier 	free(m->name);
5207dd7cddfSDavid du Colombier 	free(m->replyname);
5217dd7cddfSDavid du Colombier 	free(m->fromcolon);
5227dd7cddfSDavid du Colombier 	free(m->from);
5237dd7cddfSDavid du Colombier 	free(m->to);
5247dd7cddfSDavid du Colombier 	free(m->cc);
5257dd7cddfSDavid du Colombier 	free(m->replyto);
5267dd7cddfSDavid du Colombier 	free(m->date);
5277dd7cddfSDavid du Colombier 	free(m->subject);
5287dd7cddfSDavid du Colombier 	free(m->type);
5297dd7cddfSDavid du Colombier 	free(m->disposition);
5307dd7cddfSDavid du Colombier 	free(m->filename);
5317dd7cddfSDavid du Colombier 	free(m->digest);
5327dd7cddfSDavid du Colombier }
5337dd7cddfSDavid du Colombier 
5347dd7cddfSDavid du Colombier void
mesgdel(Message * mbox,Message * m)5357dd7cddfSDavid du Colombier mesgdel(Message *mbox, Message *m)
5367dd7cddfSDavid du Colombier {
5377dd7cddfSDavid du Colombier 	Message *n, *next;
5387dd7cddfSDavid du Colombier 
5397dd7cddfSDavid du Colombier 	if(m->opened)
54092fd5f07SDavid du Colombier 		error("internal error: deleted message still open in mesgdel");
5417dd7cddfSDavid du Colombier 	/* delete subparts */
5427dd7cddfSDavid du Colombier 	for(n=m->head; n!=nil; n=next){
5437dd7cddfSDavid du Colombier 		next = n->next;
5447dd7cddfSDavid du Colombier 		mesgdel(m, n);
5457dd7cddfSDavid du Colombier 	}
5467dd7cddfSDavid du Colombier 	/* remove this message from list */
5477dd7cddfSDavid du Colombier 	if(m->next)
5487dd7cddfSDavid du Colombier 		m->next->prev = m->prev;
5497dd7cddfSDavid du Colombier 	else
5507dd7cddfSDavid du Colombier 		mbox->tail = m->prev;
5517dd7cddfSDavid du Colombier 	if(m->prev)
5527dd7cddfSDavid du Colombier 		m->prev->next = m->next;
5537dd7cddfSDavid du Colombier 	else
5547dd7cddfSDavid du Colombier 		mbox->head = m->next;
5557dd7cddfSDavid du Colombier 
5567dd7cddfSDavid du Colombier 	mesgfreeparts(m);
5577dd7cddfSDavid du Colombier }
5587dd7cddfSDavid du Colombier 
5597dd7cddfSDavid du Colombier int
mesgsave(Message * m,char * s)5607dd7cddfSDavid du Colombier mesgsave(Message *m, char *s)
5617dd7cddfSDavid du Colombier {
5627dd7cddfSDavid du Colombier 	int ofd, n, k, ret;
5637dd7cddfSDavid du Colombier 	char *t, *raw, *unixheader, *all;
5647dd7cddfSDavid du Colombier 
5657dd7cddfSDavid du Colombier 	t = estrstrdup(mbox.name, m->name);
5667dd7cddfSDavid du Colombier 	raw = readfile(t, "raw", &n);
5677dd7cddfSDavid du Colombier 	unixheader = readfile(t, "unixheader", &k);
5687dd7cddfSDavid du Colombier 	if(raw==nil || unixheader==nil){
5699a747e4fSDavid du Colombier 		fprint(2, "Mail: can't read %s: %r\n", t);
5707dd7cddfSDavid du Colombier 		free(t);
5717dd7cddfSDavid du Colombier 		return 0;
5727dd7cddfSDavid du Colombier 	}
5737dd7cddfSDavid du Colombier 	free(t);
5747dd7cddfSDavid du Colombier 
575853458f3SDavid du Colombier 	all = emalloc(k+n+1);
576853458f3SDavid du Colombier 	memmove(all, unixheader, k);
577853458f3SDavid du Colombier 	memmove(all+k, raw, n);
578853458f3SDavid du Colombier 	memmove(all+k+n, "\n", 1);
579853458f3SDavid du Colombier 	n += k+1;
580853458f3SDavid du Colombier 	free(unixheader);
5817dd7cddfSDavid du Colombier 	free(raw);
5827dd7cddfSDavid du Colombier 	ret = 1;
5837dd7cddfSDavid du Colombier 	s = estrdup(s);
5847dd7cddfSDavid du Colombier 	if(s[0] != '/')
5857dd7cddfSDavid du Colombier 		s = egrow(estrdup(mailboxdir), "/", s);
5867dd7cddfSDavid du Colombier 	ofd = open(s, OWRITE);
5877dd7cddfSDavid du Colombier 	if(ofd < 0){
5889a747e4fSDavid du Colombier 		fprint(2, "Mail: can't open %s: %r\n", s);
5897dd7cddfSDavid du Colombier 		ret = 0;
590853458f3SDavid du Colombier 	}else if(seek(ofd, 0LL, 2)<0 || write(ofd, all, n)!=n){
5919a747e4fSDavid du Colombier 		fprint(2, "Mail: save failed: can't write %s: %r\n", s);
5927dd7cddfSDavid du Colombier 		ret = 0;
5937dd7cddfSDavid du Colombier 	}
5947dd7cddfSDavid du Colombier 	free(all);
5957dd7cddfSDavid du Colombier 	close(ofd);
5967dd7cddfSDavid du Colombier 	free(s);
5977dd7cddfSDavid du Colombier 	return ret;
5987dd7cddfSDavid du Colombier }
5997dd7cddfSDavid du Colombier 
6007dd7cddfSDavid du Colombier int
mesgcommand(Message * m,char * cmd)6017dd7cddfSDavid du Colombier mesgcommand(Message *m, char *cmd)
6027dd7cddfSDavid du Colombier {
6039a747e4fSDavid du Colombier 	char *s;
6049a747e4fSDavid du Colombier 	char *args[10];
6059a747e4fSDavid du Colombier 	int ok, ret, nargs;
6067dd7cddfSDavid du Colombier 
6077dd7cddfSDavid du Colombier 	s = cmd;
6087dd7cddfSDavid du Colombier 	ret = 1;
6099a747e4fSDavid du Colombier 	nargs = tokenize(s, args, nelem(args));
6109a747e4fSDavid du Colombier 	if(nargs == 0)
6119a747e4fSDavid du Colombier 		return 0;
6129a747e4fSDavid du Colombier 	if(strcmp(args[0], "Post") == 0){
6137dd7cddfSDavid du Colombier 		mesgsend(m);
6147dd7cddfSDavid du Colombier 		goto Return;
6157dd7cddfSDavid du Colombier 	}
6169a747e4fSDavid du Colombier 	if(strncmp(args[0], "Save", 4) == 0){
6177dd7cddfSDavid du Colombier 		if(m->isreply)
6187dd7cddfSDavid du Colombier 			goto Return;
6199a747e4fSDavid du Colombier 		s = estrdup("\t[saved");
6209a747e4fSDavid du Colombier 		if(nargs==1 || strcmp(args[1], "")==0){
6217dd7cddfSDavid du Colombier 			ok = mesgsave(m, "stored");
6227dd7cddfSDavid du Colombier 		}else{
6239a747e4fSDavid du Colombier 			ok = mesgsave(m, args[1]);
6249a747e4fSDavid du Colombier 			s = eappend(s, " ", args[1]);
6257dd7cddfSDavid du Colombier 		}
6267dd7cddfSDavid du Colombier 		if(ok){
6279a747e4fSDavid du Colombier 			s = egrow(s, "]", nil);
6289a747e4fSDavid du Colombier 			mesgmenumark(mbox.w, m->name, s);
6297dd7cddfSDavid du Colombier 		}
6309a747e4fSDavid du Colombier 		free(s);
6317dd7cddfSDavid du Colombier 		goto Return;
6327dd7cddfSDavid du Colombier 	}
6339a747e4fSDavid du Colombier 	if(strcmp(args[0], "Reply")==0){
6349a747e4fSDavid du Colombier 		if(nargs>=2 && strcmp(args[1], "all")==0)
635d9306527SDavid du Colombier 			mkreply(m, "Replyall", nil, nil, nil);
6369a747e4fSDavid du Colombier 		else
637d9306527SDavid du Colombier 			mkreply(m, "Reply", nil, nil, nil);
6387dd7cddfSDavid du Colombier 		goto Return;
6397dd7cddfSDavid du Colombier 	}
6409a747e4fSDavid du Colombier 	if(strcmp(args[0], "Q") == 0){
641d9306527SDavid du Colombier 		s = winselection(m->w);	/* will be freed by mkreply */
6429a747e4fSDavid du Colombier 		if(nargs>=3 && strcmp(args[1], "Reply")==0 && strcmp(args[2], "all")==0)
643d9306527SDavid du Colombier 			mkreply(m, "QReplyall", nil, nil, s);
6449a747e4fSDavid du Colombier 		else
645d9306527SDavid du Colombier 			mkreply(m, "QReply", nil, nil, s);
6469a747e4fSDavid du Colombier 		goto Return;
6479a747e4fSDavid du Colombier 	}
6489a747e4fSDavid du Colombier 	if(strcmp(args[0], "Del") == 0){
6497dd7cddfSDavid du Colombier 		if(windel(m->w, 0)){
6507dd7cddfSDavid du Colombier 			chanfree(m->w->cevent);
6517dd7cddfSDavid du Colombier 			free(m->w);
6527dd7cddfSDavid du Colombier 			m->w = nil;
6537dd7cddfSDavid du Colombier 			if(m->isreply)
6547dd7cddfSDavid du Colombier 				delreply(m);
6557dd7cddfSDavid du Colombier 			else{
6567dd7cddfSDavid du Colombier 				m->opened = 0;
6577dd7cddfSDavid du Colombier 				m->tagposted = 0;
6587dd7cddfSDavid du Colombier 			}
6597dd7cddfSDavid du Colombier 			free(cmd);
6607dd7cddfSDavid du Colombier 			threadexits(nil);
6617dd7cddfSDavid du Colombier 		}
6627dd7cddfSDavid du Colombier 		goto Return;
6637dd7cddfSDavid du Colombier 	}
6649a747e4fSDavid du Colombier 	if(strcmp(args[0], "Delmesg") == 0){
6657dd7cddfSDavid du Colombier 		if(!m->isreply){
6667dd7cddfSDavid du Colombier 			mesgmenumarkdel(wbox, &mbox, m, 1);
6677dd7cddfSDavid du Colombier 			free(cmd);	/* mesgcommand might not return */
6687dd7cddfSDavid du Colombier 			mesgcommand(m, estrdup("Del"));
6697dd7cddfSDavid du Colombier 			return 1;
6707dd7cddfSDavid du Colombier 		}
6717dd7cddfSDavid du Colombier 		goto Return;
6727dd7cddfSDavid du Colombier 	}
6739a747e4fSDavid du Colombier 	if(strcmp(args[0], "UnDelmesg") == 0){
6747dd7cddfSDavid du Colombier 		if(!m->isreply && m->deleted)
6757dd7cddfSDavid du Colombier 			mesgmenumarkundel(wbox, &mbox, m);
6767dd7cddfSDavid du Colombier 		goto Return;
6777dd7cddfSDavid du Colombier 	}
6789a747e4fSDavid du Colombier //	if(strcmp(args[0], "Headers") == 0){
6797dd7cddfSDavid du Colombier //		m->showheaders();
6807dd7cddfSDavid du Colombier //		return True;
6817dd7cddfSDavid du Colombier //	}
6827dd7cddfSDavid du Colombier 
6837dd7cddfSDavid du Colombier 	ret = 0;
6847dd7cddfSDavid du Colombier 
6857dd7cddfSDavid du Colombier     Return:
6867dd7cddfSDavid du Colombier 	free(cmd);
6877dd7cddfSDavid du Colombier 	return ret;
6887dd7cddfSDavid du Colombier }
6897dd7cddfSDavid du Colombier 
6907dd7cddfSDavid du Colombier void
mesgtagpost(Message * m)6917dd7cddfSDavid du Colombier mesgtagpost(Message *m)
6927dd7cddfSDavid du Colombier {
6937dd7cddfSDavid du Colombier 	if(m->tagposted)
6947dd7cddfSDavid du Colombier 		return;
6957dd7cddfSDavid du Colombier 	wintagwrite(m->w, " Post", 5);
6967dd7cddfSDavid du Colombier 	m->tagposted = 1;
6977dd7cddfSDavid du Colombier }
6987dd7cddfSDavid du Colombier 
6995d459b5aSDavid du Colombier /* need to expand selection more than default word */
7005d459b5aSDavid du Colombier #pragma varargck argpos eval 2
7015d459b5aSDavid du Colombier 
7025d459b5aSDavid du Colombier long
eval(Window * w,char * s,...)7035d459b5aSDavid du Colombier eval(Window *w, char *s, ...)
7045d459b5aSDavid du Colombier {
7055d459b5aSDavid du Colombier 	char buf[64];
7065d459b5aSDavid du Colombier 	va_list arg;
7075d459b5aSDavid du Colombier 
7085d459b5aSDavid du Colombier 	va_start(arg, s);
7095d459b5aSDavid du Colombier 	vsnprint(buf, sizeof buf, s, arg);
7105d459b5aSDavid du Colombier 	va_end(arg);
7115d459b5aSDavid du Colombier 
7125d459b5aSDavid du Colombier 	if(winsetaddr(w, buf, 1)==0)
7135d459b5aSDavid du Colombier 		return -1;
7145d459b5aSDavid du Colombier 
7155d459b5aSDavid du Colombier 	if(pread(w->addr, buf, 24, 0) != 24)
7165d459b5aSDavid du Colombier 		return -1;
7175d459b5aSDavid du Colombier 	return strtol(buf, 0, 10);
7185d459b5aSDavid du Colombier }
7195d459b5aSDavid du Colombier 
7205d459b5aSDavid du Colombier int
isemail(char * s)7215d459b5aSDavid du Colombier isemail(char *s)
7225d459b5aSDavid du Colombier {
7235d459b5aSDavid du Colombier 	int nat;
7245d459b5aSDavid du Colombier 
7255d459b5aSDavid du Colombier 	nat = 0;
7265d459b5aSDavid du Colombier 	for(; *s; s++)
7275d459b5aSDavid du Colombier 		if(*s == '@')
7285d459b5aSDavid du Colombier 			nat++;
7295d459b5aSDavid du Colombier 		else if(!isalpha(*s) && !isdigit(*s) && !strchr("_.-+/", *s))
7305d459b5aSDavid du Colombier 			return 0;
7315d459b5aSDavid du Colombier 	return nat==1;
7325d459b5aSDavid du Colombier }
7335d459b5aSDavid du Colombier 
7345d459b5aSDavid du Colombier char addrdelim[] =  "/[ \t\\n<>()\\[\\]]/";
7355d459b5aSDavid du Colombier char*
expandaddr(Window * w,Event * e)7365d459b5aSDavid du Colombier expandaddr(Window *w, Event *e)
7375d459b5aSDavid du Colombier {
7385d459b5aSDavid du Colombier 	char *s;
7395d459b5aSDavid du Colombier 	long q0, q1;
7405d459b5aSDavid du Colombier 
7415d459b5aSDavid du Colombier 	if(e->q0 != e->q1)	/* cannot happen */
7425d459b5aSDavid du Colombier 		return nil;
7435d459b5aSDavid du Colombier 
7445d459b5aSDavid du Colombier 	q0 = eval(w, "#%d-%s", e->q0, addrdelim);
7455d459b5aSDavid du Colombier 	if(q0 == -1)	/* bad char not found */
7465d459b5aSDavid du Colombier 		q0 = 0;
7475d459b5aSDavid du Colombier 	else			/* increment past bad char */
7485d459b5aSDavid du Colombier 		q0++;
7495d459b5aSDavid du Colombier 
7505d459b5aSDavid du Colombier 	q1 = eval(w, "#%d+%s", e->q0, addrdelim);
7515d459b5aSDavid du Colombier 	if(q1 < 0){
7525d459b5aSDavid du Colombier 		q1 = eval(w, "$");
7535d459b5aSDavid du Colombier 		if(q1 < 0)
7545d459b5aSDavid du Colombier 			return nil;
7555d459b5aSDavid du Colombier 	}
7565d459b5aSDavid du Colombier 	if(q0 >= q1)
7575d459b5aSDavid du Colombier 		return nil;
7585d459b5aSDavid du Colombier 	s = emalloc((q1-q0)*UTFmax+1);
7595d459b5aSDavid du Colombier 	winread(w, q0, q1, s);
7605d459b5aSDavid du Colombier 	return s;
7615d459b5aSDavid du Colombier }
7625d459b5aSDavid du Colombier 
7635d459b5aSDavid du Colombier int
replytoaddr(Window * w,Message * m,Event * e,char * s)7645d459b5aSDavid du Colombier replytoaddr(Window *w, Message *m, Event *e, char *s)
7655d459b5aSDavid du Colombier {
7665d459b5aSDavid du Colombier 	int did;
7675d459b5aSDavid du Colombier 	char *buf;
7685d459b5aSDavid du Colombier 	Plumbmsg *pm;
7695d459b5aSDavid du Colombier 
7705d459b5aSDavid du Colombier 	buf = nil;
7715d459b5aSDavid du Colombier 	did = 0;
7725d459b5aSDavid du Colombier 	if(e->flag & 2){
7735d459b5aSDavid du Colombier 		/* autoexpanded; use our own bigger expansion */
7745d459b5aSDavid du Colombier 		buf = expandaddr(w, e);
7755d459b5aSDavid du Colombier 		if(buf == nil)
7765d459b5aSDavid du Colombier 			return 0;
7775d459b5aSDavid du Colombier 		s = buf;
7785d459b5aSDavid du Colombier 	}
7795d459b5aSDavid du Colombier 	if(isemail(s)){
7805d459b5aSDavid du Colombier 		did = 1;
7815d459b5aSDavid du Colombier 		pm = emalloc(sizeof(Plumbmsg));
7825d459b5aSDavid du Colombier 		pm->src = estrdup("Mail");
7835d459b5aSDavid du Colombier 		pm->dst = estrdup("sendmail");
7845d459b5aSDavid du Colombier 		pm->data = estrdup(s);
7855d459b5aSDavid du Colombier 		pm->ndata = -1;
7865d459b5aSDavid du Colombier 		if(m->subject && m->subject[0]){
7875d459b5aSDavid du Colombier 			pm->attr = emalloc(sizeof(Plumbattr));
7885d459b5aSDavid du Colombier 			pm->attr->name = estrdup("Subject");
7895d459b5aSDavid du Colombier 			if(tolower(m->subject[0]) != 'r' || tolower(m->subject[1]) != 'e' || m->subject[2] != ':')
7905d459b5aSDavid du Colombier 				pm->attr->value = estrstrdup("Re: ", m->subject);
7915d459b5aSDavid du Colombier 			else
7925d459b5aSDavid du Colombier 				pm->attr->value = estrdup(m->subject);
7935d459b5aSDavid du Colombier 			pm->attr->next = nil;
7945d459b5aSDavid du Colombier 		}
7955d459b5aSDavid du Colombier 		if(plumbsend(plumbsendfd, pm) < 0)
7965d459b5aSDavid du Colombier 			fprint(2, "error writing plumb message: %r\n");
7975d459b5aSDavid du Colombier 		plumbfree(pm);
7985d459b5aSDavid du Colombier 	}
7995d459b5aSDavid du Colombier 	free(buf);
8005d459b5aSDavid du Colombier 	return did;
8015d459b5aSDavid du Colombier }
8025d459b5aSDavid du Colombier 
8035d459b5aSDavid du Colombier 
8047dd7cddfSDavid du Colombier void
mesgctl(void * v)8057dd7cddfSDavid du Colombier mesgctl(void *v)
8067dd7cddfSDavid du Colombier {
8077dd7cddfSDavid du Colombier 	Message *m;
8087dd7cddfSDavid du Colombier 	Window *w;
8097dd7cddfSDavid du Colombier 	Event *e, *eq, *e2, *ea;
8107dd7cddfSDavid du Colombier 	int na, nopen, i, j;
8115d459b5aSDavid du Colombier 	char *os, *s, *t, *buf;
8127dd7cddfSDavid du Colombier 
8137dd7cddfSDavid du Colombier 	m = v;
8147dd7cddfSDavid du Colombier 	w = m->w;
8157dd7cddfSDavid du Colombier 	threadsetname("mesgctl");
8167dd7cddfSDavid du Colombier 	proccreate(wineventproc, w, STACK);
8177dd7cddfSDavid du Colombier 	for(;;){
8187dd7cddfSDavid du Colombier 		e = recvp(w->cevent);
8197dd7cddfSDavid du Colombier 		switch(e->c1){
8207dd7cddfSDavid du Colombier 		default:
8217dd7cddfSDavid du Colombier 		Unk:
8227dd7cddfSDavid du Colombier 			print("unknown message %c%c\n", e->c1, e->c2);
8237dd7cddfSDavid du Colombier 			break;
8247dd7cddfSDavid du Colombier 
8257dd7cddfSDavid du Colombier 		case 'E':	/* write to body; can't affect us */
8267dd7cddfSDavid du Colombier 			break;
8277dd7cddfSDavid du Colombier 
8287dd7cddfSDavid du Colombier 		case 'F':	/* generated by our actions; ignore */
8297dd7cddfSDavid du Colombier 			break;
8307dd7cddfSDavid du Colombier 
8317dd7cddfSDavid du Colombier 		case 'K':	/* type away; we don't care */
8327dd7cddfSDavid du Colombier 		case 'M':
8337dd7cddfSDavid du Colombier 			switch(e->c2){
8347dd7cddfSDavid du Colombier 			case 'x':	/* mouse only */
8357dd7cddfSDavid du Colombier 			case 'X':
8367dd7cddfSDavid du Colombier 				ea = nil;
8377dd7cddfSDavid du Colombier 				eq = e;
8387dd7cddfSDavid du Colombier 				if(e->flag & 2){
8397dd7cddfSDavid du Colombier 					e2 = recvp(w->cevent);
8407dd7cddfSDavid du Colombier 					eq = e2;
8417dd7cddfSDavid du Colombier 				}
8427dd7cddfSDavid du Colombier 				if(e->flag & 8){
8437dd7cddfSDavid du Colombier 					ea = recvp(w->cevent);
8447dd7cddfSDavid du Colombier 					recvp(w->cevent);
8457dd7cddfSDavid du Colombier 					na = ea->nb;
8467dd7cddfSDavid du Colombier 				}else
8477dd7cddfSDavid du Colombier 					na = 0;
8487dd7cddfSDavid du Colombier 				if(eq->q1>eq->q0 && eq->nb==0){
8497dd7cddfSDavid du Colombier 					s = emalloc((eq->q1-eq->q0)*UTFmax+1);
8507dd7cddfSDavid du Colombier 					winread(w, eq->q0, eq->q1, s);
8517dd7cddfSDavid du Colombier 				}else
8527dd7cddfSDavid du Colombier 					s = estrdup(eq->b);
8537dd7cddfSDavid du Colombier 				if(na){
8547dd7cddfSDavid du Colombier 					t = emalloc(strlen(s)+1+na+1);
8557dd7cddfSDavid du Colombier 					sprint(t, "%s %s", s, ea->b);
8567dd7cddfSDavid du Colombier 					free(s);
8577dd7cddfSDavid du Colombier 					s = t;
8587dd7cddfSDavid du Colombier 				}
8597dd7cddfSDavid du Colombier 				if(!mesgcommand(m, s))	/* send it back */
8607dd7cddfSDavid du Colombier 					winwriteevent(w, e);
8617dd7cddfSDavid du Colombier 				break;
8627dd7cddfSDavid du Colombier 
8637dd7cddfSDavid du Colombier 			case 'l':	/* mouse only */
8647dd7cddfSDavid du Colombier 			case 'L':
8657dd7cddfSDavid du Colombier 				buf = nil;
8667dd7cddfSDavid du Colombier 				eq = e;
8677dd7cddfSDavid du Colombier 				if(e->flag & 2){
8687dd7cddfSDavid du Colombier 					e2 = recvp(w->cevent);
8697dd7cddfSDavid du Colombier 					eq = e2;
8707dd7cddfSDavid du Colombier 				}
8717dd7cddfSDavid du Colombier 				s = eq->b;
8727dd7cddfSDavid du Colombier 				if(eq->q1>eq->q0 && eq->nb==0){
8737dd7cddfSDavid du Colombier 					buf = emalloc((eq->q1-eq->q0)*UTFmax+1);
8747dd7cddfSDavid du Colombier 					winread(w, eq->q0, eq->q1, buf);
8757dd7cddfSDavid du Colombier 					s = buf;
8767dd7cddfSDavid du Colombier 				}
8775d459b5aSDavid du Colombier 				os = s;
8787dd7cddfSDavid du Colombier 				nopen = 0;
8797dd7cddfSDavid du Colombier 				do{
8807dd7cddfSDavid du Colombier 					/* skip mail box name if present */
8817dd7cddfSDavid du Colombier 					if(strncmp(s, mbox.name, strlen(mbox.name)) == 0)
8827dd7cddfSDavid du Colombier 						s += strlen(mbox.name);
8837dd7cddfSDavid du Colombier 					if(strstr(s, "body") != nil){
8847dd7cddfSDavid du Colombier 						/* strip any known extensions */
8857dd7cddfSDavid du Colombier 						for(i=0; exts[i].ext!=nil; i++){
8867dd7cddfSDavid du Colombier 							j = strlen(exts[i].ext);
8877dd7cddfSDavid du Colombier 							if(strlen(s)>j && strcmp(s+strlen(s)-j, exts[i].ext)==0){
8887dd7cddfSDavid du Colombier 								s[strlen(s)-j] = '\0';
8897dd7cddfSDavid du Colombier 								break;
8907dd7cddfSDavid du Colombier 							}
8917dd7cddfSDavid du Colombier 						}
8927dd7cddfSDavid du Colombier 						if(strlen(s)>5 && strcmp(s+strlen(s)-5, "/body")==0)
8937dd7cddfSDavid du Colombier 							s[strlen(s)-4] = '\0';	/* leave / in place */
8947dd7cddfSDavid du Colombier 					}
8957dd7cddfSDavid du Colombier 					nopen += mesgopen(&mbox, mbox.name, s, m, 0, nil);
8967dd7cddfSDavid du Colombier 					while(*s!=0 && *s++!='\n')
8977dd7cddfSDavid du Colombier 						;
8987dd7cddfSDavid du Colombier 				}while(*s);
8993ff48bf5SDavid du Colombier 				if(nopen == 0 && e->c1 == 'L')
9005d459b5aSDavid du Colombier 					nopen += replytoaddr(w, m, e, os);
9015d459b5aSDavid du Colombier 				if(nopen == 0)
9027dd7cddfSDavid du Colombier 					winwriteevent(w, e);
9037dd7cddfSDavid du Colombier 				free(buf);
9047dd7cddfSDavid du Colombier 				break;
9057dd7cddfSDavid du Colombier 
9067dd7cddfSDavid du Colombier 			case 'I':	/* modify away; we don't care */
9077dd7cddfSDavid du Colombier 			case 'D':
9087dd7cddfSDavid du Colombier 				mesgtagpost(m);
9097dd7cddfSDavid du Colombier 				/* fall through */
9107dd7cddfSDavid du Colombier 			case 'd':
9117dd7cddfSDavid du Colombier 			case 'i':
9127dd7cddfSDavid du Colombier 				break;
9137dd7cddfSDavid du Colombier 
9147dd7cddfSDavid du Colombier 			default:
9157dd7cddfSDavid du Colombier 				goto Unk;
9167dd7cddfSDavid du Colombier 			}
9177dd7cddfSDavid du Colombier 		}
9187dd7cddfSDavid du Colombier 	}
9197dd7cddfSDavid du Colombier }
9207dd7cddfSDavid du Colombier 
9217dd7cddfSDavid du Colombier void
mesgline(Message * m,char * header,char * value)9227dd7cddfSDavid du Colombier mesgline(Message *m, char *header, char *value)
9237dd7cddfSDavid du Colombier {
9247dd7cddfSDavid du Colombier 	if(strlen(value) > 0)
9257dd7cddfSDavid du Colombier 		Bprint(m->w->body, "%s: %s\n", header, value);
9267dd7cddfSDavid du Colombier }
9277dd7cddfSDavid du Colombier 
9287dd7cddfSDavid du Colombier int
isprintable(char * type)9297dd7cddfSDavid du Colombier isprintable(char *type)
9307dd7cddfSDavid du Colombier {
9317dd7cddfSDavid du Colombier 	int i;
9327dd7cddfSDavid du Colombier 
9337dd7cddfSDavid du Colombier 	for(i=0; goodtypes[i]!=nil; i++)
9347dd7cddfSDavid du Colombier 		if(strcmp(type, goodtypes[i])==0)
9357dd7cddfSDavid du Colombier 			return 1;
9367dd7cddfSDavid du Colombier 	return 0;
9377dd7cddfSDavid du Colombier }
9387dd7cddfSDavid du Colombier 
9397dd7cddfSDavid du Colombier char*
ext(char * type)9407dd7cddfSDavid du Colombier ext(char *type)
9417dd7cddfSDavid du Colombier {
9427dd7cddfSDavid du Colombier 	int i;
9437dd7cddfSDavid du Colombier 
9447dd7cddfSDavid du Colombier 	for(i=0; exts[i].type!=nil; i++)
9457dd7cddfSDavid du Colombier 		if(strcmp(type, exts[i].type)==0)
9467dd7cddfSDavid du Colombier 			return exts[i].ext;
9477dd7cddfSDavid du Colombier 	return "";
9487dd7cddfSDavid du Colombier }
9497dd7cddfSDavid du Colombier 
9507dd7cddfSDavid du Colombier void
mimedisplay(Message * m,char * name,char * rootdir,Window * w,int fileonly)9517dd7cddfSDavid du Colombier mimedisplay(Message *m, char *name, char *rootdir, Window *w, int fileonly)
9527dd7cddfSDavid du Colombier {
953671dfc47SDavid du Colombier 	char *dest, *maildest;
9547dd7cddfSDavid du Colombier 
9557dd7cddfSDavid du Colombier 	if(strcmp(m->disposition, "file")==0 || strlen(m->filename)!=0){
9567dd7cddfSDavid du Colombier 		if(strlen(m->filename) == 0){
9577dd7cddfSDavid du Colombier 			dest = estrdup(m->name);
9587dd7cddfSDavid du Colombier 			dest[strlen(dest)-1] = '\0';
9597dd7cddfSDavid du Colombier 		}else
9607dd7cddfSDavid du Colombier 			dest = estrdup(m->filename);
961671dfc47SDavid du Colombier 		if(maildest = getenv("maildest")){
962671dfc47SDavid du Colombier 			maildest = eappend(maildest, "/", dest);
963671dfc47SDavid du Colombier 			Bprint(w->body, "\tcp %s%sbody%s %q\n", rootdir, name, ext(m->type), maildest);
964671dfc47SDavid du Colombier 			free(maildest);
965671dfc47SDavid du Colombier 		}
966671dfc47SDavid du Colombier 		if(m->filename[0] != '/'){
9677dd7cddfSDavid du Colombier 			dest = egrow(estrdup(home), "/", dest);
968671dfc47SDavid du Colombier 		}
969d9306527SDavid du Colombier 		Bprint(w->body, "\tcp %s%sbody%s %q\n", rootdir, name, ext(m->type), dest);
9707dd7cddfSDavid du Colombier 		free(dest);
9717dd7cddfSDavid du Colombier 	}else if(!fileonly)
9727dd7cddfSDavid du Colombier 		Bprint(w->body, "\tfile is %s%sbody%s\n", rootdir, name, ext(m->type));
9737dd7cddfSDavid du Colombier }
9747dd7cddfSDavid du Colombier 
9757dd7cddfSDavid du Colombier void
printheader(char * dir,Biobuf * b,char ** okheaders)976c27b0bc9SDavid du Colombier printheader(char *dir, Biobuf *b, char **okheaders)
9779a747e4fSDavid du Colombier {
9789a747e4fSDavid du Colombier 	char *s;
9799a747e4fSDavid du Colombier 	char *lines[100];
9809a747e4fSDavid du Colombier 	int i, j, n;
9819a747e4fSDavid du Colombier 
9829a747e4fSDavid du Colombier 	s = readfile(dir, "header", nil);
9839a747e4fSDavid du Colombier 	if(s == nil)
9849a747e4fSDavid du Colombier 		return;
9859a747e4fSDavid du Colombier 	n = getfields(s, lines, nelem(lines), 0, "\n");
9869a747e4fSDavid du Colombier 	for(i=0; i<n; i++)
9879a747e4fSDavid du Colombier 		for(j=0; okheaders[j]; j++)
9889a747e4fSDavid du Colombier 			if(cistrncmp(lines[i], okheaders[j], strlen(okheaders[j])) == 0)
9899a747e4fSDavid du Colombier 				Bprint(b, "%s\n", lines[i]);
9909a747e4fSDavid du Colombier 	free(s);
9919a747e4fSDavid du Colombier }
9929a747e4fSDavid du Colombier 
993*8ccc32efSDavid du Colombier /*
994*8ccc32efSDavid du Colombier  * find the best alternative part.
995*8ccc32efSDavid du Colombier  *
996*8ccc32efSDavid du Colombier  * turkeys have started emitting empty text/plain parts,
997*8ccc32efSDavid du Colombier  * with the actual content in a text/html part, which complicates the choice.
998*8ccc32efSDavid du Colombier  *
999*8ccc32efSDavid du Colombier  * bigger turkeys emit a tiny base64-encoded text/plain part,
1000*8ccc32efSDavid du Colombier  * a small base64-encoded text/html part, and the real content is in
1001*8ccc32efSDavid du Colombier  * a text/calendar part.
1002*8ccc32efSDavid du Colombier  *
1003*8ccc32efSDavid du Colombier  * the magic lengths are empirically derived.
1004*8ccc32efSDavid du Colombier  * as turkeys devolve and mutate, this will only get worse.
1005*8ccc32efSDavid du Colombier  */
1006*8ccc32efSDavid du Colombier static Message*
bestalt(Message * m,char * dir)1007*8ccc32efSDavid du Colombier bestalt(Message *m, char *dir)
1008*8ccc32efSDavid du Colombier {
1009*8ccc32efSDavid du Colombier 	int len;
1010*8ccc32efSDavid du Colombier 	char *subdir;
1011*8ccc32efSDavid du Colombier 	Message *nm;
1012*8ccc32efSDavid du Colombier 	Message *realplain, *realhtml, *realcal;
1013*8ccc32efSDavid du Colombier 
1014*8ccc32efSDavid du Colombier 	realplain = realhtml = realcal = nil;
1015*8ccc32efSDavid du Colombier 	for(nm = m->head; nm != nil; nm = nm->next){
1016*8ccc32efSDavid du Colombier 		subdir = estrstrdup(dir, nm->name);
1017*8ccc32efSDavid du Colombier 		len = 0;
1018*8ccc32efSDavid du Colombier 		free(readbody(nm->type, subdir, &len));
1019*8ccc32efSDavid du Colombier 		free(subdir);
1020*8ccc32efSDavid du Colombier 		if(strcmp(nm->type, "text/plain") == 0 && len >= 8)
1021*8ccc32efSDavid du Colombier 			realplain = nm;
1022*8ccc32efSDavid du Colombier 		else if(strcmp(nm->type, "text/html") == 0 && len >= 600)
1023*8ccc32efSDavid du Colombier 			realhtml = nm;
1024*8ccc32efSDavid du Colombier 		else if(strcmp(nm->type, "text/calendar") == 0)
1025*8ccc32efSDavid du Colombier 			realcal = nm;
1026*8ccc32efSDavid du Colombier 	}
1027*8ccc32efSDavid du Colombier 	if(realplain == nil && realhtml == nil && realcal)
1028*8ccc32efSDavid du Colombier 		return realcal;			/* super-turkey */
1029*8ccc32efSDavid du Colombier 	else if(realplain == nil && realhtml)
1030*8ccc32efSDavid du Colombier 		return realhtml;		/* regular turkey */
1031*8ccc32efSDavid du Colombier 	else
1032*8ccc32efSDavid du Colombier 		return realplain;
1033*8ccc32efSDavid du Colombier }
1034*8ccc32efSDavid du Colombier 
10359a747e4fSDavid du Colombier void
mesgload(Message * m,char * rootdir,char * file,Window * w)10367dd7cddfSDavid du Colombier mesgload(Message *m, char *rootdir, char *file, Window *w)
10377dd7cddfSDavid du Colombier {
10387dd7cddfSDavid du Colombier 	char *s, *subdir, *name, *dir;
10397dd7cddfSDavid du Colombier 	Message *mp, *thisone;
10407dd7cddfSDavid du Colombier 	int n;
10417dd7cddfSDavid du Colombier 
10427dd7cddfSDavid du Colombier 	dir = estrstrdup(rootdir, file);
10439a747e4fSDavid du Colombier 
10449a747e4fSDavid du Colombier 	if(strcmp(m->type, "message/rfc822") != 0){	/* suppress headers of envelopes */
10459a747e4fSDavid du Colombier 		if(strlen(m->from) > 0){
10467dd7cddfSDavid du Colombier 			Bprint(w->body, "From: %s\n", m->from);
10477dd7cddfSDavid du Colombier 			mesgline(m, "Date", m->date);
10487dd7cddfSDavid du Colombier 			mesgline(m, "To", m->to);
10497dd7cddfSDavid du Colombier 			mesgline(m, "CC", m->cc);
10507dd7cddfSDavid du Colombier 			mesgline(m, "Subject", m->subject);
1051c27b0bc9SDavid du Colombier 			printheader(dir, w->body, extraheaders);
1052c27b0bc9SDavid du Colombier 		}else{
1053c27b0bc9SDavid du Colombier 			printheader(dir, w->body, okheaders);
1054c27b0bc9SDavid du Colombier 			printheader(dir, w->body, extraheaders);
1055c27b0bc9SDavid du Colombier 		}
10567dd7cddfSDavid du Colombier 		Bprint(w->body, "\n");
10579a747e4fSDavid du Colombier 	}
10587dd7cddfSDavid du Colombier 
10599a747e4fSDavid du Colombier 	if(m->level == 1 && m->recursed == 0){
10609a747e4fSDavid du Colombier 		m->recursed = 1;
10619a747e4fSDavid du Colombier 		readmbox(m, rootdir, m->name);
10629a747e4fSDavid du Colombier 	}
10637dd7cddfSDavid du Colombier 	if(m->head == nil){	/* single part message */
10647dd7cddfSDavid du Colombier 		if(strcmp(m->type, "text")==0 || strncmp(m->type, "text/", 5)==0){
10657dd7cddfSDavid du Colombier 			mimedisplay(m, m->name, rootdir, w, 1);
10669a747e4fSDavid du Colombier 			s = readbody(m->type, dir, &n);
10677dd7cddfSDavid du Colombier 			winwritebody(w, s, n);
10687dd7cddfSDavid du Colombier 			free(s);
10697dd7cddfSDavid du Colombier 		}else
10707dd7cddfSDavid du Colombier 			mimedisplay(m, m->name, rootdir, w, 0);
10719a747e4fSDavid du Colombier 	}else{
10729a747e4fSDavid du Colombier 		/* multi-part message, either multipart/* or message/rfc822 */
10737dd7cddfSDavid du Colombier 		thisone = nil;
10747dd7cddfSDavid du Colombier 		if(strcmp(m->type, "multipart/alternative") == 0){
1075*8ccc32efSDavid du Colombier 			thisone = bestalt(m, dir);
1076*8ccc32efSDavid du Colombier 			if(thisone == nil){
10777dd7cddfSDavid du Colombier 				thisone = m->head; /* in case we can't find a good one */
10787dd7cddfSDavid du Colombier 				for(mp=m->head; mp!=nil; mp=mp->next)
10797dd7cddfSDavid du Colombier 					if(isprintable(mp->type)){
10807dd7cddfSDavid du Colombier 						thisone = mp;
10817dd7cddfSDavid du Colombier 						break;
10827dd7cddfSDavid du Colombier 					}
10837dd7cddfSDavid du Colombier 			}
1084*8ccc32efSDavid du Colombier 		}
10857dd7cddfSDavid du Colombier 		for(mp=m->head; mp!=nil; mp=mp->next){
10867dd7cddfSDavid du Colombier 			if(thisone!=nil && mp!=thisone)
10877dd7cddfSDavid du Colombier 				continue;
10887dd7cddfSDavid du Colombier 			subdir = estrstrdup(dir, mp->name);
10897dd7cddfSDavid du Colombier 			name = estrstrdup(file, mp->name);
10907dd7cddfSDavid du Colombier 			/* skip first element in name because it's already in window name */
10917dd7cddfSDavid du Colombier 			if(mp != m->head)
1092*8ccc32efSDavid du Colombier 				Bprint(w->body, "\n===> %s (%s) [%s]\n",
1093*8ccc32efSDavid du Colombier 					strchr(name, '/')+1, mp->type,
1094*8ccc32efSDavid du Colombier 					mp->disposition);
1095*8ccc32efSDavid du Colombier 			if(strcmp(mp->type, "text")==0 ||
1096*8ccc32efSDavid du Colombier 			    strncmp(mp->type, "text/", 5)==0){
10977dd7cddfSDavid du Colombier 				mimedisplay(mp, name, rootdir, w, 1);
1098c27b0bc9SDavid du Colombier 				printheader(subdir, w->body, okheaders);
1099c27b0bc9SDavid du Colombier 				printheader(subdir, w->body, extraheaders);
11007dd7cddfSDavid du Colombier 				winwritebody(w, "\n", 1);
11019a747e4fSDavid du Colombier 				s = readbody(mp->type, subdir, &n);
11027dd7cddfSDavid du Colombier 				winwritebody(w, s, n);
11037dd7cddfSDavid du Colombier 				free(s);
11047dd7cddfSDavid du Colombier 			}else{
1105*8ccc32efSDavid du Colombier 				if(strncmp(mp->type, "multipart/", 10)==0 ||
1106*8ccc32efSDavid du Colombier 				    strcmp(mp->type, "message/rfc822")==0){
11077dd7cddfSDavid du Colombier 					mp->w = w;
11087dd7cddfSDavid du Colombier 					mesgload(mp, rootdir, name, w);
11097dd7cddfSDavid du Colombier 					mp->w = nil;
11107dd7cddfSDavid du Colombier 				}else
11117dd7cddfSDavid du Colombier 					mimedisplay(mp, name, rootdir, w, 0);
11127dd7cddfSDavid du Colombier 			}
11137dd7cddfSDavid du Colombier 			free(name);
11147dd7cddfSDavid du Colombier 			free(subdir);
11157dd7cddfSDavid du Colombier 		}
11167dd7cddfSDavid du Colombier 	}
11177dd7cddfSDavid du Colombier 	free(dir);
11187dd7cddfSDavid du Colombier }
11197dd7cddfSDavid du Colombier 
11207dd7cddfSDavid du Colombier int
tokenizec(char * str,char ** args,int max,char * splitc)11217dd7cddfSDavid du Colombier tokenizec(char *str, char **args, int max, char *splitc)
11227dd7cddfSDavid du Colombier {
11237dd7cddfSDavid du Colombier 	int na;
11247dd7cddfSDavid du Colombier 	int intok = 0;
11257dd7cddfSDavid du Colombier 
11267dd7cddfSDavid du Colombier 	if(max <= 0)
11277dd7cddfSDavid du Colombier 		return 0;
11287dd7cddfSDavid du Colombier 	for(na=0; *str != '\0';str++){
11297dd7cddfSDavid du Colombier 		if(strchr(splitc, *str) == nil){
11307dd7cddfSDavid du Colombier 			if(intok)
11317dd7cddfSDavid du Colombier 				continue;
11327dd7cddfSDavid du Colombier 			args[na++] = str;
11337dd7cddfSDavid du Colombier 			intok = 1;
11347dd7cddfSDavid du Colombier 		}else{
11357dd7cddfSDavid du Colombier 			/* it's a separator/skip character */
11367dd7cddfSDavid du Colombier 			*str = '\0';
11377dd7cddfSDavid du Colombier 			if(intok){
11387dd7cddfSDavid du Colombier 				intok = 0;
11397dd7cddfSDavid du Colombier 				if(na >= max)
11407dd7cddfSDavid du Colombier 					break;
11417dd7cddfSDavid du Colombier 			}
11427dd7cddfSDavid du Colombier 		}
11437dd7cddfSDavid du Colombier 	}
11447dd7cddfSDavid du Colombier 	return na;
11457dd7cddfSDavid du Colombier }
11467dd7cddfSDavid du Colombier 
11477dd7cddfSDavid du Colombier Message*
mesglookup(Message * mbox,char * name,char * digest)11487dd7cddfSDavid du Colombier mesglookup(Message *mbox, char *name, char *digest)
11497dd7cddfSDavid du Colombier {
11507dd7cddfSDavid du Colombier 	int n;
11517dd7cddfSDavid du Colombier 	Message *m;
11527dd7cddfSDavid du Colombier 	char *t;
11537dd7cddfSDavid du Colombier 
11547dd7cddfSDavid du Colombier 	if(digest){
11557dd7cddfSDavid du Colombier 		/* can find exactly */
11567dd7cddfSDavid du Colombier 		for(m=mbox->head; m!=nil; m=m->next)
11577dd7cddfSDavid du Colombier 			if(strcmp(digest, m->digest) == 0)
11587dd7cddfSDavid du Colombier 				break;
11597dd7cddfSDavid du Colombier 		return m;
11607dd7cddfSDavid du Colombier 	}
11617dd7cddfSDavid du Colombier 
11627dd7cddfSDavid du Colombier 	n = strlen(name);
11637dd7cddfSDavid du Colombier 	if(n == 0)
11647dd7cddfSDavid du Colombier 		return nil;
11657dd7cddfSDavid du Colombier 	if(name[n-1] == '/')
11667dd7cddfSDavid du Colombier 		t = estrdup(name);
11677dd7cddfSDavid du Colombier 	else
11687dd7cddfSDavid du Colombier 		t = estrstrdup(name, "/");
11697dd7cddfSDavid du Colombier 	for(m=mbox->head; m!=nil; m=m->next)
11707dd7cddfSDavid du Colombier 		if(strcmp(t, m->name) == 0)
11717dd7cddfSDavid du Colombier 			break;
11727dd7cddfSDavid du Colombier 	free(t);
11737dd7cddfSDavid du Colombier 	return m;
11747dd7cddfSDavid du Colombier }
11757dd7cddfSDavid du Colombier 
11769a747e4fSDavid du Colombier /*
11779a747e4fSDavid du Colombier  * Find plumb port, knowing type is text, given file name (by extension)
11789a747e4fSDavid du Colombier  */
11797dd7cddfSDavid du Colombier int
plumbportbysuffix(char * file)11809a747e4fSDavid du Colombier plumbportbysuffix(char *file)
11819a747e4fSDavid du Colombier {
11829a747e4fSDavid du Colombier 	char *suf;
11839a747e4fSDavid du Colombier 	int i, nsuf, nfile;
11849a747e4fSDavid du Colombier 
11859a747e4fSDavid du Colombier 	nfile = strlen(file);
11869a747e4fSDavid du Colombier 	for(i=0; ports[i].type!=nil; i++){
11879a747e4fSDavid du Colombier 		suf = ports[i].suffix;
11889a747e4fSDavid du Colombier 		nsuf = strlen(suf);
11899a747e4fSDavid du Colombier 		if(nfile > nsuf)
11909a747e4fSDavid du Colombier 			if(cistrncmp(file+nfile-nsuf, suf, nsuf) == 0)
11919a747e4fSDavid du Colombier 				return i;
11929a747e4fSDavid du Colombier 	}
11939a747e4fSDavid du Colombier 	return 0;
11949a747e4fSDavid du Colombier }
11959a747e4fSDavid du Colombier 
11969a747e4fSDavid du Colombier /*
11979a747e4fSDavid du Colombier  * Find plumb port using type and file name (by extension)
11989a747e4fSDavid du Colombier  */
11999a747e4fSDavid du Colombier int
plumbport(char * type,char * file)12009a747e4fSDavid du Colombier plumbport(char *type, char *file)
12017dd7cddfSDavid du Colombier {
12027dd7cddfSDavid du Colombier 	int i;
12037dd7cddfSDavid du Colombier 
12047dd7cddfSDavid du Colombier 	for(i=0; ports[i].type!=nil; i++)
12059a747e4fSDavid du Colombier 		if(strncmp(type, ports[i].type, strlen(ports[i].type)) == 0)
12067dd7cddfSDavid du Colombier 			return i;
12077dd7cddfSDavid du Colombier 	/* see if it's a text type */
12087dd7cddfSDavid du Colombier 	for(i=0; goodtypes[i]!=nil; i++)
12099a747e4fSDavid du Colombier 		if(strncmp(type, goodtypes[i], strlen(goodtypes[i])) == 0)
12109a747e4fSDavid du Colombier 			return plumbportbysuffix(file);
12117dd7cddfSDavid du Colombier 	return -1;
12127dd7cddfSDavid du Colombier }
12137dd7cddfSDavid du Colombier 
12147dd7cddfSDavid du Colombier void
plumb(Message * m,char * dir)12157dd7cddfSDavid du Colombier plumb(Message *m, char *dir)
12167dd7cddfSDavid du Colombier {
12177dd7cddfSDavid du Colombier 	int i;
12187dd7cddfSDavid du Colombier 	char *port;
12197dd7cddfSDavid du Colombier 	Plumbmsg *pm;
12207dd7cddfSDavid du Colombier 
12217dd7cddfSDavid du Colombier 	if(strlen(m->type) == 0)
12227dd7cddfSDavid du Colombier 		return;
12239a747e4fSDavid du Colombier 	i = plumbport(m->type, m->filename);
12247dd7cddfSDavid du Colombier 	if(i < 0)
12259a747e4fSDavid du Colombier 		fprint(2, "can't find destination for message subpart\n");
12267dd7cddfSDavid du Colombier 	else{
12277dd7cddfSDavid du Colombier 		port = ports[i].port;
12287dd7cddfSDavid du Colombier 		pm = emalloc(sizeof(Plumbmsg));
12297dd7cddfSDavid du Colombier 		pm->src = estrdup("Mail");
12307dd7cddfSDavid du Colombier 		if(port)
12317dd7cddfSDavid du Colombier 			pm->dst = estrdup(port);
12327dd7cddfSDavid du Colombier 		else
12337dd7cddfSDavid du Colombier 			pm->dst = nil;
12347dd7cddfSDavid du Colombier 		pm->wdir = nil;
12357dd7cddfSDavid du Colombier 		pm->type = estrdup("text");
12367dd7cddfSDavid du Colombier 		pm->ndata = -1;
12377dd7cddfSDavid du Colombier 		pm->data = estrstrdup(dir, "body");
12389a747e4fSDavid du Colombier 		pm->data = eappend(pm->data, "", ports[i].suffix);
12397dd7cddfSDavid du Colombier 		if(plumbsend(plumbsendfd, pm) < 0)
12409a747e4fSDavid du Colombier 			fprint(2, "error writing plumb message: %r\n");
12417dd7cddfSDavid du Colombier 		plumbfree(pm);
12427dd7cddfSDavid du Colombier 	}
12437dd7cddfSDavid du Colombier }
12447dd7cddfSDavid du Colombier 
12457dd7cddfSDavid du Colombier int
mesgopen(Message * mbox,char * dir,char * s,Message * mesg,int plumbed,char * digest)12467dd7cddfSDavid du Colombier mesgopen(Message *mbox, char *dir, char *s, Message *mesg, int plumbed, char *digest)
12477dd7cddfSDavid du Colombier {
12487dd7cddfSDavid du Colombier 	char *t, *u, *v;
12497dd7cddfSDavid du Colombier 	Message *m;
12507dd7cddfSDavid du Colombier 	char *direlem[10];
12519a747e4fSDavid du Colombier 	int i, ndirelem, reuse;
12527dd7cddfSDavid du Colombier 
12537dd7cddfSDavid du Colombier 	/* find white-space-delimited first word */
12547dd7cddfSDavid du Colombier 	for(t=s; *t!='\0' && !isspace(*t); t++)
12557dd7cddfSDavid du Colombier 		;
12567dd7cddfSDavid du Colombier 	u = emalloc(t-s+1);
12577dd7cddfSDavid du Colombier 	memmove(u, s, t-s);
12587dd7cddfSDavid du Colombier 	/* separate it on slashes */
12597dd7cddfSDavid du Colombier 	ndirelem = tokenizec(u, direlem, nelem(direlem), "/");
12607dd7cddfSDavid du Colombier 	if(ndirelem <= 0){
12617dd7cddfSDavid du Colombier     Error:
12627dd7cddfSDavid du Colombier 		free(u);
12637dd7cddfSDavid du Colombier 		return 0;
12647dd7cddfSDavid du Colombier 	}
12657dd7cddfSDavid du Colombier 	if(plumbed){
12667dd7cddfSDavid du Colombier 		write(wctlfd, "top", 3);
12677dd7cddfSDavid du Colombier 		write(wctlfd, "current", 7);
12687dd7cddfSDavid du Colombier 	}
12697dd7cddfSDavid du Colombier 	/* open window for message */
12707dd7cddfSDavid du Colombier 	m = mesglookup(mbox, direlem[0], digest);
12717dd7cddfSDavid du Colombier 	if(m == nil)
12727dd7cddfSDavid du Colombier 		goto Error;
12737dd7cddfSDavid du Colombier 	if(mesg!=nil && m!=mesg)	/* string looked like subpart but isn't part of this message */
12747dd7cddfSDavid du Colombier 		goto Error;
12757dd7cddfSDavid du Colombier 	if(m->opened == 0){
12769a747e4fSDavid du Colombier 		if(m->w == nil){
12779a747e4fSDavid du Colombier 			reuse = 0;
12787dd7cddfSDavid du Colombier 			m->w = newwindow();
12799a747e4fSDavid du Colombier 		}else{
12809a747e4fSDavid du Colombier 			reuse = 1;
12819a747e4fSDavid du Colombier 			/* re-use existing window */
12829a747e4fSDavid du Colombier 			if(winsetaddr(m->w, "0,$", 1)){
12839a747e4fSDavid du Colombier 				if(m->w->data < 0)
12849a747e4fSDavid du Colombier 					m->w->data = winopenfile(m->w, "data");
12859a747e4fSDavid du Colombier 				write(m->w->data, "", 0);
12869a747e4fSDavid du Colombier 			}
12879a747e4fSDavid du Colombier 		}
12887dd7cddfSDavid du Colombier 		v = estrstrdup(mbox->name, m->name);
12897dd7cddfSDavid du Colombier 		winname(m->w, v);
12907dd7cddfSDavid du Colombier 		free(v);
12919a747e4fSDavid du Colombier 		if(!reuse){
12927dd7cddfSDavid du Colombier 			if(m->deleted)
12939a747e4fSDavid du Colombier 				wintagwrite(m->w, "Q Reply all UnDelmesg Save ", 2+6+4+10+5);
12947dd7cddfSDavid du Colombier 			else
12959a747e4fSDavid du Colombier 				wintagwrite(m->w, "Q Reply all Delmesg Save ", 2+6+4+8+5);
12969a747e4fSDavid du Colombier 		}
12977dd7cddfSDavid du Colombier 		threadcreate(mesgctl, m, STACK);
12987dd7cddfSDavid du Colombier 		winopenbody(m->w, OWRITE);
12997dd7cddfSDavid du Colombier 		mesgload(m, dir, m->name, m->w);
13007dd7cddfSDavid du Colombier 		winclosebody(m->w);
13017dd7cddfSDavid du Colombier 		winclean(m->w);
13027dd7cddfSDavid du Colombier 		m->opened = 1;
13037dd7cddfSDavid du Colombier 		if(ndirelem == 1){
13047dd7cddfSDavid du Colombier 			free(u);
13057dd7cddfSDavid du Colombier 			return 1;
13067dd7cddfSDavid du Colombier 		}
13077dd7cddfSDavid du Colombier 	}
13089a747e4fSDavid du Colombier 	if(ndirelem == 1 && plumbport(m->type, m->filename) <= 0){
13097dd7cddfSDavid du Colombier 		/* make sure dot is visible */
13107dd7cddfSDavid du Colombier 		ctlprint(m->w->ctl, "show\n");
13117dd7cddfSDavid du Colombier 		return 0;
13127dd7cddfSDavid du Colombier 	}
13137dd7cddfSDavid du Colombier 	/* walk to subpart */
13147dd7cddfSDavid du Colombier 	dir = estrstrdup(dir, m->name);
13157dd7cddfSDavid du Colombier 	for(i=1; i<ndirelem; i++){
13167dd7cddfSDavid du Colombier 		m = mesglookup(m, direlem[i], digest);
13177dd7cddfSDavid du Colombier 		if(m == nil)
13187dd7cddfSDavid du Colombier 			break;
13197dd7cddfSDavid du Colombier 		dir = egrow(dir, m->name, nil);
13207dd7cddfSDavid du Colombier 	}
13219a747e4fSDavid du Colombier 	if(m != nil && plumbport(m->type, m->filename) > 0)
13227dd7cddfSDavid du Colombier 		plumb(m, dir);
13237dd7cddfSDavid du Colombier 	free(dir);
13247dd7cddfSDavid du Colombier 	free(u);
13257dd7cddfSDavid du Colombier 	return 1;
13267dd7cddfSDavid du Colombier }
13277dd7cddfSDavid du Colombier 
13287dd7cddfSDavid du Colombier void
rewritembox(Window * w,Message * mbox)13297dd7cddfSDavid du Colombier rewritembox(Window *w, Message *mbox)
13307dd7cddfSDavid du Colombier {
13317dd7cddfSDavid du Colombier 	Message *m, *next;
13327dd7cddfSDavid du Colombier 	char *deletestr, *t;
13337dd7cddfSDavid du Colombier 	int nopen;
13347dd7cddfSDavid du Colombier 
133580ee5cbfSDavid du Colombier 	deletestr = estrstrdup("delete ", fsname);
13367dd7cddfSDavid du Colombier 
13377dd7cddfSDavid du Colombier 	nopen = 0;
13387dd7cddfSDavid du Colombier 	for(m=mbox->head; m!=nil; m=next){
13397dd7cddfSDavid du Colombier 		next = m->next;
13407dd7cddfSDavid du Colombier 		if(m->deleted == 0)
13417dd7cddfSDavid du Colombier 			continue;
13427dd7cddfSDavid du Colombier 		if(m->opened){
13437dd7cddfSDavid du Colombier 			nopen++;
13447dd7cddfSDavid du Colombier 			continue;
13457dd7cddfSDavid du Colombier 		}
13467dd7cddfSDavid du Colombier 		if(m->writebackdel){
13477dd7cddfSDavid du Colombier 			/* messages deleted by plumb message are not removed again */
13487dd7cddfSDavid du Colombier 			t = estrdup(m->name);
13497dd7cddfSDavid du Colombier 			if(strlen(t) > 0)
13507dd7cddfSDavid du Colombier 				t[strlen(t)-1] = '\0';
13517dd7cddfSDavid du Colombier 			deletestr = egrow(deletestr, " ", t);
13527dd7cddfSDavid du Colombier 		}
13537dd7cddfSDavid du Colombier 		mesgmenudel(w, mbox, m);
13547dd7cddfSDavid du Colombier 		mesgdel(mbox, m);
13557dd7cddfSDavid du Colombier 	}
13567dd7cddfSDavid du Colombier 	if(write(mbox->ctlfd, deletestr, strlen(deletestr)) < 0)
13579a747e4fSDavid du Colombier 		fprint(2, "Mail: warning: error removing mail message files: %r\n");
13587dd7cddfSDavid du Colombier 	free(deletestr);
13597dd7cddfSDavid du Colombier 	winselect(w, "0", 0);
13607dd7cddfSDavid du Colombier 	if(nopen == 0)
13617dd7cddfSDavid du Colombier 		winclean(w);
13627dd7cddfSDavid du Colombier 	mbox->dirty = 0;
13637dd7cddfSDavid du Colombier }
13647dd7cddfSDavid du Colombier 
13657dd7cddfSDavid du Colombier /* name is a full file name, but it might not belong to us */
13667dd7cddfSDavid du Colombier Message*
mesglookupfile(Message * mbox,char * name,char * digest)13677dd7cddfSDavid du Colombier mesglookupfile(Message *mbox, char *name, char *digest)
13687dd7cddfSDavid du Colombier {
13697dd7cddfSDavid du Colombier 	int k, n;
13707dd7cddfSDavid du Colombier 
13717dd7cddfSDavid du Colombier 	k = strlen(name);
13727dd7cddfSDavid du Colombier 	n = strlen(mbox->name);
13737dd7cddfSDavid du Colombier 	if(k==0 || strncmp(name, mbox->name, n) != 0){
13749a747e4fSDavid du Colombier //		fprint(2, "Mail: message %s not in this mailbox\n", name);
13757dd7cddfSDavid du Colombier 		return nil;
13767dd7cddfSDavid du Colombier 	}
13777dd7cddfSDavid du Colombier 	return mesglookup(mbox, name+n, digest);
13787dd7cddfSDavid du Colombier }
1379