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 type "M" uchar* 980ee5cbfSDavid du Colombier #pragma varargck argpos pop3cmd 2 1080ee5cbfSDavid du Colombier 1180ee5cbfSDavid du Colombier typedef struct Pop Pop; 1280ee5cbfSDavid du Colombier struct Pop { 1380ee5cbfSDavid du Colombier char *freep; // free this to free the strings below 1480ee5cbfSDavid du Colombier 1580ee5cbfSDavid du Colombier char *host; 1680ee5cbfSDavid du Colombier char *user; 17b27b55e2SDavid du Colombier char *port; 1880ee5cbfSDavid du Colombier 1980ee5cbfSDavid du Colombier int ppop; 2080ee5cbfSDavid du Colombier int refreshtime; 2180ee5cbfSDavid du Colombier int debug; 229a747e4fSDavid du Colombier int pipeline; 23b27b55e2SDavid du Colombier int encrypted; 249a747e4fSDavid du Colombier int needtls; 2539734e7eSDavid du Colombier int notls; 26b27b55e2SDavid du Colombier int needssl; 2780ee5cbfSDavid du Colombier 289a747e4fSDavid du Colombier // open network connection 299a747e4fSDavid du Colombier Biobuf bin; 309a747e4fSDavid du Colombier Biobuf bout; 3180ee5cbfSDavid du Colombier int fd; 32*3b6af982SDavid du Colombier char *lastline; // from Brdstr 339a747e4fSDavid du Colombier 349a747e4fSDavid du Colombier Thumbprint *thumb; 3580ee5cbfSDavid du Colombier }; 3680ee5cbfSDavid du Colombier 3780ee5cbfSDavid du Colombier char* 3880ee5cbfSDavid du Colombier geterrstr(void) 3980ee5cbfSDavid du Colombier { 409a747e4fSDavid du Colombier static char err[64]; 4180ee5cbfSDavid du Colombier 4280ee5cbfSDavid du Colombier err[0] = '\0'; 439a747e4fSDavid du Colombier errstr(err, sizeof(err)); 4480ee5cbfSDavid du Colombier return err; 4580ee5cbfSDavid du Colombier } 4680ee5cbfSDavid du Colombier 4780ee5cbfSDavid du Colombier // 4880ee5cbfSDavid du Colombier // get pop3 response line , without worrying 4980ee5cbfSDavid du Colombier // about multiline responses; the clients 5080ee5cbfSDavid du Colombier // will deal with that. 5180ee5cbfSDavid du Colombier // 5280ee5cbfSDavid du Colombier static int 5380ee5cbfSDavid du Colombier isokay(char *s) 5480ee5cbfSDavid du Colombier { 5580ee5cbfSDavid du Colombier return s!=nil && strncmp(s, "+OK", 3)==0; 5680ee5cbfSDavid du Colombier } 5780ee5cbfSDavid du Colombier 5880ee5cbfSDavid du Colombier static void 5980ee5cbfSDavid du Colombier pop3cmd(Pop *pop, char *fmt, ...) 6080ee5cbfSDavid du Colombier { 6180ee5cbfSDavid du Colombier char buf[128], *p; 6280ee5cbfSDavid du Colombier va_list va; 6380ee5cbfSDavid du Colombier 6480ee5cbfSDavid du Colombier va_start(va, fmt); 659a747e4fSDavid du Colombier vseprint(buf, buf+sizeof(buf), fmt, va); 6680ee5cbfSDavid du Colombier va_end(va); 6780ee5cbfSDavid du Colombier 6880ee5cbfSDavid du Colombier p = buf+strlen(buf); 6980ee5cbfSDavid du Colombier if(p > (buf+sizeof(buf)-3)) 7080ee5cbfSDavid du Colombier sysfatal("pop3 command too long"); 7180ee5cbfSDavid du Colombier 7280ee5cbfSDavid du Colombier if(pop->debug) 7380ee5cbfSDavid du Colombier fprint(2, "<- %s\n", buf); 7480ee5cbfSDavid du Colombier strcpy(p, "\r\n"); 759a747e4fSDavid du Colombier Bwrite(&pop->bout, buf, strlen(buf)); 769a747e4fSDavid du Colombier Bflush(&pop->bout); 7780ee5cbfSDavid du Colombier } 7880ee5cbfSDavid du Colombier 7980ee5cbfSDavid du Colombier static char* 8080ee5cbfSDavid du Colombier pop3resp(Pop *pop) 8180ee5cbfSDavid du Colombier { 8280ee5cbfSDavid du Colombier char *s; 8380ee5cbfSDavid du Colombier char *p; 8480ee5cbfSDavid du Colombier 8539734e7eSDavid du Colombier alarm(60*1000); 86*3b6af982SDavid du Colombier if((s = Brdstr(&pop->bin, '\n', 0)) == nil){ 8739734e7eSDavid du Colombier close(pop->fd); 8839734e7eSDavid du Colombier pop->fd = -1; 8939734e7eSDavid du Colombier alarm(0); 90fb7f0c93SDavid du Colombier return "unexpected eof"; 9139734e7eSDavid du Colombier } 9239734e7eSDavid du Colombier alarm(0); 9380ee5cbfSDavid du Colombier 94*3b6af982SDavid du Colombier p = s+strlen(s)-1; 9580ee5cbfSDavid du Colombier while(p >= s && (*p == '\r' || *p == '\n')) 9680ee5cbfSDavid du Colombier *p-- = '\0'; 9780ee5cbfSDavid du Colombier 9880ee5cbfSDavid du Colombier if(pop->debug) 9980ee5cbfSDavid du Colombier fprint(2, "-> %s\n", s); 100*3b6af982SDavid du Colombier free(pop->lastline); 101*3b6af982SDavid du Colombier pop->lastline = s; 10280ee5cbfSDavid du Colombier return s; 10380ee5cbfSDavid du Colombier } 10480ee5cbfSDavid du Colombier 1057a02f3c0SDavid du Colombier static int 1067a02f3c0SDavid du Colombier pop3log(char *fmt, ...) 1077a02f3c0SDavid du Colombier { 1087a02f3c0SDavid du Colombier va_list ap; 1097a02f3c0SDavid du Colombier 1107a02f3c0SDavid du Colombier va_start(ap,fmt); 1117a02f3c0SDavid du Colombier syslog(0, "/sys/log/pop3", fmt, ap); 1127a02f3c0SDavid du Colombier va_end(ap); 1137a02f3c0SDavid du Colombier return 0; 1147a02f3c0SDavid du Colombier } 1157a02f3c0SDavid du Colombier 116b27b55e2SDavid du Colombier static char* 117b27b55e2SDavid du Colombier pop3pushtls(Pop *pop) 118b27b55e2SDavid du Colombier { 119b27b55e2SDavid du Colombier int fd; 120b27b55e2SDavid du Colombier uchar digest[SHA1dlen]; 121b27b55e2SDavid du Colombier TLSconn conn; 122b27b55e2SDavid du Colombier 123b27b55e2SDavid du Colombier memset(&conn, 0, sizeof conn); 124b27b55e2SDavid du Colombier // conn.trace = pop3log; 125b27b55e2SDavid du Colombier fd = tlsClient(pop->fd, &conn); 126b27b55e2SDavid du Colombier if(fd < 0) 127b27b55e2SDavid du Colombier return "tls error"; 128b27b55e2SDavid du Colombier if(conn.cert==nil || conn.certlen <= 0){ 129b27b55e2SDavid du Colombier close(fd); 130b27b55e2SDavid du Colombier return "server did not provide TLS certificate"; 131b27b55e2SDavid du Colombier } 132b27b55e2SDavid du Colombier sha1(conn.cert, conn.certlen, digest, nil); 133b27b55e2SDavid du Colombier if(!pop->thumb || !okThumbprint(digest, pop->thumb)){ 134b27b55e2SDavid du Colombier fmtinstall('H', encodefmt); 135b27b55e2SDavid du Colombier close(fd); 136b27b55e2SDavid du Colombier free(conn.cert); 137b27b55e2SDavid du Colombier fprint(2, "upas/fs pop3: server certificate %.*H not recognized\n", SHA1dlen, digest); 138b27b55e2SDavid du Colombier return "bad server certificate"; 139b27b55e2SDavid du Colombier } 140b27b55e2SDavid du Colombier free(conn.cert); 141b27b55e2SDavid du Colombier close(pop->fd); 142b27b55e2SDavid du Colombier pop->fd = fd; 143b27b55e2SDavid du Colombier pop->encrypted = 1; 144b27b55e2SDavid du Colombier Binit(&pop->bin, pop->fd, OREAD); 145b27b55e2SDavid du Colombier Binit(&pop->bout, pop->fd, OWRITE); 146b27b55e2SDavid du Colombier return nil; 147b27b55e2SDavid du Colombier } 1487a02f3c0SDavid du Colombier 14980ee5cbfSDavid du Colombier // 1509a747e4fSDavid du Colombier // get capability list, possibly start tls 1519a747e4fSDavid du Colombier // 1529a747e4fSDavid du Colombier static char* 1539a747e4fSDavid du Colombier pop3capa(Pop *pop) 1549a747e4fSDavid du Colombier { 1559a747e4fSDavid du Colombier char *s; 1569a747e4fSDavid du Colombier int hastls; 1579a747e4fSDavid du Colombier 1589a747e4fSDavid du Colombier pop3cmd(pop, "CAPA"); 1599a747e4fSDavid du Colombier if(!isokay(pop3resp(pop))) 1609a747e4fSDavid du Colombier return nil; 1619a747e4fSDavid du Colombier 1629a747e4fSDavid du Colombier hastls = 0; 163dc5a79c1SDavid du Colombier for(;;){ 164dc5a79c1SDavid du Colombier s = pop3resp(pop); 165fb7f0c93SDavid du Colombier if(strcmp(s, ".") == 0 || strcmp(s, "unexpected eof") == 0) 1669a747e4fSDavid du Colombier break; 1679a747e4fSDavid du Colombier if(strcmp(s, "STLS") == 0) 1689a747e4fSDavid du Colombier hastls = 1; 1699a747e4fSDavid du Colombier if(strcmp(s, "PIPELINING") == 0) 1709a747e4fSDavid du Colombier pop->pipeline = 1; 1719a747e4fSDavid du Colombier } 1729a747e4fSDavid du Colombier 17339734e7eSDavid du Colombier if(hastls && !pop->notls){ 1749a747e4fSDavid du Colombier pop3cmd(pop, "STLS"); 1759a747e4fSDavid du Colombier if(!isokay(s = pop3resp(pop))) 1769a747e4fSDavid du Colombier return s; 177b27b55e2SDavid du Colombier if((s = pop3pushtls(pop)) != nil) 178b27b55e2SDavid du Colombier return s; 1799a747e4fSDavid du Colombier } 1809a747e4fSDavid du Colombier return nil; 1819a747e4fSDavid du Colombier } 1829a747e4fSDavid du Colombier 1839a747e4fSDavid du Colombier // 18480ee5cbfSDavid du Colombier // log in using APOP if possible, password if allowed by user 18580ee5cbfSDavid du Colombier // 18680ee5cbfSDavid du Colombier static char* 18780ee5cbfSDavid du Colombier pop3login(Pop *pop) 18880ee5cbfSDavid du Colombier { 1899a747e4fSDavid du Colombier int n; 19080ee5cbfSDavid du Colombier char *s, *p, *q; 1919a747e4fSDavid du Colombier char ubuf[128], user[128]; 19280ee5cbfSDavid du Colombier char buf[500]; 1939a747e4fSDavid du Colombier UserPasswd *up; 19480ee5cbfSDavid du Colombier 19580ee5cbfSDavid du Colombier s = pop3resp(pop); 19680ee5cbfSDavid du Colombier if(!isokay(s)) 19780ee5cbfSDavid du Colombier return "error in initial handshake"; 19880ee5cbfSDavid du Colombier 1999a747e4fSDavid du Colombier if(pop->user) 2009a747e4fSDavid du Colombier snprint(ubuf, sizeof ubuf, " user=%q", pop->user); 2019a747e4fSDavid du Colombier else 2029a747e4fSDavid du Colombier ubuf[0] = '\0'; 2039a747e4fSDavid du Colombier 20480ee5cbfSDavid du Colombier // look for apop banner 2059a747e4fSDavid du Colombier if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) { 20680ee5cbfSDavid du Colombier *++q = '\0'; 2079a747e4fSDavid du Colombier if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s", 2089a747e4fSDavid du Colombier pop->host, ubuf)) < 0) 2099a747e4fSDavid du Colombier return "factotum failed"; 2109a747e4fSDavid du Colombier if(user[0]=='\0') 2119a747e4fSDavid du Colombier return "factotum did not return a user name"; 21280ee5cbfSDavid du Colombier 2139a747e4fSDavid du Colombier if(s = pop3capa(pop)) 2149a747e4fSDavid du Colombier return s; 21580ee5cbfSDavid du Colombier 2169a747e4fSDavid du Colombier pop3cmd(pop, "APOP %s %.*s", user, n, buf); 21780ee5cbfSDavid du Colombier if(!isokay(s = pop3resp(pop))) 21880ee5cbfSDavid du Colombier return s; 21980ee5cbfSDavid du Colombier 22080ee5cbfSDavid du Colombier return nil; 22180ee5cbfSDavid du Colombier } else { 22280ee5cbfSDavid du Colombier if(pop->ppop == 0) 22380ee5cbfSDavid du Colombier return "no APOP hdr from server"; 22480ee5cbfSDavid du Colombier 2259a747e4fSDavid du Colombier if(s = pop3capa(pop)) 22680ee5cbfSDavid du Colombier return s; 22780ee5cbfSDavid du Colombier 228b27b55e2SDavid du Colombier if(pop->needtls && !pop->encrypted) 2299a747e4fSDavid du Colombier return "could not negotiate TLS"; 23080ee5cbfSDavid du Colombier 2319a747e4fSDavid du Colombier up = auth_getuserpasswd(auth_getkey, "proto=pass service=pop dom=%q%s", 2329a747e4fSDavid du Colombier pop->host, ubuf); 2339a747e4fSDavid du Colombier if(up == nil) 2349a747e4fSDavid du Colombier return "no usable keys found"; 2359a747e4fSDavid du Colombier 2369a747e4fSDavid du Colombier pop3cmd(pop, "USER %s", up->user); 2379a747e4fSDavid du Colombier if(!isokay(s = pop3resp(pop))){ 2389a747e4fSDavid du Colombier free(up); 2399a747e4fSDavid du Colombier return s; 2409a747e4fSDavid du Colombier } 2419a747e4fSDavid du Colombier pop3cmd(pop, "PASS %s", up->passwd); 2429a747e4fSDavid du Colombier free(up); 24380ee5cbfSDavid du Colombier if(!isokay(s = pop3resp(pop))) 24480ee5cbfSDavid du Colombier return s; 24580ee5cbfSDavid du Colombier 24680ee5cbfSDavid du Colombier return nil; 24780ee5cbfSDavid du Colombier } 24880ee5cbfSDavid du Colombier } 24980ee5cbfSDavid du Colombier 25080ee5cbfSDavid du Colombier // 25180ee5cbfSDavid du Colombier // dial and handshake with pop server 25280ee5cbfSDavid du Colombier // 25380ee5cbfSDavid du Colombier static char* 25480ee5cbfSDavid du Colombier pop3dial(Pop *pop) 25580ee5cbfSDavid du Colombier { 25680ee5cbfSDavid du Colombier char *err; 25780ee5cbfSDavid du Colombier 258b27b55e2SDavid du Colombier if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0) 25980ee5cbfSDavid du Colombier return geterrstr(); 26080ee5cbfSDavid du Colombier 261b27b55e2SDavid du Colombier if(pop->needssl){ 262b27b55e2SDavid du Colombier if((err = pop3pushtls(pop)) != nil) 263b27b55e2SDavid du Colombier return err; 264b27b55e2SDavid du Colombier }else{ 2659a747e4fSDavid du Colombier Binit(&pop->bin, pop->fd, OREAD); 2669a747e4fSDavid du Colombier Binit(&pop->bout, pop->fd, OWRITE); 267b27b55e2SDavid du Colombier } 26880ee5cbfSDavid du Colombier 26980ee5cbfSDavid du Colombier if(err = pop3login(pop)) { 27080ee5cbfSDavid du Colombier close(pop->fd); 27180ee5cbfSDavid du Colombier return err; 27280ee5cbfSDavid du Colombier } 27380ee5cbfSDavid du Colombier 27480ee5cbfSDavid du Colombier return nil; 27580ee5cbfSDavid du Colombier } 27680ee5cbfSDavid du Colombier 27780ee5cbfSDavid du Colombier // 27880ee5cbfSDavid du Colombier // close connection 27980ee5cbfSDavid du Colombier // 28080ee5cbfSDavid du Colombier static void 28180ee5cbfSDavid du Colombier pop3hangup(Pop *pop) 28280ee5cbfSDavid du Colombier { 28380ee5cbfSDavid du Colombier pop3cmd(pop, "QUIT"); 28480ee5cbfSDavid du Colombier pop3resp(pop); 28580ee5cbfSDavid du Colombier close(pop->fd); 28680ee5cbfSDavid du Colombier } 28780ee5cbfSDavid du Colombier 28880ee5cbfSDavid du Colombier // 28980ee5cbfSDavid du Colombier // download a single message 29080ee5cbfSDavid du Colombier // 29180ee5cbfSDavid du Colombier static char* 29280ee5cbfSDavid du Colombier pop3download(Pop *pop, Message *m) 29380ee5cbfSDavid du Colombier { 29480ee5cbfSDavid du Colombier char *s, *f[3], *wp, *ep; 29580ee5cbfSDavid du Colombier char sdigest[SHA1dlen*2+1]; 29680ee5cbfSDavid du Colombier int i, l, sz; 29780ee5cbfSDavid du Colombier 2989a747e4fSDavid du Colombier if(!pop->pipeline) 29980ee5cbfSDavid du Colombier pop3cmd(pop, "LIST %d", m->mesgno); 30080ee5cbfSDavid du Colombier if(!isokay(s = pop3resp(pop))) 30180ee5cbfSDavid du Colombier return s; 30280ee5cbfSDavid du Colombier 30380ee5cbfSDavid du Colombier if(tokenize(s, f, 3) != 3) 30480ee5cbfSDavid du Colombier return "syntax error in LIST response"; 30580ee5cbfSDavid du Colombier 30680ee5cbfSDavid du Colombier if(atoi(f[1]) != m->mesgno) 30780ee5cbfSDavid du Colombier return "out of sync with pop3 server"; 30880ee5cbfSDavid du Colombier 30980ee5cbfSDavid du Colombier sz = atoi(f[2])+200; /* 200 because the plan9 pop3 server lies */ 31080ee5cbfSDavid du Colombier if(sz == 0) 31180ee5cbfSDavid du Colombier return "invalid size in LIST response"; 31280ee5cbfSDavid du Colombier 31380ee5cbfSDavid du Colombier m->start = wp = emalloc(sz+1); 31480ee5cbfSDavid du Colombier ep = wp+sz; 31580ee5cbfSDavid du Colombier 3169a747e4fSDavid du Colombier if(!pop->pipeline) 31780ee5cbfSDavid du Colombier pop3cmd(pop, "RETR %d", m->mesgno); 31880ee5cbfSDavid du Colombier if(!isokay(s = pop3resp(pop))) { 31980ee5cbfSDavid du Colombier m->start = nil; 32080ee5cbfSDavid du Colombier free(wp); 32180ee5cbfSDavid du Colombier return s; 32280ee5cbfSDavid du Colombier } 32380ee5cbfSDavid du Colombier 32480ee5cbfSDavid du Colombier s = nil; 32580ee5cbfSDavid du Colombier while(wp <= ep) { 32680ee5cbfSDavid du Colombier s = pop3resp(pop); 327dc5a79c1SDavid du Colombier if(strcmp(s, "unexpected eof") == 0) { 32880ee5cbfSDavid du Colombier free(m->start); 32980ee5cbfSDavid du Colombier m->start = nil; 33080ee5cbfSDavid du Colombier return "unexpected end of conversation"; 33180ee5cbfSDavid du Colombier } 33280ee5cbfSDavid du Colombier if(strcmp(s, ".") == 0) 33380ee5cbfSDavid du Colombier break; 33480ee5cbfSDavid du Colombier 33580ee5cbfSDavid du Colombier l = strlen(s)+1; 33680ee5cbfSDavid du Colombier if(s[0] == '.') { 33780ee5cbfSDavid du Colombier s++; 33880ee5cbfSDavid du Colombier l--; 33980ee5cbfSDavid du Colombier } 34015b88db3SDavid du Colombier /* 34115b88db3SDavid du Colombier * grow by 10%/200bytes - some servers 34215b88db3SDavid du Colombier * lie about message sizes 34315b88db3SDavid du Colombier */ 34480ee5cbfSDavid du Colombier if(wp+l > ep) { 34515b88db3SDavid du Colombier int pos = wp - m->start; 34615b88db3SDavid du Colombier sz += ((sz / 10) < 200)? 200: sz/10; 34715b88db3SDavid du Colombier m->start = erealloc(m->start, sz+1); 34815b88db3SDavid du Colombier wp = m->start+pos; 34915b88db3SDavid du Colombier ep = m->start+sz; 35080ee5cbfSDavid du Colombier } 35180ee5cbfSDavid du Colombier memmove(wp, s, l-1); 35280ee5cbfSDavid du Colombier wp[l-1] = '\n'; 35380ee5cbfSDavid du Colombier wp += l; 35480ee5cbfSDavid du Colombier } 35580ee5cbfSDavid du Colombier 35680ee5cbfSDavid du Colombier if(s == nil || strcmp(s, ".") != 0) 35780ee5cbfSDavid du Colombier return "out of sync with pop3 server"; 35880ee5cbfSDavid du Colombier 35980ee5cbfSDavid du Colombier m->end = wp; 36080ee5cbfSDavid du Colombier 36180ee5cbfSDavid du Colombier // make sure there's a trailing null 36280ee5cbfSDavid du Colombier // (helps in body searches) 36380ee5cbfSDavid du Colombier *m->end = 0; 36480ee5cbfSDavid du Colombier m->bend = m->rbend = m->end; 36580ee5cbfSDavid du Colombier m->header = m->start; 36680ee5cbfSDavid du Colombier 36780ee5cbfSDavid du Colombier // digest message 36880ee5cbfSDavid du Colombier sha1((uchar*)m->start, m->end - m->start, m->digest, nil); 36980ee5cbfSDavid du Colombier for(i = 0; i < SHA1dlen; i++) 37080ee5cbfSDavid du Colombier sprint(sdigest+2*i, "%2.2ux", m->digest[i]); 37180ee5cbfSDavid du Colombier m->sdigest = s_copy(sdigest); 37280ee5cbfSDavid du Colombier 37380ee5cbfSDavid du Colombier return nil; 37480ee5cbfSDavid du Colombier } 37580ee5cbfSDavid du Colombier 37680ee5cbfSDavid du Colombier // 37780ee5cbfSDavid du Colombier // check for new messages on pop server 37880ee5cbfSDavid du Colombier // UIDL is not required by RFC 1939, but 37980ee5cbfSDavid du Colombier // netscape requires it, so almost every server supports it. 38080ee5cbfSDavid du Colombier // we'll use it to make our lives easier. 38180ee5cbfSDavid du Colombier // 38280ee5cbfSDavid du Colombier static char* 38380ee5cbfSDavid du Colombier pop3read(Pop *pop, Mailbox *mb, int doplumb) 38480ee5cbfSDavid du Colombier { 38580ee5cbfSDavid du Colombier char *s, *p, *uidl, *f[2]; 38680ee5cbfSDavid du Colombier int mesgno, ignore, nnew; 38780ee5cbfSDavid du Colombier Message *m, *next, **l; 38880ee5cbfSDavid du Colombier 3899a747e4fSDavid du Colombier // Some POP servers disallow UIDL if the maildrop is empty. 3909a747e4fSDavid du Colombier pop3cmd(pop, "STAT"); 39180ee5cbfSDavid du Colombier if(!isokay(s = pop3resp(pop))) 39280ee5cbfSDavid du Colombier return s; 39380ee5cbfSDavid du Colombier 39480ee5cbfSDavid du Colombier // fetch message listing; note messages to grab 39580ee5cbfSDavid du Colombier l = &mb->root->part; 3969a747e4fSDavid du Colombier if(strncmp(s, "+OK 0 ", 6) != 0) { 3979a747e4fSDavid du Colombier pop3cmd(pop, "UIDL"); 3989a747e4fSDavid du Colombier if(!isokay(s = pop3resp(pop))) 3999a747e4fSDavid du Colombier return s; 4009a747e4fSDavid du Colombier 401dc5a79c1SDavid du Colombier for(;;){ 402dc5a79c1SDavid du Colombier p = pop3resp(pop); 403dc5a79c1SDavid du Colombier if(strcmp(p, ".") == 0 || strcmp(p, "unexpected eof") == 0) 40480ee5cbfSDavid du Colombier break; 40580ee5cbfSDavid du Colombier 40680ee5cbfSDavid du Colombier if(tokenize(p, f, 2) != 2) 40780ee5cbfSDavid du Colombier continue; 40880ee5cbfSDavid du Colombier 40980ee5cbfSDavid du Colombier mesgno = atoi(f[0]); 41080ee5cbfSDavid du Colombier uidl = f[1]; 41180ee5cbfSDavid du Colombier if(strlen(uidl) > 75) // RFC 1939 says 70 characters max 41280ee5cbfSDavid du Colombier continue; 41380ee5cbfSDavid du Colombier 41480ee5cbfSDavid du Colombier ignore = 0; 41580ee5cbfSDavid du Colombier while(*l != nil) { 41680ee5cbfSDavid du Colombier if(strcmp((*l)->uidl, uidl) == 0) { 41780ee5cbfSDavid du Colombier // matches mail we already have, note mesgno for deletion 41880ee5cbfSDavid du Colombier (*l)->mesgno = mesgno; 41980ee5cbfSDavid du Colombier ignore = 1; 42080ee5cbfSDavid du Colombier l = &(*l)->next; 42180ee5cbfSDavid du Colombier break; 42280ee5cbfSDavid du Colombier } else { 42380ee5cbfSDavid du Colombier // old mail no longer in box mark deleted 42480ee5cbfSDavid du Colombier if(doplumb) 42580ee5cbfSDavid du Colombier mailplumb(mb, *l, 1); 42680ee5cbfSDavid du Colombier (*l)->inmbox = 0; 42780ee5cbfSDavid du Colombier (*l)->deleted = 1; 42880ee5cbfSDavid du Colombier l = &(*l)->next; 42980ee5cbfSDavid du Colombier } 43080ee5cbfSDavid du Colombier } 43180ee5cbfSDavid du Colombier if(ignore) 43280ee5cbfSDavid du Colombier continue; 43380ee5cbfSDavid du Colombier 43480ee5cbfSDavid du Colombier m = newmessage(mb->root); 43580ee5cbfSDavid du Colombier m->mallocd = 1; 43680ee5cbfSDavid du Colombier m->inmbox = 1; 43780ee5cbfSDavid du Colombier m->mesgno = mesgno; 43880ee5cbfSDavid du Colombier strcpy(m->uidl, uidl); 43980ee5cbfSDavid du Colombier 44080ee5cbfSDavid du Colombier // chain in; will fill in message later 44180ee5cbfSDavid du Colombier *l = m; 44280ee5cbfSDavid du Colombier l = &m->next; 44380ee5cbfSDavid du Colombier } 4449a747e4fSDavid du Colombier } 44580ee5cbfSDavid du Colombier 44680ee5cbfSDavid du Colombier // whatever is left has been removed from the mbox, mark as deleted 44780ee5cbfSDavid du Colombier while(*l != nil) { 44880ee5cbfSDavid du Colombier if(doplumb) 44980ee5cbfSDavid du Colombier mailplumb(mb, *l, 1); 45080ee5cbfSDavid du Colombier (*l)->inmbox = 0; 45180ee5cbfSDavid du Colombier (*l)->deleted = 1; 45280ee5cbfSDavid du Colombier l = &(*l)->next; 45380ee5cbfSDavid du Colombier } 45480ee5cbfSDavid du Colombier 45580ee5cbfSDavid du Colombier // download new messages 45680ee5cbfSDavid du Colombier nnew = 0; 4579a747e4fSDavid du Colombier if(pop->pipeline){ 4589a747e4fSDavid du Colombier switch(rfork(RFPROC|RFMEM)){ 4599a747e4fSDavid du Colombier case -1: 4609a747e4fSDavid du Colombier fprint(2, "rfork: %r\n"); 4619a747e4fSDavid du Colombier pop->pipeline = 0; 4629a747e4fSDavid du Colombier 4639a747e4fSDavid du Colombier default: 4649a747e4fSDavid du Colombier break; 4659a747e4fSDavid du Colombier 4669a747e4fSDavid du Colombier case 0: 4679a747e4fSDavid du Colombier for(m = mb->root->part; m != nil; m = m->next){ 4689a747e4fSDavid du Colombier if(m->start != nil) 4699a747e4fSDavid du Colombier continue; 4709a747e4fSDavid du Colombier Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno); 4719a747e4fSDavid du Colombier } 4729a747e4fSDavid du Colombier Bflush(&pop->bout); 4739a747e4fSDavid du Colombier _exits(nil); 4749a747e4fSDavid du Colombier } 4759a747e4fSDavid du Colombier } 4769a747e4fSDavid du Colombier 47780ee5cbfSDavid du Colombier for(m = mb->root->part; m != nil; m = next) { 47880ee5cbfSDavid du Colombier next = m->next; 47980ee5cbfSDavid du Colombier 48080ee5cbfSDavid du Colombier if(m->start != nil) 48180ee5cbfSDavid du Colombier continue; 48280ee5cbfSDavid du Colombier 48380ee5cbfSDavid du Colombier if(s = pop3download(pop, m)) { 48480ee5cbfSDavid du Colombier // message disappeared? unchain 48580ee5cbfSDavid du Colombier fprint(2, "download %d: %s\n", m->mesgno, s); 48680ee5cbfSDavid du Colombier delmessage(mb, m); 48780ee5cbfSDavid du Colombier mb->root->subname--; 48880ee5cbfSDavid du Colombier continue; 48980ee5cbfSDavid du Colombier } 49080ee5cbfSDavid du Colombier nnew++; 4917a02f3c0SDavid du Colombier parse(m, 0, mb, 1); 49280ee5cbfSDavid du Colombier 49380ee5cbfSDavid du Colombier if(doplumb) 49480ee5cbfSDavid du Colombier mailplumb(mb, m, 0); 49580ee5cbfSDavid du Colombier } 4969a747e4fSDavid du Colombier if(pop->pipeline) 4979a747e4fSDavid du Colombier waitpid(); 49880ee5cbfSDavid du Colombier 499fa1160edSDavid du Colombier if(nnew || mb->vers == 0) { 50080ee5cbfSDavid du Colombier mb->vers++; 5019a747e4fSDavid du Colombier henter(PATH(0, Qtop), mb->name, 5029a747e4fSDavid du Colombier (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb); 50380ee5cbfSDavid du Colombier } 50480ee5cbfSDavid du Colombier 50580ee5cbfSDavid du Colombier return nil; 50680ee5cbfSDavid du Colombier } 50780ee5cbfSDavid du Colombier 50880ee5cbfSDavid du Colombier // 50980ee5cbfSDavid du Colombier // delete marked messages 51080ee5cbfSDavid du Colombier // 51180ee5cbfSDavid du Colombier static void 51280ee5cbfSDavid du Colombier pop3purge(Pop *pop, Mailbox *mb) 51380ee5cbfSDavid du Colombier { 51480ee5cbfSDavid du Colombier Message *m, *next; 51580ee5cbfSDavid du Colombier 51639734e7eSDavid du Colombier if(pop->pipeline){ 51739734e7eSDavid du Colombier switch(rfork(RFPROC|RFMEM)){ 51839734e7eSDavid du Colombier case -1: 51939734e7eSDavid du Colombier fprint(2, "rfork: %r\n"); 52039734e7eSDavid du Colombier pop->pipeline = 0; 52139734e7eSDavid du Colombier 52239734e7eSDavid du Colombier default: 52339734e7eSDavid du Colombier break; 52439734e7eSDavid du Colombier 52539734e7eSDavid du Colombier case 0: 52639734e7eSDavid du Colombier for(m = mb->root->part; m != nil; m = next){ 52739734e7eSDavid du Colombier next = m->next; 52839734e7eSDavid du Colombier if(m->deleted && m->refs == 0){ 52939734e7eSDavid du Colombier if(m->inmbox) 53039734e7eSDavid du Colombier Bprint(&pop->bout, "DELE %d\r\n", m->mesgno); 53139734e7eSDavid du Colombier } 53239734e7eSDavid du Colombier } 53339734e7eSDavid du Colombier Bflush(&pop->bout); 53439734e7eSDavid du Colombier _exits(nil); 53539734e7eSDavid du Colombier } 53639734e7eSDavid du Colombier } 53780ee5cbfSDavid du Colombier for(m = mb->root->part; m != nil; m = next) { 53880ee5cbfSDavid du Colombier next = m->next; 53980ee5cbfSDavid du Colombier if(m->deleted && m->refs == 0) { 54080ee5cbfSDavid du Colombier if(m->inmbox) { 54139734e7eSDavid du Colombier if(!pop->pipeline) 54280ee5cbfSDavid du Colombier pop3cmd(pop, "DELE %d", m->mesgno); 54380ee5cbfSDavid du Colombier if(isokay(pop3resp(pop))) 54480ee5cbfSDavid du Colombier delmessage(mb, m); 54580ee5cbfSDavid du Colombier } else 54680ee5cbfSDavid du Colombier delmessage(mb, m); 54780ee5cbfSDavid du Colombier } 54880ee5cbfSDavid du Colombier } 54980ee5cbfSDavid du Colombier } 55080ee5cbfSDavid du Colombier 55180ee5cbfSDavid du Colombier 55280ee5cbfSDavid du Colombier // connect to pop3 server, sync mailbox 55380ee5cbfSDavid du Colombier static char* 55480ee5cbfSDavid du Colombier pop3sync(Mailbox *mb, int doplumb) 55580ee5cbfSDavid du Colombier { 55680ee5cbfSDavid du Colombier char *err; 55780ee5cbfSDavid du Colombier Pop *pop; 55880ee5cbfSDavid du Colombier 55980ee5cbfSDavid du Colombier pop = mb->aux; 56080ee5cbfSDavid du Colombier 56180ee5cbfSDavid du Colombier if(err = pop3dial(pop)) { 56280ee5cbfSDavid du Colombier mb->waketime = time(0) + pop->refreshtime; 56380ee5cbfSDavid du Colombier return err; 56480ee5cbfSDavid du Colombier } 56580ee5cbfSDavid du Colombier 56680ee5cbfSDavid du Colombier if((err = pop3read(pop, mb, doplumb)) == nil){ 56780ee5cbfSDavid du Colombier pop3purge(pop, mb); 5689a747e4fSDavid du Colombier mb->d->atime = mb->d->mtime = time(0); 56980ee5cbfSDavid du Colombier } 57080ee5cbfSDavid du Colombier pop3hangup(pop); 57180ee5cbfSDavid du Colombier mb->waketime = time(0) + pop->refreshtime; 57280ee5cbfSDavid du Colombier return err; 57380ee5cbfSDavid du Colombier } 57480ee5cbfSDavid du Colombier 57580ee5cbfSDavid du Colombier static char Epop3ctl[] = "bad pop3 control message"; 57680ee5cbfSDavid du Colombier 57780ee5cbfSDavid du Colombier static char* 57880ee5cbfSDavid du Colombier pop3ctl(Mailbox *mb, int argc, char **argv) 57980ee5cbfSDavid du Colombier { 58080ee5cbfSDavid du Colombier int n; 58180ee5cbfSDavid du Colombier Pop *pop; 58280ee5cbfSDavid du Colombier 58380ee5cbfSDavid du Colombier pop = mb->aux; 58480ee5cbfSDavid du Colombier if(argc < 1) 58580ee5cbfSDavid du Colombier return Epop3ctl; 58680ee5cbfSDavid du Colombier 58780ee5cbfSDavid du Colombier if(argc==1 && strcmp(argv[0], "debug")==0){ 58880ee5cbfSDavid du Colombier pop->debug = 1; 58980ee5cbfSDavid du Colombier return nil; 59080ee5cbfSDavid du Colombier } 59180ee5cbfSDavid du Colombier 59280ee5cbfSDavid du Colombier if(argc==1 && strcmp(argv[0], "nodebug")==0){ 59380ee5cbfSDavid du Colombier pop->debug = 0; 59480ee5cbfSDavid du Colombier return nil; 59580ee5cbfSDavid du Colombier } 59680ee5cbfSDavid du Colombier 5979a747e4fSDavid du Colombier if(argc==1 && strcmp(argv[0], "thumbprint")==0){ 5989a747e4fSDavid du Colombier if(pop->thumb) 5999a747e4fSDavid du Colombier freeThumbprints(pop->thumb); 6009a747e4fSDavid du Colombier pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); 6019a747e4fSDavid du Colombier } 60280ee5cbfSDavid du Colombier if(strcmp(argv[0], "refresh")==0){ 60380ee5cbfSDavid du Colombier if(argc==1){ 60480ee5cbfSDavid du Colombier pop->refreshtime = 60; 60580ee5cbfSDavid du Colombier return nil; 60680ee5cbfSDavid du Colombier } 60780ee5cbfSDavid du Colombier if(argc==2){ 60880ee5cbfSDavid du Colombier n = atoi(argv[1]); 60980ee5cbfSDavid du Colombier if(n < 15) 61080ee5cbfSDavid du Colombier return Epop3ctl; 61180ee5cbfSDavid du Colombier pop->refreshtime = n; 61280ee5cbfSDavid du Colombier return nil; 61380ee5cbfSDavid du Colombier } 61480ee5cbfSDavid du Colombier } 61580ee5cbfSDavid du Colombier 61680ee5cbfSDavid du Colombier return Epop3ctl; 61780ee5cbfSDavid du Colombier } 61880ee5cbfSDavid du Colombier 61980ee5cbfSDavid du Colombier // free extra memory associated with mb 62080ee5cbfSDavid du Colombier static void 62180ee5cbfSDavid du Colombier pop3close(Mailbox *mb) 62280ee5cbfSDavid du Colombier { 62380ee5cbfSDavid du Colombier Pop *pop; 62480ee5cbfSDavid du Colombier 62580ee5cbfSDavid du Colombier pop = mb->aux; 62680ee5cbfSDavid du Colombier free(pop->freep); 62780ee5cbfSDavid du Colombier free(pop); 62880ee5cbfSDavid du Colombier } 62980ee5cbfSDavid du Colombier 63080ee5cbfSDavid du Colombier // 63180ee5cbfSDavid du Colombier // open mailboxes of the form /pop/host/user or /apop/host/user 63280ee5cbfSDavid du Colombier // 63380ee5cbfSDavid du Colombier char* 63480ee5cbfSDavid du Colombier pop3mbox(Mailbox *mb, char *path) 63580ee5cbfSDavid du Colombier { 63680ee5cbfSDavid du Colombier char *f[10]; 637b27b55e2SDavid du Colombier int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls; 63880ee5cbfSDavid du Colombier Pop *pop; 63980ee5cbfSDavid du Colombier 6409a747e4fSDavid du Colombier quotefmtinstall(); 641b27b55e2SDavid du Colombier popssl = strncmp(path, "/pops/", 6) == 0; 642b27b55e2SDavid du Colombier apopssl = strncmp(path, "/apops/", 7) == 0; 6439a747e4fSDavid du Colombier poptls = strncmp(path, "/poptls/", 8) == 0; 64439734e7eSDavid du Colombier popnotls = strncmp(path, "/popnotls/", 10) == 0; 645b27b55e2SDavid du Colombier ppop = popssl || poptls || popnotls || strncmp(path, "/pop/", 5) == 0; 6469a747e4fSDavid du Colombier apoptls = strncmp(path, "/apoptls/", 9) == 0; 64739734e7eSDavid du Colombier apopnotls = strncmp(path, "/apopnotls/", 11) == 0; 648b27b55e2SDavid du Colombier apop = apopssl || apoptls || apopnotls || strncmp(path, "/apop/", 6) == 0; 6499a747e4fSDavid du Colombier 6509a747e4fSDavid du Colombier if(!ppop && !apop) 65180ee5cbfSDavid du Colombier return Enotme; 65280ee5cbfSDavid du Colombier 65380ee5cbfSDavid du Colombier path = strdup(path); 65480ee5cbfSDavid du Colombier if(path == nil) 65580ee5cbfSDavid du Colombier return "out of memory"; 65680ee5cbfSDavid du Colombier 65780ee5cbfSDavid du Colombier nf = getfields(path, f, nelem(f), 0, "/"); 6589a747e4fSDavid du Colombier if(nf != 3 && nf != 4) { 65980ee5cbfSDavid du Colombier free(path); 660b27b55e2SDavid du Colombier return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]"; 66180ee5cbfSDavid du Colombier } 66280ee5cbfSDavid du Colombier 66380ee5cbfSDavid du Colombier pop = emalloc(sizeof(*pop)); 66480ee5cbfSDavid du Colombier pop->freep = path; 66580ee5cbfSDavid du Colombier pop->host = f[2]; 6669a747e4fSDavid du Colombier if(nf < 4) 6679a747e4fSDavid du Colombier pop->user = nil; 6689a747e4fSDavid du Colombier else 66980ee5cbfSDavid du Colombier pop->user = f[3]; 6709a747e4fSDavid du Colombier pop->ppop = ppop; 671b27b55e2SDavid du Colombier pop->needssl = popssl || apopssl; 6729a747e4fSDavid du Colombier pop->needtls = poptls || apoptls; 6739a747e4fSDavid du Colombier pop->refreshtime = 60; 67439734e7eSDavid du Colombier pop->notls = popnotls || apopnotls; 6759a747e4fSDavid du Colombier pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); 67680ee5cbfSDavid du Colombier 67780ee5cbfSDavid du Colombier mb->aux = pop; 67880ee5cbfSDavid du Colombier mb->sync = pop3sync; 67980ee5cbfSDavid du Colombier mb->close = pop3close; 68080ee5cbfSDavid du Colombier mb->ctl = pop3ctl; 6819a747e4fSDavid du Colombier mb->d = emalloc(sizeof(*mb->d)); 68280ee5cbfSDavid du Colombier 68380ee5cbfSDavid du Colombier return nil; 68480ee5cbfSDavid du Colombier } 68580ee5cbfSDavid du Colombier 686