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