xref: /plan9/sys/src/cmd/upas/fs/imap4.c (revision 27acba7cf6d37c65abba4ecf8ad572a5980447ad)
180ee5cbfSDavid du Colombier #include "common.h"
280ee5cbfSDavid du Colombier #include <ctype.h>
380ee5cbfSDavid du Colombier #include <plumb.h>
480ee5cbfSDavid du Colombier #include <libsec.h>
59a747e4fSDavid du Colombier #include <auth.h>
680ee5cbfSDavid du Colombier #include "dat.h"
780ee5cbfSDavid du Colombier 
880ee5cbfSDavid du Colombier #pragma varargck argpos imap4cmd 2
93ff48bf5SDavid du Colombier #pragma varargck	type	"Z"	char*
103ff48bf5SDavid du Colombier 
113ff48bf5SDavid du Colombier int	doublequote(Fmt*);
12ba64361bSDavid du Colombier int	pipeline = 1;
1380ee5cbfSDavid du Colombier 
1480ee5cbfSDavid du Colombier static char Eio[] = "i/o error";
1580ee5cbfSDavid du Colombier 
1680ee5cbfSDavid du Colombier typedef struct Imap Imap;
1780ee5cbfSDavid du Colombier struct Imap {
1880ee5cbfSDavid du Colombier 	char *freep;	// free this to free the strings below
1980ee5cbfSDavid du Colombier 
2080ee5cbfSDavid du Colombier 	char *host;
2180ee5cbfSDavid du Colombier 	char *user;
2280ee5cbfSDavid du Colombier 	char *mbox;
2380ee5cbfSDavid du Colombier 
2480ee5cbfSDavid du Colombier 	int mustssl;
2580ee5cbfSDavid du Colombier 	int refreshtime;
2680ee5cbfSDavid du Colombier 	int debug;
2780ee5cbfSDavid du Colombier 
2880ee5cbfSDavid du Colombier 	ulong tag;
2980ee5cbfSDavid du Colombier 	ulong validity;
3080ee5cbfSDavid du Colombier 	int nmsg;
3180ee5cbfSDavid du Colombier 	int size;
3280ee5cbfSDavid du Colombier 	char *base;
3380ee5cbfSDavid du Colombier 	char *data;
3480ee5cbfSDavid du Colombier 
3580ee5cbfSDavid du Colombier 	vlong *uid;
3680ee5cbfSDavid du Colombier 	int nuid;
3780ee5cbfSDavid du Colombier 	int muid;
3880ee5cbfSDavid du Colombier 
399a747e4fSDavid du Colombier 	Thumbprint *thumb;
409a747e4fSDavid du Colombier 
419a747e4fSDavid du Colombier 	// open network connection
429a747e4fSDavid du Colombier 	Biobuf bin;
439a747e4fSDavid du Colombier 	Biobuf bout;
4480ee5cbfSDavid du Colombier 	int fd;
4580ee5cbfSDavid du Colombier };
4680ee5cbfSDavid du Colombier 
4780ee5cbfSDavid du Colombier static char*
removecr(char * s)4880ee5cbfSDavid du Colombier removecr(char *s)
4980ee5cbfSDavid du Colombier {
5080ee5cbfSDavid du Colombier 	char *r, *w;
5180ee5cbfSDavid du Colombier 
5280ee5cbfSDavid du Colombier 	for(r=w=s; *r; r++)
5380ee5cbfSDavid du Colombier 		if(*r != '\r')
5480ee5cbfSDavid du Colombier 			*w++ = *r;
5580ee5cbfSDavid du Colombier 	*w = '\0';
5680ee5cbfSDavid du Colombier 	return s;
5780ee5cbfSDavid du Colombier }
5880ee5cbfSDavid du Colombier 
5980ee5cbfSDavid du Colombier //
6080ee5cbfSDavid du Colombier // send imap4 command
6180ee5cbfSDavid du Colombier //
6280ee5cbfSDavid du Colombier static void
imap4cmd(Imap * imap,char * fmt,...)6380ee5cbfSDavid du Colombier imap4cmd(Imap *imap, char *fmt, ...)
6480ee5cbfSDavid du Colombier {
6580ee5cbfSDavid du Colombier 	char buf[128], *p;
6680ee5cbfSDavid du Colombier 	va_list va;
6780ee5cbfSDavid du Colombier 
6880ee5cbfSDavid du Colombier 	va_start(va, fmt);
699a747e4fSDavid du Colombier 	p = buf+sprint(buf, "9X%lud ", imap->tag);
709a747e4fSDavid du Colombier 	vseprint(p, buf+sizeof(buf), fmt, va);
7180ee5cbfSDavid du Colombier 	va_end(va);
7280ee5cbfSDavid du Colombier 
7380ee5cbfSDavid du Colombier 	p = buf+strlen(buf);
7480ee5cbfSDavid du Colombier 	if(p > (buf+sizeof(buf)-3))
7580ee5cbfSDavid du Colombier 		sysfatal("imap4 command too long");
7680ee5cbfSDavid du Colombier 
7780ee5cbfSDavid du Colombier 	if(imap->debug)
7880ee5cbfSDavid du Colombier 		fprint(2, "-> %s\n", buf);
7980ee5cbfSDavid du Colombier 	strcpy(p, "\r\n");
809a747e4fSDavid du Colombier 	Bwrite(&imap->bout, buf, strlen(buf));
819a747e4fSDavid du Colombier 	Bflush(&imap->bout);
8280ee5cbfSDavid du Colombier }
8380ee5cbfSDavid du Colombier 
8480ee5cbfSDavid du Colombier enum {
8580ee5cbfSDavid du Colombier 	OK,
8680ee5cbfSDavid du Colombier 	NO,
8780ee5cbfSDavid du Colombier 	BAD,
8880ee5cbfSDavid du Colombier 	BYE,
8980ee5cbfSDavid du Colombier 	EXISTS,
9080ee5cbfSDavid du Colombier 	STATUS,
9180ee5cbfSDavid du Colombier 	FETCH,
9280ee5cbfSDavid du Colombier 	UNKNOWN,
9380ee5cbfSDavid du Colombier };
9480ee5cbfSDavid du Colombier 
9580ee5cbfSDavid du Colombier static char *verblist[] = {
9680ee5cbfSDavid du Colombier [OK]		"OK",
9780ee5cbfSDavid du Colombier [NO]		"NO",
9880ee5cbfSDavid du Colombier [BAD]	"BAD",
9980ee5cbfSDavid du Colombier [BYE]	"BYE",
10080ee5cbfSDavid du Colombier [EXISTS]	"EXISTS",
10180ee5cbfSDavid du Colombier [STATUS]	"STATUS",
10280ee5cbfSDavid du Colombier [FETCH]	"FETCH",
10380ee5cbfSDavid du Colombier };
10480ee5cbfSDavid du Colombier 
10580ee5cbfSDavid du Colombier static int
verbcode(char * verb)10680ee5cbfSDavid du Colombier verbcode(char *verb)
10780ee5cbfSDavid du Colombier {
10880ee5cbfSDavid du Colombier 	int i;
10980ee5cbfSDavid du Colombier 	char *q;
11080ee5cbfSDavid du Colombier 
11180ee5cbfSDavid du Colombier 	if(q = strchr(verb, ' '))
11280ee5cbfSDavid du Colombier 		*q = '\0';
11380ee5cbfSDavid du Colombier 
11480ee5cbfSDavid du Colombier 	for(i=0; i<nelem(verblist); i++)
11580ee5cbfSDavid du Colombier 		if(verblist[i] && strcmp(verblist[i], verb)==0){
11680ee5cbfSDavid du Colombier 			if(q)
11780ee5cbfSDavid du Colombier 				*q = ' ';
11880ee5cbfSDavid du Colombier 			return i;
11980ee5cbfSDavid du Colombier 		}
12080ee5cbfSDavid du Colombier 	if(q)
12180ee5cbfSDavid du Colombier 		*q = ' ';
12280ee5cbfSDavid du Colombier 	return UNKNOWN;
12380ee5cbfSDavid du Colombier }
12480ee5cbfSDavid du Colombier 
1259a747e4fSDavid du Colombier static void
strupr(char * s)1269a747e4fSDavid du Colombier strupr(char *s)
1279a747e4fSDavid du Colombier {
1289a747e4fSDavid du Colombier 	for(; *s; s++)
1299a747e4fSDavid du Colombier 		if('a' <= *s && *s <= 'z')
1309a747e4fSDavid du Colombier 			*s += 'A'-'a';
1319a747e4fSDavid du Colombier }
1329a747e4fSDavid du Colombier 
133f7bdc0bcSDavid du Colombier static void
imapgrow(Imap * imap,int n)134f7bdc0bcSDavid du Colombier imapgrow(Imap *imap, int n)
135f7bdc0bcSDavid du Colombier {
136f7bdc0bcSDavid du Colombier 	int i;
137f7bdc0bcSDavid du Colombier 
138f7bdc0bcSDavid du Colombier 	if(imap->data == nil){
139f7bdc0bcSDavid du Colombier 		imap->base = emalloc(n+1);
140f7bdc0bcSDavid du Colombier 		imap->data = imap->base;
141f7bdc0bcSDavid du Colombier 		imap->size = n+1;
142f7bdc0bcSDavid du Colombier 	}
143f7bdc0bcSDavid du Colombier 	if(n >= imap->size){
144f7bdc0bcSDavid du Colombier 		// friggin microsoft - reallocate
145f7bdc0bcSDavid du Colombier 		i = imap->data - imap->base;
146f7bdc0bcSDavid du Colombier 		imap->base = erealloc(imap->base, i+n+1);
147f7bdc0bcSDavid du Colombier 		imap->data = imap->base + i;
148f7bdc0bcSDavid du Colombier 		imap->size = n+1;
149f7bdc0bcSDavid du Colombier 	}
150f7bdc0bcSDavid du Colombier }
151f7bdc0bcSDavid du Colombier 
1529a747e4fSDavid du Colombier 
15380ee5cbfSDavid du Colombier //
15480ee5cbfSDavid du Colombier // get imap4 response line.  there might be various
15580ee5cbfSDavid du Colombier // data or other informational lines mixed in.
15680ee5cbfSDavid du Colombier //
15780ee5cbfSDavid du Colombier static char*
imap4resp(Imap * imap)15880ee5cbfSDavid du Colombier imap4resp(Imap *imap)
15980ee5cbfSDavid du Colombier {
1609a747e4fSDavid du Colombier 	char *line, *p, *ep, *op, *q, *r, *en, *verb;
161fa1160edSDavid du Colombier 	int i, n;
162fa1160edSDavid du Colombier 	static char error[256];
16380ee5cbfSDavid du Colombier 
1649a747e4fSDavid du Colombier 	while(p = Brdline(&imap->bin, '\n')){
1659a747e4fSDavid du Colombier 		ep = p+Blinelen(&imap->bin);
16680ee5cbfSDavid du Colombier 		while(ep > p && (ep[-1]=='\n' || ep[-1]=='\r'))
16780ee5cbfSDavid du Colombier 			*--ep = '\0';
16880ee5cbfSDavid du Colombier 
16980ee5cbfSDavid du Colombier 		if(imap->debug)
17080ee5cbfSDavid du Colombier 			fprint(2, "<- %s\n", p);
1719a747e4fSDavid du Colombier 		strupr(p);
17280ee5cbfSDavid du Colombier 
17380ee5cbfSDavid du Colombier 		switch(p[0]){
17480ee5cbfSDavid du Colombier 		case '+':
17580ee5cbfSDavid du Colombier 			if(imap->tag == 0)
17680ee5cbfSDavid du Colombier 				fprint(2, "unexpected: %s\n", p);
17780ee5cbfSDavid du Colombier 			break;
17880ee5cbfSDavid du Colombier 
17980ee5cbfSDavid du Colombier 		// ``unsolicited'' information; everything happens here.
18080ee5cbfSDavid du Colombier 		case '*':
18180ee5cbfSDavid du Colombier 			if(p[1]!=' ')
18280ee5cbfSDavid du Colombier 				continue;
18380ee5cbfSDavid du Colombier 			p += 2;
18480ee5cbfSDavid du Colombier 			line = p;
18580ee5cbfSDavid du Colombier 			n = strtol(p, &p, 10);
18680ee5cbfSDavid du Colombier 			if(*p==' ')
18780ee5cbfSDavid du Colombier 				p++;
18880ee5cbfSDavid du Colombier 			verb = p;
18980ee5cbfSDavid du Colombier 
19080ee5cbfSDavid du Colombier 			if(p = strchr(verb, ' '))
19180ee5cbfSDavid du Colombier 				p++;
19280ee5cbfSDavid du Colombier 			else
19380ee5cbfSDavid du Colombier 				p = verb+strlen(verb);
19480ee5cbfSDavid du Colombier 
19580ee5cbfSDavid du Colombier 			switch(verbcode(verb)){
19680ee5cbfSDavid du Colombier 			case OK:
19780ee5cbfSDavid du Colombier 			case NO:
19880ee5cbfSDavid du Colombier 			case BAD:
19980ee5cbfSDavid du Colombier 				// human readable text at p;
20080ee5cbfSDavid du Colombier 				break;
20180ee5cbfSDavid du Colombier 			case BYE:
20280ee5cbfSDavid du Colombier 				// early disconnect
20380ee5cbfSDavid du Colombier 				// human readable text at p;
20480ee5cbfSDavid du Colombier 				break;
20580ee5cbfSDavid du Colombier 
20680ee5cbfSDavid du Colombier 			// * 32 EXISTS
20780ee5cbfSDavid du Colombier 			case EXISTS:
20880ee5cbfSDavid du Colombier 				imap->nmsg = n;
20980ee5cbfSDavid du Colombier 				break;
21080ee5cbfSDavid du Colombier 
21180ee5cbfSDavid du Colombier 			// * STATUS Inbox (MESSAGES 2 UIDVALIDITY 960164964)
21280ee5cbfSDavid du Colombier 			case STATUS:
21380ee5cbfSDavid du Colombier 				if(q = strstr(p, "MESSAGES"))
21480ee5cbfSDavid du Colombier 					imap->nmsg = atoi(q+8);
21580ee5cbfSDavid du Colombier 				if(q = strstr(p, "UIDVALIDITY"))
21680ee5cbfSDavid du Colombier 					imap->validity = strtoul(q+11, 0, 10);
21780ee5cbfSDavid du Colombier 				break;
21880ee5cbfSDavid du Colombier 
21980ee5cbfSDavid du Colombier 			case FETCH:
220f7bdc0bcSDavid du Colombier 				// * 1 FETCH (uid 8889 RFC822.SIZE 3031 body[] {3031}
221f7bdc0bcSDavid du Colombier 				// <3031 bytes of data>
222f7bdc0bcSDavid du Colombier  				// )
223f7bdc0bcSDavid du Colombier 				if(strstr(p, "RFC822.SIZE") && strstr(p, "BODY[]")){
224f7bdc0bcSDavid du Colombier 					if((q = strchr(p, '{'))
225f7bdc0bcSDavid du Colombier 					&& (n=strtol(q+1, &en, 0), *en=='}')){
226f7bdc0bcSDavid du Colombier 						if(imap->data == nil || n >= imap->size)
227f7bdc0bcSDavid du Colombier 							imapgrow(imap, n);
228f7bdc0bcSDavid du Colombier 						if((i = Bread(&imap->bin, imap->data, n)) != n){
229f7bdc0bcSDavid du Colombier 							snprint(error, sizeof error,
230f7bdc0bcSDavid du Colombier 								"short read %d != %d: %r\n",
231f7bdc0bcSDavid du Colombier 								i, n);
232f7bdc0bcSDavid du Colombier 							return error;
233f7bdc0bcSDavid du Colombier 						}
234f7bdc0bcSDavid du Colombier 						if(imap->debug)
235f7bdc0bcSDavid du Colombier 							fprint(2, "<- read %d bytes\n", n);
236f7bdc0bcSDavid du Colombier 						imap->data[n] = '\0';
237f7bdc0bcSDavid du Colombier 						if(imap->debug)
238f7bdc0bcSDavid du Colombier 							fprint(2, "<- %s\n", imap->data);
239f7bdc0bcSDavid du Colombier 						imap->data += n;
240f7bdc0bcSDavid du Colombier 						imap->size -= n;
241f7bdc0bcSDavid du Colombier 						p = Brdline(&imap->bin, '\n');
242f7bdc0bcSDavid du Colombier 						if(imap->debug)
243f7bdc0bcSDavid du Colombier 							fprint(2, "<- ignoring %.*s\n",
244f7bdc0bcSDavid du Colombier 								Blinelen(&imap->bin), p);
245f7bdc0bcSDavid du Colombier 					}else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
246f7bdc0bcSDavid du Colombier 						*r = '\0';
247f7bdc0bcSDavid du Colombier 						q++;
248f7bdc0bcSDavid du Colombier 						n = r-q;
249f7bdc0bcSDavid du Colombier 						if(imap->data == nil || n >= imap->size)
250f7bdc0bcSDavid du Colombier 							imapgrow(imap, n);
251f7bdc0bcSDavid du Colombier 						memmove(imap->data, q, n);
252f7bdc0bcSDavid du Colombier 						imap->data[n] = '\0';
253f7bdc0bcSDavid du Colombier 						imap->data += n;
254f7bdc0bcSDavid du Colombier 						imap->size -= n;
255f7bdc0bcSDavid du Colombier 					}else
256f7bdc0bcSDavid du Colombier 						return "confused about FETCH response";
257f7bdc0bcSDavid du Colombier 					break;
258f7bdc0bcSDavid du Colombier 				}
259f7bdc0bcSDavid du Colombier 
26080ee5cbfSDavid du Colombier 				// * 1 FETCH (UID 1 RFC822.SIZE 511)
26180ee5cbfSDavid du Colombier 				if(q=strstr(p, "RFC822.SIZE")){
26280ee5cbfSDavid du Colombier 					imap->size = atoi(q+11);
26380ee5cbfSDavid du Colombier 					break;
26480ee5cbfSDavid du Colombier 				}
26580ee5cbfSDavid du Colombier 
26680ee5cbfSDavid du Colombier 				// * 1 FETCH (UID 1 RFC822.HEADER {496}
26780ee5cbfSDavid du Colombier 				// <496 bytes of data>
26880ee5cbfSDavid du Colombier  				// )
26980ee5cbfSDavid du Colombier 				// * 1 FETCH (UID 1 RFC822.HEADER "data")
27080ee5cbfSDavid du Colombier 				if(strstr(p, "RFC822.HEADER") || strstr(p, "RFC822.TEXT")){
2719a747e4fSDavid du Colombier 					if((q = strchr(p, '{'))
2729a747e4fSDavid du Colombier 					&& (n=strtol(q+1, &en, 0), *en=='}')){
273f7bdc0bcSDavid du Colombier 						if(imap->data == nil || n >= imap->size)
274f7bdc0bcSDavid du Colombier 							imapgrow(imap, n);
275fa1160edSDavid du Colombier 						if((i = Bread(&imap->bin, imap->data, n)) != n){
276f7bdc0bcSDavid du Colombier 							snprint(error, sizeof error,
277f7bdc0bcSDavid du Colombier 								"short read %d != %d: %r\n",
278f7bdc0bcSDavid du Colombier 								i, n);
279fa1160edSDavid du Colombier 							return error;
280fa1160edSDavid du Colombier 						}
281f7bdc0bcSDavid du Colombier 						if(imap->debug)
282f7bdc0bcSDavid du Colombier 							fprint(2, "<- read %d bytes\n", n);
28380ee5cbfSDavid du Colombier 						imap->data[n] = '\0';
284f7bdc0bcSDavid du Colombier 						if(imap->debug)
285f7bdc0bcSDavid du Colombier 							fprint(2, "<- %s\n", imap->data);
28680ee5cbfSDavid du Colombier 						imap->data += n;
28780ee5cbfSDavid du Colombier 						imap->size -= n;
288f7bdc0bcSDavid du Colombier 						p = Brdline(&imap->bin, '\n');
289f7bdc0bcSDavid du Colombier 						if(imap->debug)
290f7bdc0bcSDavid du Colombier 							fprint(2, "<- ignoring %.*s\n",
291f7bdc0bcSDavid du Colombier 								Blinelen(&imap->bin), p);
29280ee5cbfSDavid du Colombier 					}else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
29380ee5cbfSDavid du Colombier 						*r = '\0';
29480ee5cbfSDavid du Colombier 						q++;
29580ee5cbfSDavid du Colombier 						n = r-q;
296f7bdc0bcSDavid du Colombier 						if(imap->data == nil || n >= imap->size)
297f7bdc0bcSDavid du Colombier 							imapgrow(imap, n);
29880ee5cbfSDavid du Colombier 						memmove(imap->data, q, n);
29980ee5cbfSDavid du Colombier 						imap->data[n] = '\0';
30080ee5cbfSDavid du Colombier 						imap->data += n;
30180ee5cbfSDavid du Colombier 						imap->size -= n;
30280ee5cbfSDavid du Colombier 					}else
30380ee5cbfSDavid du Colombier 						return "confused about FETCH response";
30480ee5cbfSDavid du Colombier 					break;
30580ee5cbfSDavid du Colombier 				}
30680ee5cbfSDavid du Colombier 
30780ee5cbfSDavid du Colombier 				// * 1 FETCH (UID 1)
30880ee5cbfSDavid du Colombier 				// * 2 FETCH (UID 6)
30980ee5cbfSDavid du Colombier 				if(q = strstr(p, "UID")){
31080ee5cbfSDavid du Colombier 					if(imap->nuid < imap->muid)
31180ee5cbfSDavid du Colombier 						imap->uid[imap->nuid++] = ((vlong)imap->validity<<32)|strtoul(q+3, nil, 10);
31280ee5cbfSDavid du Colombier 					break;
31380ee5cbfSDavid du Colombier 				}
31480ee5cbfSDavid du Colombier 			}
31580ee5cbfSDavid du Colombier 
31680ee5cbfSDavid du Colombier 			if(imap->tag == 0)
31780ee5cbfSDavid du Colombier 				return line;
31880ee5cbfSDavid du Colombier 			break;
31980ee5cbfSDavid du Colombier 
32080ee5cbfSDavid du Colombier 		case '9':		// response to our message
3219a747e4fSDavid du Colombier 			op = p;
3229a747e4fSDavid du Colombier 			if(p[1]=='X' && strtoul(p+2, &p, 10)==imap->tag){
32380ee5cbfSDavid du Colombier 				while(*p==' ')
32480ee5cbfSDavid du Colombier 					p++;
3259a747e4fSDavid du Colombier 				imap->tag++;
32680ee5cbfSDavid du Colombier 				return p;
32780ee5cbfSDavid du Colombier 			}
3289a747e4fSDavid du Colombier 			fprint(2, "expected %lud; got %s\n", imap->tag, op);
32980ee5cbfSDavid du Colombier 			break;
33080ee5cbfSDavid du Colombier 
33180ee5cbfSDavid du Colombier 		default:
332f15b2559SDavid du Colombier 			if(imap->debug || *p)
33380ee5cbfSDavid du Colombier 				fprint(2, "unexpected line: %s\n", p);
33480ee5cbfSDavid du Colombier 		}
33580ee5cbfSDavid du Colombier 	}
336f7bdc0bcSDavid du Colombier 	snprint(error, sizeof error, "i/o error: %r\n");
337f7bdc0bcSDavid du Colombier 	return error;
33880ee5cbfSDavid du Colombier }
33980ee5cbfSDavid du Colombier 
34080ee5cbfSDavid du Colombier static int
isokay(char * resp)34180ee5cbfSDavid du Colombier isokay(char *resp)
34280ee5cbfSDavid du Colombier {
34380ee5cbfSDavid du Colombier 	return strncmp(resp, "OK", 2)==0;
34480ee5cbfSDavid du Colombier }
34580ee5cbfSDavid du Colombier 
34680ee5cbfSDavid du Colombier //
34780ee5cbfSDavid du Colombier // log in to IMAP4 server, select mailbox, no SSL at the moment
34880ee5cbfSDavid du Colombier //
34980ee5cbfSDavid du Colombier static char*
imap4login(Imap * imap)35080ee5cbfSDavid du Colombier imap4login(Imap *imap)
35180ee5cbfSDavid du Colombier {
3529a747e4fSDavid du Colombier 	char *s;
3539a747e4fSDavid du Colombier 	UserPasswd *up;
35480ee5cbfSDavid du Colombier 
35580ee5cbfSDavid du Colombier 	imap->tag = 0;
35680ee5cbfSDavid du Colombier 	s = imap4resp(imap);
357d9306527SDavid du Colombier 	if(!isokay(s))
358d9306527SDavid du Colombier 		return "error in initial IMAP handshake";
35980ee5cbfSDavid du Colombier 
3609a747e4fSDavid du Colombier 	if(imap->user != nil)
3619a747e4fSDavid du Colombier 		up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q user=%q", imap->host, imap->user);
3629a747e4fSDavid du Colombier 	else
3639a747e4fSDavid du Colombier 		up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q", imap->host);
3649a747e4fSDavid du Colombier 	if(up == nil)
365d9306527SDavid du Colombier 		return "cannot find IMAP password";
36680ee5cbfSDavid du Colombier 
3679a747e4fSDavid du Colombier 	imap->tag = 1;
3683ff48bf5SDavid du Colombier 	imap4cmd(imap, "LOGIN %Z %Z", up->user, up->passwd);
3699a747e4fSDavid du Colombier 	free(up);
37080ee5cbfSDavid du Colombier 	if(!isokay(s = imap4resp(imap)))
37180ee5cbfSDavid du Colombier 		return s;
37280ee5cbfSDavid du Colombier 
3733ff48bf5SDavid du Colombier 	imap4cmd(imap, "SELECT %Z", imap->mbox);
37480ee5cbfSDavid du Colombier 	if(!isokay(s = imap4resp(imap)))
37580ee5cbfSDavid du Colombier 		return s;
37680ee5cbfSDavid du Colombier 
37780ee5cbfSDavid du Colombier 	return nil;
37880ee5cbfSDavid du Colombier }
37980ee5cbfSDavid du Colombier 
3803e748dfcSDavid du Colombier static char*
imaperrstr(char * host,char * port)3813e748dfcSDavid du Colombier imaperrstr(char *host, char *port)
3823e748dfcSDavid du Colombier {
38321887c0bSDavid du Colombier 	/*
38421887c0bSDavid du Colombier 	 * make mess big enough to hold a TLS certificate fingerprint
38521887c0bSDavid du Colombier 	 * plus quite a bit of slop.
38621887c0bSDavid du Colombier 	 */
38721887c0bSDavid du Colombier 	static char mess[3 * Errlen];
38821887c0bSDavid du Colombier 	char err[Errlen];
3893e748dfcSDavid du Colombier 
3903e748dfcSDavid du Colombier 	err[0] = '\0';
3913e748dfcSDavid du Colombier 	errstr(err, sizeof(err));
3923e748dfcSDavid du Colombier 	snprint(mess, sizeof(mess), "%s/%s:%s", host, port, err);
3933e748dfcSDavid du Colombier 	return mess;
3943e748dfcSDavid du Colombier }
3953e748dfcSDavid du Colombier 
3964727cc49SDavid du Colombier static int
starttls(Imap * imap,TLSconn * connp)3974727cc49SDavid du Colombier starttls(Imap *imap, TLSconn *connp)
3984727cc49SDavid du Colombier {
3994727cc49SDavid du Colombier 	int sfd;
4004727cc49SDavid du Colombier 	uchar digest[SHA1dlen];
4014727cc49SDavid du Colombier 
4024727cc49SDavid du Colombier 	fmtinstall('H', encodefmt);
4034727cc49SDavid du Colombier 	memset(connp, 0, sizeof *connp);
4044727cc49SDavid du Colombier 	sfd = tlsClient(imap->fd, connp);
4054727cc49SDavid du Colombier 	if(sfd < 0) {
4064727cc49SDavid du Colombier 		werrstr("tlsClient: %r");
4074727cc49SDavid du Colombier 		return -1;
4084727cc49SDavid du Colombier 	}
4094727cc49SDavid du Colombier 	if(connp->cert==nil || connp->certlen <= 0) {
4104727cc49SDavid du Colombier 		close(sfd);
4114727cc49SDavid du Colombier 		werrstr("server did not provide TLS certificate");
4124727cc49SDavid du Colombier 		return -1;
4134727cc49SDavid du Colombier 	}
4144727cc49SDavid du Colombier 	sha1(connp->cert, connp->certlen, digest, nil);
415*27acba7cSDavid du Colombier 	/*
416*27acba7cSDavid du Colombier 	 * don't do this any more.  our local it people are rotating their
417*27acba7cSDavid du Colombier 	 * certificates faster than we can keep up.
418*27acba7cSDavid du Colombier 	 */
419*27acba7cSDavid du Colombier 	if(0 && (!imap->thumb || !okThumbprint(digest, imap->thumb))){
4204727cc49SDavid du Colombier 		close(sfd);
4214727cc49SDavid du Colombier 		werrstr("server certificate %.*H not recognized",
4224727cc49SDavid du Colombier 			SHA1dlen, digest);
4234727cc49SDavid du Colombier 		return -1;
4244727cc49SDavid du Colombier 	}
4254727cc49SDavid du Colombier 	close(imap->fd);
4264727cc49SDavid du Colombier 	imap->fd = sfd;
4274727cc49SDavid du Colombier 	return sfd;
4284727cc49SDavid du Colombier }
4294727cc49SDavid du Colombier 
4309a747e4fSDavid du Colombier //
43180ee5cbfSDavid du Colombier // dial and handshake with the imap server
43280ee5cbfSDavid du Colombier //
43380ee5cbfSDavid du Colombier static char*
imap4dial(Imap * imap)43480ee5cbfSDavid du Colombier imap4dial(Imap *imap)
43580ee5cbfSDavid du Colombier {
4369a747e4fSDavid du Colombier 	char *err, *port;
4379a747e4fSDavid du Colombier 	int sfd;
4389a747e4fSDavid du Colombier 	TLSconn conn;
43980ee5cbfSDavid du Colombier 
4409a747e4fSDavid du Colombier 	if(imap->fd >= 0){
4419a747e4fSDavid du Colombier 		imap4cmd(imap, "noop");
4429a747e4fSDavid du Colombier 		if(isokay(imap4resp(imap)))
4439a747e4fSDavid du Colombier 			return nil;
4449a747e4fSDavid du Colombier 		close(imap->fd);
4459a747e4fSDavid du Colombier 		imap->fd = -1;
4469a747e4fSDavid du Colombier 	}
4479a747e4fSDavid du Colombier 
4489a747e4fSDavid du Colombier 	if(imap->mustssl)
4499a747e4fSDavid du Colombier 		port = "imaps";
4509a747e4fSDavid du Colombier 	else
4512c16e87eSDavid du Colombier 		port = "imap";
4529a747e4fSDavid du Colombier 
4539a747e4fSDavid du Colombier 	if((imap->fd = dial(netmkaddr(imap->host, "net", port), 0, 0, 0)) < 0)
4543e748dfcSDavid du Colombier 		return imaperrstr(imap->host, port);
45580ee5cbfSDavid du Colombier 
4569a747e4fSDavid du Colombier 	if(imap->mustssl){
4574727cc49SDavid du Colombier 		sfd = starttls(imap, &conn);
4584727cc49SDavid du Colombier 		if (sfd < 0) {
4599a747e4fSDavid du Colombier 			free(conn.cert);
4604727cc49SDavid du Colombier 			return imaperrstr(imap->host, port);
4614727cc49SDavid du Colombier 		}
462f7bdc0bcSDavid du Colombier 		if(imap->debug){
463f7bdc0bcSDavid du Colombier 			char fn[128];
464f7bdc0bcSDavid du Colombier 			int fd;
465f7bdc0bcSDavid du Colombier 
466f7bdc0bcSDavid du Colombier 			snprint(fn, sizeof fn, "%s/ctl", conn.dir);
467f7bdc0bcSDavid du Colombier 			fd = open(fn, ORDWR);
468f7bdc0bcSDavid du Colombier 			if(fd < 0)
469f7bdc0bcSDavid du Colombier 				fprint(2, "opening ctl: %r\n");
470f7bdc0bcSDavid du Colombier 			if(fprint(fd, "debug") < 0)
471f7bdc0bcSDavid du Colombier 				fprint(2, "writing ctl: %r\n");
472f7bdc0bcSDavid du Colombier 			close(fd);
473f7bdc0bcSDavid du Colombier 		}
4749a747e4fSDavid du Colombier 	}
4759a747e4fSDavid du Colombier 	Binit(&imap->bin, imap->fd, OREAD);
4769a747e4fSDavid du Colombier 	Binit(&imap->bout, imap->fd, OWRITE);
47780ee5cbfSDavid du Colombier 
47880ee5cbfSDavid du Colombier 	if(err = imap4login(imap)) {
47980ee5cbfSDavid du Colombier 		close(imap->fd);
48080ee5cbfSDavid du Colombier 		return err;
48180ee5cbfSDavid du Colombier 	}
48280ee5cbfSDavid du Colombier 
48380ee5cbfSDavid du Colombier 	return nil;
48480ee5cbfSDavid du Colombier }
48580ee5cbfSDavid du Colombier 
48680ee5cbfSDavid du Colombier //
48780ee5cbfSDavid du Colombier // close connection
48880ee5cbfSDavid du Colombier //
48980ee5cbfSDavid du Colombier static void
imap4hangup(Imap * imap)49080ee5cbfSDavid du Colombier imap4hangup(Imap *imap)
49180ee5cbfSDavid du Colombier {
49280ee5cbfSDavid du Colombier 	imap4cmd(imap, "LOGOUT");
49380ee5cbfSDavid du Colombier 	imap4resp(imap);
49480ee5cbfSDavid du Colombier 	close(imap->fd);
49580ee5cbfSDavid du Colombier }
49680ee5cbfSDavid du Colombier 
49780ee5cbfSDavid du Colombier //
49880ee5cbfSDavid du Colombier // download a single message
49980ee5cbfSDavid du Colombier //
50080ee5cbfSDavid du Colombier static char*
imap4fetch(Mailbox * mb,Message * m)50180ee5cbfSDavid du Colombier imap4fetch(Mailbox *mb, Message *m)
50280ee5cbfSDavid du Colombier {
503f7bdc0bcSDavid du Colombier 	int i;
5049a747e4fSDavid du Colombier 	char *p, *s, sdigest[2*SHA1dlen+1];
50580ee5cbfSDavid du Colombier 	Imap *imap;
50680ee5cbfSDavid du Colombier 
50780ee5cbfSDavid du Colombier 	imap = mb->aux;
50880ee5cbfSDavid du Colombier 
50980ee5cbfSDavid du Colombier 	imap->size = 0;
51080ee5cbfSDavid du Colombier 
51180ee5cbfSDavid du Colombier 	if(!isokay(s = imap4resp(imap)))
51280ee5cbfSDavid du Colombier 		return s;
51380ee5cbfSDavid du Colombier 
514f7bdc0bcSDavid du Colombier 	p = imap->base;
5158d37e088SDavid du Colombier 	if(p == nil)
5168d37e088SDavid du Colombier 		return "did not get message body";
5178d37e088SDavid du Colombier 
51880ee5cbfSDavid du Colombier 	removecr(p);
51980ee5cbfSDavid du Colombier 	free(m->start);
52080ee5cbfSDavid du Colombier 	m->start = p;
52180ee5cbfSDavid du Colombier 	m->end = p+strlen(p);
52280ee5cbfSDavid du Colombier 	m->bend = m->rbend = m->end;
52380ee5cbfSDavid du Colombier 	m->header = m->start;
52480ee5cbfSDavid du Colombier 
52580ee5cbfSDavid du Colombier 	imap->base = nil;
52680ee5cbfSDavid du Colombier 	imap->data = nil;
52780ee5cbfSDavid du Colombier 
5287a02f3c0SDavid du Colombier 	parse(m, 0, mb, 1);
52980ee5cbfSDavid du Colombier 
53080ee5cbfSDavid du Colombier 	// digest headers
53180ee5cbfSDavid du Colombier 	sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
53280ee5cbfSDavid du Colombier 	for(i = 0; i < SHA1dlen; i++)
53380ee5cbfSDavid du Colombier 		sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
53480ee5cbfSDavid du Colombier 	m->sdigest = s_copy(sdigest);
53580ee5cbfSDavid du Colombier 
53680ee5cbfSDavid du Colombier 	return nil;
53780ee5cbfSDavid du Colombier }
53880ee5cbfSDavid du Colombier 
53980ee5cbfSDavid du Colombier //
54080ee5cbfSDavid du Colombier // check for new messages on imap4 server
54180ee5cbfSDavid du Colombier // download new messages, mark deleted messages
54280ee5cbfSDavid du Colombier //
54380ee5cbfSDavid du Colombier static char*
imap4read(Imap * imap,Mailbox * mb,int doplumb)54480ee5cbfSDavid du Colombier imap4read(Imap *imap, Mailbox *mb, int doplumb)
54580ee5cbfSDavid du Colombier {
54680ee5cbfSDavid du Colombier 	char *s;
5479a747e4fSDavid du Colombier 	int i, ignore, nnew, t;
54880ee5cbfSDavid du Colombier 	Message *m, *next, **l;
54980ee5cbfSDavid du Colombier 
5503ff48bf5SDavid du Colombier 	imap4cmd(imap, "STATUS %Z (MESSAGES UIDVALIDITY)", imap->mbox);
55180ee5cbfSDavid du Colombier 	if(!isokay(s = imap4resp(imap)))
55280ee5cbfSDavid du Colombier 		return s;
55380ee5cbfSDavid du Colombier 
55480ee5cbfSDavid du Colombier 	imap->nuid = 0;
55580ee5cbfSDavid du Colombier 	imap->uid = erealloc(imap->uid, imap->nmsg*sizeof(imap->uid[0]));
55680ee5cbfSDavid du Colombier 	imap->muid = imap->nmsg;
55780ee5cbfSDavid du Colombier 
5583ff48bf5SDavid du Colombier 	if(imap->nmsg > 0){
55980ee5cbfSDavid du Colombier 		imap4cmd(imap, "UID FETCH 1:* UID");
56080ee5cbfSDavid du Colombier 		if(!isokay(s = imap4resp(imap)))
56180ee5cbfSDavid du Colombier 			return s;
5623ff48bf5SDavid du Colombier 	}
56380ee5cbfSDavid du Colombier 
56480ee5cbfSDavid du Colombier 	l = &mb->root->part;
56580ee5cbfSDavid du Colombier 	for(i=0; i<imap->nuid; i++){
56680ee5cbfSDavid du Colombier 		ignore = 0;
56780ee5cbfSDavid du Colombier 		while(*l != nil){
56880ee5cbfSDavid du Colombier 			if((*l)->imapuid == imap->uid[i]){
56980ee5cbfSDavid du Colombier 				ignore = 1;
57080ee5cbfSDavid du Colombier 				l = &(*l)->next;
57180ee5cbfSDavid du Colombier 				break;
57280ee5cbfSDavid du Colombier 			}else{
57380ee5cbfSDavid du Colombier 				// old mail, we don't have it anymore
57480ee5cbfSDavid du Colombier 				if(doplumb)
57580ee5cbfSDavid du Colombier 					mailplumb(mb, *l, 1);
57680ee5cbfSDavid du Colombier 				(*l)->inmbox = 0;
57780ee5cbfSDavid du Colombier 				(*l)->deleted = 1;
57880ee5cbfSDavid du Colombier 				l = &(*l)->next;
57980ee5cbfSDavid du Colombier 			}
58080ee5cbfSDavid du Colombier 		}
58180ee5cbfSDavid du Colombier 		if(ignore)
58280ee5cbfSDavid du Colombier 			continue;
58380ee5cbfSDavid du Colombier 
58480ee5cbfSDavid du Colombier 		// new message
58580ee5cbfSDavid du Colombier 		m = newmessage(mb->root);
58680ee5cbfSDavid du Colombier 		m->mallocd = 1;
58780ee5cbfSDavid du Colombier 		m->inmbox = 1;
58880ee5cbfSDavid du Colombier 		m->imapuid = imap->uid[i];
58980ee5cbfSDavid du Colombier 
59080ee5cbfSDavid du Colombier 		// add to chain, will download soon
59180ee5cbfSDavid du Colombier 		*l = m;
59280ee5cbfSDavid du Colombier 		l = &m->next;
59380ee5cbfSDavid du Colombier 	}
59480ee5cbfSDavid du Colombier 
59580ee5cbfSDavid du Colombier 	// whatever is left at the end of the chain is gone
59680ee5cbfSDavid du Colombier 	while(*l != nil){
59780ee5cbfSDavid du Colombier 		if(doplumb)
59880ee5cbfSDavid du Colombier 			mailplumb(mb, *l, 1);
59980ee5cbfSDavid du Colombier 		(*l)->inmbox = 0;
60080ee5cbfSDavid du Colombier 		(*l)->deleted = 1;
60180ee5cbfSDavid du Colombier 		l = &(*l)->next;
60280ee5cbfSDavid du Colombier 	}
60380ee5cbfSDavid du Colombier 
60480ee5cbfSDavid du Colombier 	// download new messages
6059a747e4fSDavid du Colombier 	t = imap->tag;
606ba64361bSDavid du Colombier 	if(pipeline)
6079a747e4fSDavid du Colombier 	switch(rfork(RFPROC|RFMEM)){
6089a747e4fSDavid du Colombier 	case -1:
6099a747e4fSDavid du Colombier 		sysfatal("rfork: %r");
6109a747e4fSDavid du Colombier 	default:
6119a747e4fSDavid du Colombier 		break;
6129a747e4fSDavid du Colombier 	case 0:
6139a747e4fSDavid du Colombier 		for(m = mb->root->part; m != nil; m = m->next){
6149a747e4fSDavid du Colombier 			if(m->start != nil)
6159a747e4fSDavid du Colombier 				continue;
616f7bdc0bcSDavid du Colombier 			if(imap->debug)
617f7bdc0bcSDavid du Colombier 				fprint(2, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
618f7bdc0bcSDavid du Colombier 					t, (ulong)m->imapuid);
619f7bdc0bcSDavid du Colombier 			Bprint(&imap->bout, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
620f7bdc0bcSDavid du Colombier 				t++, (ulong)m->imapuid);
6219a747e4fSDavid du Colombier 		}
6229a747e4fSDavid du Colombier 		Bflush(&imap->bout);
6239a747e4fSDavid du Colombier 		_exits(nil);
6249a747e4fSDavid du Colombier 	}
6259a747e4fSDavid du Colombier 
62680ee5cbfSDavid du Colombier 	nnew = 0;
62780ee5cbfSDavid du Colombier 	for(m=mb->root->part; m!=nil; m=next){
62880ee5cbfSDavid du Colombier 		next = m->next;
62980ee5cbfSDavid du Colombier 		if(m->start != nil)
63080ee5cbfSDavid du Colombier 			continue;
63180ee5cbfSDavid du Colombier 
632ba64361bSDavid du Colombier 		if(!pipeline){
633ba64361bSDavid du Colombier 			Bprint(&imap->bout, "9X%lud UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
634ba64361bSDavid du Colombier 				(ulong)imap->tag, (ulong)m->imapuid);
635ba64361bSDavid du Colombier 			Bflush(&imap->bout);
636ba64361bSDavid du Colombier 		}
637ba64361bSDavid du Colombier 
63880ee5cbfSDavid du Colombier 		if(s = imap4fetch(mb, m)){
63980ee5cbfSDavid du Colombier 			// message disappeared?  unchain
64080ee5cbfSDavid du Colombier 			fprint(2, "download %lud: %s\n", (ulong)m->imapuid, s);
64180ee5cbfSDavid du Colombier 			delmessage(mb, m);
64280ee5cbfSDavid du Colombier 			mb->root->subname--;
64380ee5cbfSDavid du Colombier 			continue;
64480ee5cbfSDavid du Colombier 		}
64580ee5cbfSDavid du Colombier 		nnew++;
64680ee5cbfSDavid du Colombier 		if(doplumb)
64780ee5cbfSDavid du Colombier 			mailplumb(mb, m, 0);
64880ee5cbfSDavid du Colombier 	}
649ba64361bSDavid du Colombier 	if(pipeline)
6509a747e4fSDavid du Colombier 		waitpid();
6519a747e4fSDavid du Colombier 
652fa1160edSDavid du Colombier 	if(nnew || mb->vers == 0){
65380ee5cbfSDavid du Colombier 		mb->vers++;
6549a747e4fSDavid du Colombier 		henter(PATH(0, Qtop), mb->name,
6559a747e4fSDavid du Colombier 			(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
65680ee5cbfSDavid du Colombier 	}
65780ee5cbfSDavid du Colombier 	return nil;
65880ee5cbfSDavid du Colombier }
65980ee5cbfSDavid du Colombier 
66080ee5cbfSDavid du Colombier //
66180ee5cbfSDavid du Colombier // sync mailbox
66280ee5cbfSDavid du Colombier //
66380ee5cbfSDavid du Colombier static void
imap4purge(Imap * imap,Mailbox * mb)66480ee5cbfSDavid du Colombier imap4purge(Imap *imap, Mailbox *mb)
66580ee5cbfSDavid du Colombier {
66680ee5cbfSDavid du Colombier 	int ndel;
66780ee5cbfSDavid du Colombier 	Message *m, *next;
66880ee5cbfSDavid du Colombier 
66980ee5cbfSDavid du Colombier 	ndel = 0;
67080ee5cbfSDavid du Colombier 	for(m=mb->root->part; m!=nil; m=next){
67180ee5cbfSDavid du Colombier 		next = m->next;
67280ee5cbfSDavid du Colombier 		if(m->deleted && m->refs==0){
67380ee5cbfSDavid du Colombier 			if(m->inmbox && (ulong)(m->imapuid>>32)==imap->validity){
67480ee5cbfSDavid du Colombier 				imap4cmd(imap, "UID STORE %lud +FLAGS (\\Deleted)", (ulong)m->imapuid);
67580ee5cbfSDavid du Colombier 				if(isokay(imap4resp(imap))){
67680ee5cbfSDavid du Colombier 					ndel++;
67780ee5cbfSDavid du Colombier 					delmessage(mb, m);
67880ee5cbfSDavid du Colombier 				}
67980ee5cbfSDavid du Colombier 			}else
68080ee5cbfSDavid du Colombier 				delmessage(mb, m);
68180ee5cbfSDavid du Colombier 		}
68280ee5cbfSDavid du Colombier 	}
68380ee5cbfSDavid du Colombier 
68480ee5cbfSDavid du Colombier 	if(ndel){
68580ee5cbfSDavid du Colombier 		imap4cmd(imap, "EXPUNGE");
68680ee5cbfSDavid du Colombier 		imap4resp(imap);
68780ee5cbfSDavid du Colombier 	}
68880ee5cbfSDavid du Colombier }
68980ee5cbfSDavid du Colombier 
69080ee5cbfSDavid du Colombier //
69180ee5cbfSDavid du Colombier // connect to imap4 server, sync mailbox
69280ee5cbfSDavid du Colombier //
69380ee5cbfSDavid du Colombier static char*
imap4sync(Mailbox * mb,int doplumb)69480ee5cbfSDavid du Colombier imap4sync(Mailbox *mb, int doplumb)
69580ee5cbfSDavid du Colombier {
69680ee5cbfSDavid du Colombier 	char *err;
69780ee5cbfSDavid du Colombier 	Imap *imap;
69880ee5cbfSDavid du Colombier 
69980ee5cbfSDavid du Colombier 	imap = mb->aux;
70080ee5cbfSDavid du Colombier 
70180ee5cbfSDavid du Colombier 	if(err = imap4dial(imap)){
70280ee5cbfSDavid du Colombier 		mb->waketime = time(0) + imap->refreshtime;
70380ee5cbfSDavid du Colombier 		return err;
70480ee5cbfSDavid du Colombier 	}
70580ee5cbfSDavid du Colombier 
70680ee5cbfSDavid du Colombier 	if((err = imap4read(imap, mb, doplumb)) == nil){
70780ee5cbfSDavid du Colombier 		imap4purge(imap, mb);
7089a747e4fSDavid du Colombier 		mb->d->atime = mb->d->mtime = time(0);
70980ee5cbfSDavid du Colombier 	}
7109a747e4fSDavid du Colombier 	/*
7119a747e4fSDavid du Colombier 	 * don't hang up; leave connection open for next time.
7129a747e4fSDavid du Colombier 	 */
7139a747e4fSDavid du Colombier 	// imap4hangup(imap);
71480ee5cbfSDavid du Colombier 	mb->waketime = time(0) + imap->refreshtime;
71580ee5cbfSDavid du Colombier 	return err;
71680ee5cbfSDavid du Colombier }
71780ee5cbfSDavid du Colombier 
71880ee5cbfSDavid du Colombier static char Eimap4ctl[] = "bad imap4 control message";
71980ee5cbfSDavid du Colombier 
72080ee5cbfSDavid du Colombier static char*
imap4ctl(Mailbox * mb,int argc,char ** argv)72180ee5cbfSDavid du Colombier imap4ctl(Mailbox *mb, int argc, char **argv)
72280ee5cbfSDavid du Colombier {
72380ee5cbfSDavid du Colombier 	int n;
72480ee5cbfSDavid du Colombier 	Imap *imap;
72580ee5cbfSDavid du Colombier 
72680ee5cbfSDavid du Colombier 	imap = mb->aux;
72780ee5cbfSDavid du Colombier 	if(argc < 1)
72880ee5cbfSDavid du Colombier 		return Eimap4ctl;
72980ee5cbfSDavid du Colombier 
73080ee5cbfSDavid du Colombier 	if(argc==1 && strcmp(argv[0], "debug")==0){
73180ee5cbfSDavid du Colombier 		imap->debug = 1;
73280ee5cbfSDavid du Colombier 		return nil;
73380ee5cbfSDavid du Colombier 	}
73480ee5cbfSDavid du Colombier 
73580ee5cbfSDavid du Colombier 	if(argc==1 && strcmp(argv[0], "nodebug")==0){
73680ee5cbfSDavid du Colombier 		imap->debug = 0;
73780ee5cbfSDavid du Colombier 		return nil;
73880ee5cbfSDavid du Colombier 	}
73980ee5cbfSDavid du Colombier 
7409a747e4fSDavid du Colombier 	if(argc==1 && strcmp(argv[0], "thumbprint")==0){
7419a747e4fSDavid du Colombier 		if(imap->thumb)
7429a747e4fSDavid du Colombier 			freeThumbprints(imap->thumb);
7439a747e4fSDavid du Colombier 		imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
7449a747e4fSDavid du Colombier 	}
74580ee5cbfSDavid du Colombier 	if(strcmp(argv[0], "refresh")==0){
74680ee5cbfSDavid du Colombier 		if(argc==1){
74780ee5cbfSDavid du Colombier 			imap->refreshtime = 60;
74880ee5cbfSDavid du Colombier 			return nil;
74980ee5cbfSDavid du Colombier 		}
75080ee5cbfSDavid du Colombier 		if(argc==2){
75180ee5cbfSDavid du Colombier 			n = atoi(argv[1]);
75280ee5cbfSDavid du Colombier 			if(n < 15)
75380ee5cbfSDavid du Colombier 				return Eimap4ctl;
75480ee5cbfSDavid du Colombier 			imap->refreshtime = n;
75580ee5cbfSDavid du Colombier 			return nil;
75680ee5cbfSDavid du Colombier 		}
75780ee5cbfSDavid du Colombier 	}
75880ee5cbfSDavid du Colombier 
75980ee5cbfSDavid du Colombier 	return Eimap4ctl;
76080ee5cbfSDavid du Colombier }
76180ee5cbfSDavid du Colombier 
76280ee5cbfSDavid du Colombier //
76380ee5cbfSDavid du Colombier // free extra memory associated with mb
76480ee5cbfSDavid du Colombier //
76580ee5cbfSDavid du Colombier static void
imap4close(Mailbox * mb)76680ee5cbfSDavid du Colombier imap4close(Mailbox *mb)
76780ee5cbfSDavid du Colombier {
76880ee5cbfSDavid du Colombier 	Imap *imap;
76980ee5cbfSDavid du Colombier 
77080ee5cbfSDavid du Colombier 	imap = mb->aux;
77180ee5cbfSDavid du Colombier 	free(imap->freep);
77280ee5cbfSDavid du Colombier 	free(imap->base);
77380ee5cbfSDavid du Colombier 	free(imap->uid);
774f7bdc0bcSDavid du Colombier 	if(imap->fd >= 0)
775f7bdc0bcSDavid du Colombier 		close(imap->fd);
77680ee5cbfSDavid du Colombier 	free(imap);
77780ee5cbfSDavid du Colombier }
77880ee5cbfSDavid du Colombier 
77980ee5cbfSDavid du Colombier //
78080ee5cbfSDavid du Colombier // open mailboxes of the form /imap/host/user
78180ee5cbfSDavid du Colombier //
78280ee5cbfSDavid du Colombier char*
imap4mbox(Mailbox * mb,char * path)78380ee5cbfSDavid du Colombier imap4mbox(Mailbox *mb, char *path)
78480ee5cbfSDavid du Colombier {
78580ee5cbfSDavid du Colombier 	char *f[10];
7869a747e4fSDavid du Colombier 	int mustssl, nf;
78780ee5cbfSDavid du Colombier 	Imap *imap;
78880ee5cbfSDavid du Colombier 
7899a747e4fSDavid du Colombier 	quotefmtinstall();
7903ff48bf5SDavid du Colombier 	fmtinstall('Z', doublequote);
7919a747e4fSDavid du Colombier 	if(strncmp(path, "/imap/", 6) != 0 && strncmp(path, "/imaps/", 7) != 0)
79280ee5cbfSDavid du Colombier 		return Enotme;
7939a747e4fSDavid du Colombier 	mustssl = (strncmp(path, "/imaps/", 7) == 0);
79480ee5cbfSDavid du Colombier 
79580ee5cbfSDavid du Colombier 	path = strdup(path);
79680ee5cbfSDavid du Colombier 	if(path == nil)
79780ee5cbfSDavid du Colombier 		return "out of memory";
79880ee5cbfSDavid du Colombier 
79980ee5cbfSDavid du Colombier 	nf = getfields(path, f, 5, 0, "/");
8009a747e4fSDavid du Colombier 	if(nf < 3){
80180ee5cbfSDavid du Colombier 		free(path);
8029a747e4fSDavid du Colombier 		return "bad imap path syntax /imap[s]/system[/user[/mailbox]]";
80380ee5cbfSDavid du Colombier 	}
80480ee5cbfSDavid du Colombier 
80580ee5cbfSDavid du Colombier 	imap = emalloc(sizeof(*imap));
8069a747e4fSDavid du Colombier 	imap->fd = -1;
807fa1160edSDavid du Colombier 	imap->debug = debug;
80880ee5cbfSDavid du Colombier 	imap->freep = path;
8099a747e4fSDavid du Colombier 	imap->mustssl = mustssl;
81080ee5cbfSDavid du Colombier 	imap->host = f[2];
8119a747e4fSDavid du Colombier 	if(nf < 4)
8129a747e4fSDavid du Colombier 		imap->user = nil;
81380ee5cbfSDavid du Colombier 	else
8149a747e4fSDavid du Colombier 		imap->user = f[3];
8159a747e4fSDavid du Colombier 	if(nf < 5)
81680ee5cbfSDavid du Colombier 		imap->mbox = "Inbox";
8179a747e4fSDavid du Colombier 	else
8189a747e4fSDavid du Colombier 		imap->mbox = f[4];
8199a747e4fSDavid du Colombier 	imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
82080ee5cbfSDavid du Colombier 
82180ee5cbfSDavid du Colombier 	mb->aux = imap;
82280ee5cbfSDavid du Colombier 	mb->sync = imap4sync;
82380ee5cbfSDavid du Colombier 	mb->close = imap4close;
82480ee5cbfSDavid du Colombier 	mb->ctl = imap4ctl;
8259a747e4fSDavid du Colombier 	mb->d = emalloc(sizeof(*mb->d));
82680ee5cbfSDavid du Colombier 	//mb->fetch = imap4fetch;
82780ee5cbfSDavid du Colombier 
82880ee5cbfSDavid du Colombier 	return nil;
82980ee5cbfSDavid du Colombier }
8303ff48bf5SDavid du Colombier 
8313ff48bf5SDavid du Colombier //
8323ff48bf5SDavid du Colombier // Formatter for %"
8333ff48bf5SDavid du Colombier // Use double quotes to protect white space, frogs, \ and "
8343ff48bf5SDavid du Colombier //
8353ff48bf5SDavid du Colombier enum
8363ff48bf5SDavid du Colombier {
8373ff48bf5SDavid du Colombier 	Qok = 0,
8383ff48bf5SDavid du Colombier 	Qquote,
8393ff48bf5SDavid du Colombier 	Qbackslash,
8403ff48bf5SDavid du Colombier };
8413ff48bf5SDavid du Colombier 
8423ff48bf5SDavid du Colombier static int
needtoquote(Rune r)8433ff48bf5SDavid du Colombier needtoquote(Rune r)
8443ff48bf5SDavid du Colombier {
8453ff48bf5SDavid du Colombier 	if(r >= Runeself)
8463ff48bf5SDavid du Colombier 		return Qquote;
8473ff48bf5SDavid du Colombier 	if(r <= ' ')
8483ff48bf5SDavid du Colombier 		return Qquote;
8493ff48bf5SDavid du Colombier 	if(r=='\\' || r=='"')
8503ff48bf5SDavid du Colombier 		return Qbackslash;
8513ff48bf5SDavid du Colombier 	return Qok;
8523ff48bf5SDavid du Colombier }
8533ff48bf5SDavid du Colombier 
8543ff48bf5SDavid du Colombier int
doublequote(Fmt * f)8553ff48bf5SDavid du Colombier doublequote(Fmt *f)
8563ff48bf5SDavid du Colombier {
8573ff48bf5SDavid du Colombier 	char *s, *t;
8583ff48bf5SDavid du Colombier 	int w, quotes;
8593ff48bf5SDavid du Colombier 	Rune r;
8603ff48bf5SDavid du Colombier 
8613ff48bf5SDavid du Colombier 	s = va_arg(f->args, char*);
8623ff48bf5SDavid du Colombier 	if(s == nil || *s == '\0')
8633ff48bf5SDavid du Colombier 		return fmtstrcpy(f, "\"\"");
8643ff48bf5SDavid du Colombier 
8653ff48bf5SDavid du Colombier 	quotes = 0;
8663ff48bf5SDavid du Colombier 	for(t=s; *t; t+=w){
8673ff48bf5SDavid du Colombier 		w = chartorune(&r, t);
8683ff48bf5SDavid du Colombier 		quotes |= needtoquote(r);
8693ff48bf5SDavid du Colombier 	}
8703ff48bf5SDavid du Colombier 	if(quotes == 0)
8713ff48bf5SDavid du Colombier 		return fmtstrcpy(f, s);
8723ff48bf5SDavid du Colombier 
8733ff48bf5SDavid du Colombier 	fmtrune(f, '"');
8743ff48bf5SDavid du Colombier 	for(t=s; *t; t+=w){
8753ff48bf5SDavid du Colombier 		w = chartorune(&r, t);
8763ff48bf5SDavid du Colombier 		if(needtoquote(r) == Qbackslash)
8773ff48bf5SDavid du Colombier 			fmtrune(f, '\\');
8783ff48bf5SDavid du Colombier 		fmtrune(f, r);
8793ff48bf5SDavid du Colombier 	}
8803ff48bf5SDavid du Colombier 	return fmtrune(f, '"');
8813ff48bf5SDavid du Colombier }
882