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;
323b6af982SDavid du Colombier char *lastline; // from Brdstr
339a747e4fSDavid du Colombier
349a747e4fSDavid du Colombier Thumbprint *thumb;
3580ee5cbfSDavid du Colombier };
3680ee5cbfSDavid du Colombier
3780ee5cbfSDavid du Colombier char*
geterrstr(void)3880ee5cbfSDavid du Colombier geterrstr(void)
3980ee5cbfSDavid du Colombier {
4021887c0bSDavid du Colombier static char err[Errlen];
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
isokay(char * s)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
pop3cmd(Pop * pop,char * fmt,...)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*
pop3resp(Pop * pop)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);
863b6af982SDavid 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
943b6af982SDavid 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);
1003b6af982SDavid du Colombier free(pop->lastline);
1013b6af982SDavid du Colombier pop->lastline = s;
10280ee5cbfSDavid du Colombier return s;
10380ee5cbfSDavid du Colombier }
10480ee5cbfSDavid du Colombier
1057a02f3c0SDavid du Colombier static int
pop3log(char * fmt,...)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*
pop3pushtls(Pop * pop)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);
133*27acba7cSDavid du Colombier /*
134*27acba7cSDavid du Colombier * don't do this any more. our local it people are rotating their
135*27acba7cSDavid du Colombier * certificates faster than we can keep up.
136*27acba7cSDavid du Colombier */
137*27acba7cSDavid du Colombier if(0 && (!pop->thumb || !okThumbprint(digest, pop->thumb))){
138b27b55e2SDavid du Colombier fmtinstall('H', encodefmt);
139b27b55e2SDavid du Colombier close(fd);
140b27b55e2SDavid du Colombier free(conn.cert);
141b27b55e2SDavid du Colombier fprint(2, "upas/fs pop3: server certificate %.*H not recognized\n", SHA1dlen, digest);
142b27b55e2SDavid du Colombier return "bad server certificate";
143b27b55e2SDavid du Colombier }
144b27b55e2SDavid du Colombier free(conn.cert);
145b27b55e2SDavid du Colombier close(pop->fd);
146b27b55e2SDavid du Colombier pop->fd = fd;
147b27b55e2SDavid du Colombier pop->encrypted = 1;
148b27b55e2SDavid du Colombier Binit(&pop->bin, pop->fd, OREAD);
149b27b55e2SDavid du Colombier Binit(&pop->bout, pop->fd, OWRITE);
150b27b55e2SDavid du Colombier return nil;
151b27b55e2SDavid du Colombier }
1527a02f3c0SDavid du Colombier
15380ee5cbfSDavid du Colombier //
1549a747e4fSDavid du Colombier // get capability list, possibly start tls
1559a747e4fSDavid du Colombier //
1569a747e4fSDavid du Colombier static char*
pop3capa(Pop * pop)1579a747e4fSDavid du Colombier pop3capa(Pop *pop)
1589a747e4fSDavid du Colombier {
1599a747e4fSDavid du Colombier char *s;
1609a747e4fSDavid du Colombier int hastls;
1619a747e4fSDavid du Colombier
1629a747e4fSDavid du Colombier pop3cmd(pop, "CAPA");
1639a747e4fSDavid du Colombier if(!isokay(pop3resp(pop)))
1649a747e4fSDavid du Colombier return nil;
1659a747e4fSDavid du Colombier
1669a747e4fSDavid du Colombier hastls = 0;
167dc5a79c1SDavid du Colombier for(;;){
168dc5a79c1SDavid du Colombier s = pop3resp(pop);
169fb7f0c93SDavid du Colombier if(strcmp(s, ".") == 0 || strcmp(s, "unexpected eof") == 0)
1709a747e4fSDavid du Colombier break;
1719a747e4fSDavid du Colombier if(strcmp(s, "STLS") == 0)
1729a747e4fSDavid du Colombier hastls = 1;
1739a747e4fSDavid du Colombier if(strcmp(s, "PIPELINING") == 0)
1749a747e4fSDavid du Colombier pop->pipeline = 1;
1758e5b7f3bSDavid du Colombier if(strcmp(s, "EXPIRE 0") == 0)
1768e5b7f3bSDavid du Colombier return "server does not allow mail to be left on server";
1779a747e4fSDavid du Colombier }
1789a747e4fSDavid du Colombier
17939734e7eSDavid du Colombier if(hastls && !pop->notls){
1809a747e4fSDavid du Colombier pop3cmd(pop, "STLS");
1819a747e4fSDavid du Colombier if(!isokay(s = pop3resp(pop)))
1829a747e4fSDavid du Colombier return s;
183b27b55e2SDavid du Colombier if((s = pop3pushtls(pop)) != nil)
184b27b55e2SDavid du Colombier return s;
1859a747e4fSDavid du Colombier }
1869a747e4fSDavid du Colombier return nil;
1879a747e4fSDavid du Colombier }
1889a747e4fSDavid du Colombier
1899a747e4fSDavid du Colombier //
19080ee5cbfSDavid du Colombier // log in using APOP if possible, password if allowed by user
19180ee5cbfSDavid du Colombier //
19280ee5cbfSDavid du Colombier static char*
pop3login(Pop * pop)19380ee5cbfSDavid du Colombier pop3login(Pop *pop)
19480ee5cbfSDavid du Colombier {
1959a747e4fSDavid du Colombier int n;
19680ee5cbfSDavid du Colombier char *s, *p, *q;
1979a747e4fSDavid du Colombier char ubuf[128], user[128];
19880ee5cbfSDavid du Colombier char buf[500];
1999a747e4fSDavid du Colombier UserPasswd *up;
20080ee5cbfSDavid du Colombier
20180ee5cbfSDavid du Colombier s = pop3resp(pop);
20280ee5cbfSDavid du Colombier if(!isokay(s))
20380ee5cbfSDavid du Colombier return "error in initial handshake";
20480ee5cbfSDavid du Colombier
2059a747e4fSDavid du Colombier if(pop->user)
2069a747e4fSDavid du Colombier snprint(ubuf, sizeof ubuf, " user=%q", pop->user);
2079a747e4fSDavid du Colombier else
2089a747e4fSDavid du Colombier ubuf[0] = '\0';
2099a747e4fSDavid du Colombier
21080ee5cbfSDavid du Colombier // look for apop banner
2119a747e4fSDavid du Colombier if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) {
21280ee5cbfSDavid du Colombier *++q = '\0';
2139a747e4fSDavid du Colombier if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
2149a747e4fSDavid du Colombier pop->host, ubuf)) < 0)
2159a747e4fSDavid du Colombier return "factotum failed";
2169a747e4fSDavid du Colombier if(user[0]=='\0')
2179a747e4fSDavid du Colombier return "factotum did not return a user name";
21880ee5cbfSDavid du Colombier
2199a747e4fSDavid du Colombier if(s = pop3capa(pop))
2209a747e4fSDavid du Colombier return s;
22180ee5cbfSDavid du Colombier
2229a747e4fSDavid du Colombier pop3cmd(pop, "APOP %s %.*s", user, n, buf);
22380ee5cbfSDavid du Colombier if(!isokay(s = pop3resp(pop)))
22480ee5cbfSDavid du Colombier return s;
22580ee5cbfSDavid du Colombier
22680ee5cbfSDavid du Colombier return nil;
22780ee5cbfSDavid du Colombier } else {
22880ee5cbfSDavid du Colombier if(pop->ppop == 0)
22980ee5cbfSDavid du Colombier return "no APOP hdr from server";
23080ee5cbfSDavid du Colombier
2319a747e4fSDavid du Colombier if(s = pop3capa(pop))
23280ee5cbfSDavid du Colombier return s;
23380ee5cbfSDavid du Colombier
234b27b55e2SDavid du Colombier if(pop->needtls && !pop->encrypted)
2359a747e4fSDavid du Colombier return "could not negotiate TLS";
23680ee5cbfSDavid du Colombier
2379a747e4fSDavid du Colombier up = auth_getuserpasswd(auth_getkey, "proto=pass service=pop dom=%q%s",
2389a747e4fSDavid du Colombier pop->host, ubuf);
2399a747e4fSDavid du Colombier if(up == nil)
2409a747e4fSDavid du Colombier return "no usable keys found";
2419a747e4fSDavid du Colombier
2429a747e4fSDavid du Colombier pop3cmd(pop, "USER %s", up->user);
2439a747e4fSDavid du Colombier if(!isokay(s = pop3resp(pop))){
2449a747e4fSDavid du Colombier free(up);
2459a747e4fSDavid du Colombier return s;
2469a747e4fSDavid du Colombier }
2479a747e4fSDavid du Colombier pop3cmd(pop, "PASS %s", up->passwd);
2489a747e4fSDavid du Colombier free(up);
24980ee5cbfSDavid du Colombier if(!isokay(s = pop3resp(pop)))
25080ee5cbfSDavid du Colombier return s;
25180ee5cbfSDavid du Colombier
25280ee5cbfSDavid du Colombier return nil;
25380ee5cbfSDavid du Colombier }
25480ee5cbfSDavid du Colombier }
25580ee5cbfSDavid du Colombier
25680ee5cbfSDavid du Colombier //
25780ee5cbfSDavid du Colombier // dial and handshake with pop server
25880ee5cbfSDavid du Colombier //
25980ee5cbfSDavid du Colombier static char*
pop3dial(Pop * pop)26080ee5cbfSDavid du Colombier pop3dial(Pop *pop)
26180ee5cbfSDavid du Colombier {
26280ee5cbfSDavid du Colombier char *err;
26380ee5cbfSDavid du Colombier
264b27b55e2SDavid du Colombier if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0)
26580ee5cbfSDavid du Colombier return geterrstr();
26680ee5cbfSDavid du Colombier
267b27b55e2SDavid du Colombier if(pop->needssl){
268b27b55e2SDavid du Colombier if((err = pop3pushtls(pop)) != nil)
269b27b55e2SDavid du Colombier return err;
270b27b55e2SDavid du Colombier }else{
2719a747e4fSDavid du Colombier Binit(&pop->bin, pop->fd, OREAD);
2729a747e4fSDavid du Colombier Binit(&pop->bout, pop->fd, OWRITE);
273b27b55e2SDavid du Colombier }
27480ee5cbfSDavid du Colombier
27580ee5cbfSDavid du Colombier if(err = pop3login(pop)) {
27680ee5cbfSDavid du Colombier close(pop->fd);
27780ee5cbfSDavid du Colombier return err;
27880ee5cbfSDavid du Colombier }
27980ee5cbfSDavid du Colombier
28080ee5cbfSDavid du Colombier return nil;
28180ee5cbfSDavid du Colombier }
28280ee5cbfSDavid du Colombier
28380ee5cbfSDavid du Colombier //
28480ee5cbfSDavid du Colombier // close connection
28580ee5cbfSDavid du Colombier //
28680ee5cbfSDavid du Colombier static void
pop3hangup(Pop * pop)28780ee5cbfSDavid du Colombier pop3hangup(Pop *pop)
28880ee5cbfSDavid du Colombier {
28980ee5cbfSDavid du Colombier pop3cmd(pop, "QUIT");
29080ee5cbfSDavid du Colombier pop3resp(pop);
29180ee5cbfSDavid du Colombier close(pop->fd);
29280ee5cbfSDavid du Colombier }
29380ee5cbfSDavid du Colombier
29480ee5cbfSDavid du Colombier //
29580ee5cbfSDavid du Colombier // download a single message
29680ee5cbfSDavid du Colombier //
29780ee5cbfSDavid du Colombier static char*
pop3download(Pop * pop,Message * m)29880ee5cbfSDavid du Colombier pop3download(Pop *pop, Message *m)
29980ee5cbfSDavid du Colombier {
30080ee5cbfSDavid du Colombier char *s, *f[3], *wp, *ep;
30180ee5cbfSDavid du Colombier char sdigest[SHA1dlen*2+1];
30280ee5cbfSDavid du Colombier int i, l, sz;
30380ee5cbfSDavid du Colombier
3049a747e4fSDavid du Colombier if(!pop->pipeline)
30580ee5cbfSDavid du Colombier pop3cmd(pop, "LIST %d", m->mesgno);
30680ee5cbfSDavid du Colombier if(!isokay(s = pop3resp(pop)))
30780ee5cbfSDavid du Colombier return s;
30880ee5cbfSDavid du Colombier
30980ee5cbfSDavid du Colombier if(tokenize(s, f, 3) != 3)
31080ee5cbfSDavid du Colombier return "syntax error in LIST response";
31180ee5cbfSDavid du Colombier
31280ee5cbfSDavid du Colombier if(atoi(f[1]) != m->mesgno)
31380ee5cbfSDavid du Colombier return "out of sync with pop3 server";
31480ee5cbfSDavid du Colombier
31580ee5cbfSDavid du Colombier sz = atoi(f[2])+200; /* 200 because the plan9 pop3 server lies */
31680ee5cbfSDavid du Colombier if(sz == 0)
31780ee5cbfSDavid du Colombier return "invalid size in LIST response";
31880ee5cbfSDavid du Colombier
31980ee5cbfSDavid du Colombier m->start = wp = emalloc(sz+1);
32080ee5cbfSDavid du Colombier ep = wp+sz;
32180ee5cbfSDavid du Colombier
3229a747e4fSDavid du Colombier if(!pop->pipeline)
32380ee5cbfSDavid du Colombier pop3cmd(pop, "RETR %d", m->mesgno);
32480ee5cbfSDavid du Colombier if(!isokay(s = pop3resp(pop))) {
32580ee5cbfSDavid du Colombier m->start = nil;
32680ee5cbfSDavid du Colombier free(wp);
32780ee5cbfSDavid du Colombier return s;
32880ee5cbfSDavid du Colombier }
32980ee5cbfSDavid du Colombier
33080ee5cbfSDavid du Colombier s = nil;
33180ee5cbfSDavid du Colombier while(wp <= ep) {
33280ee5cbfSDavid du Colombier s = pop3resp(pop);
333dc5a79c1SDavid du Colombier if(strcmp(s, "unexpected eof") == 0) {
33480ee5cbfSDavid du Colombier free(m->start);
33580ee5cbfSDavid du Colombier m->start = nil;
33680ee5cbfSDavid du Colombier return "unexpected end of conversation";
33780ee5cbfSDavid du Colombier }
33880ee5cbfSDavid du Colombier if(strcmp(s, ".") == 0)
33980ee5cbfSDavid du Colombier break;
34080ee5cbfSDavid du Colombier
34180ee5cbfSDavid du Colombier l = strlen(s)+1;
34280ee5cbfSDavid du Colombier if(s[0] == '.') {
34380ee5cbfSDavid du Colombier s++;
34480ee5cbfSDavid du Colombier l--;
34580ee5cbfSDavid du Colombier }
34615b88db3SDavid du Colombier /*
34715b88db3SDavid du Colombier * grow by 10%/200bytes - some servers
34815b88db3SDavid du Colombier * lie about message sizes
34915b88db3SDavid du Colombier */
35080ee5cbfSDavid du Colombier if(wp+l > ep) {
35115b88db3SDavid du Colombier int pos = wp - m->start;
35215b88db3SDavid du Colombier sz += ((sz / 10) < 200)? 200: sz/10;
35315b88db3SDavid du Colombier m->start = erealloc(m->start, sz+1);
35415b88db3SDavid du Colombier wp = m->start+pos;
35515b88db3SDavid du Colombier ep = m->start+sz;
35680ee5cbfSDavid du Colombier }
35780ee5cbfSDavid du Colombier memmove(wp, s, l-1);
35880ee5cbfSDavid du Colombier wp[l-1] = '\n';
35980ee5cbfSDavid du Colombier wp += l;
36080ee5cbfSDavid du Colombier }
36180ee5cbfSDavid du Colombier
36280ee5cbfSDavid du Colombier if(s == nil || strcmp(s, ".") != 0)
36380ee5cbfSDavid du Colombier return "out of sync with pop3 server";
36480ee5cbfSDavid du Colombier
36580ee5cbfSDavid du Colombier m->end = wp;
36680ee5cbfSDavid du Colombier
36780ee5cbfSDavid du Colombier // make sure there's a trailing null
36880ee5cbfSDavid du Colombier // (helps in body searches)
36980ee5cbfSDavid du Colombier *m->end = 0;
37080ee5cbfSDavid du Colombier m->bend = m->rbend = m->end;
37180ee5cbfSDavid du Colombier m->header = m->start;
37280ee5cbfSDavid du Colombier
37380ee5cbfSDavid du Colombier // digest message
37480ee5cbfSDavid du Colombier sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
37580ee5cbfSDavid du Colombier for(i = 0; i < SHA1dlen; i++)
37680ee5cbfSDavid du Colombier sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
37780ee5cbfSDavid du Colombier m->sdigest = s_copy(sdigest);
37880ee5cbfSDavid du Colombier
37980ee5cbfSDavid du Colombier return nil;
38080ee5cbfSDavid du Colombier }
38180ee5cbfSDavid du Colombier
38280ee5cbfSDavid du Colombier //
38380ee5cbfSDavid du Colombier // check for new messages on pop server
38480ee5cbfSDavid du Colombier // UIDL is not required by RFC 1939, but
38580ee5cbfSDavid du Colombier // netscape requires it, so almost every server supports it.
38680ee5cbfSDavid du Colombier // we'll use it to make our lives easier.
38780ee5cbfSDavid du Colombier //
38880ee5cbfSDavid du Colombier static char*
pop3read(Pop * pop,Mailbox * mb,int doplumb)38980ee5cbfSDavid du Colombier pop3read(Pop *pop, Mailbox *mb, int doplumb)
39080ee5cbfSDavid du Colombier {
39180ee5cbfSDavid du Colombier char *s, *p, *uidl, *f[2];
39280ee5cbfSDavid du Colombier int mesgno, ignore, nnew;
39380ee5cbfSDavid du Colombier Message *m, *next, **l;
39480ee5cbfSDavid du Colombier
3959a747e4fSDavid du Colombier // Some POP servers disallow UIDL if the maildrop is empty.
3969a747e4fSDavid du Colombier pop3cmd(pop, "STAT");
39780ee5cbfSDavid du Colombier if(!isokay(s = pop3resp(pop)))
39880ee5cbfSDavid du Colombier return s;
39980ee5cbfSDavid du Colombier
40080ee5cbfSDavid du Colombier // fetch message listing; note messages to grab
40180ee5cbfSDavid du Colombier l = &mb->root->part;
4029a747e4fSDavid du Colombier if(strncmp(s, "+OK 0 ", 6) != 0) {
4039a747e4fSDavid du Colombier pop3cmd(pop, "UIDL");
4049a747e4fSDavid du Colombier if(!isokay(s = pop3resp(pop)))
4059a747e4fSDavid du Colombier return s;
4069a747e4fSDavid du Colombier
407dc5a79c1SDavid du Colombier for(;;){
408dc5a79c1SDavid du Colombier p = pop3resp(pop);
409dc5a79c1SDavid du Colombier if(strcmp(p, ".") == 0 || strcmp(p, "unexpected eof") == 0)
41080ee5cbfSDavid du Colombier break;
41180ee5cbfSDavid du Colombier
41280ee5cbfSDavid du Colombier if(tokenize(p, f, 2) != 2)
41380ee5cbfSDavid du Colombier continue;
41480ee5cbfSDavid du Colombier
41580ee5cbfSDavid du Colombier mesgno = atoi(f[0]);
41680ee5cbfSDavid du Colombier uidl = f[1];
41780ee5cbfSDavid du Colombier if(strlen(uidl) > 75) // RFC 1939 says 70 characters max
41880ee5cbfSDavid du Colombier continue;
41980ee5cbfSDavid du Colombier
42080ee5cbfSDavid du Colombier ignore = 0;
42180ee5cbfSDavid du Colombier while(*l != nil) {
42280ee5cbfSDavid du Colombier if(strcmp((*l)->uidl, uidl) == 0) {
42380ee5cbfSDavid du Colombier // matches mail we already have, note mesgno for deletion
42480ee5cbfSDavid du Colombier (*l)->mesgno = mesgno;
42580ee5cbfSDavid du Colombier ignore = 1;
42680ee5cbfSDavid du Colombier l = &(*l)->next;
42780ee5cbfSDavid du Colombier break;
42880ee5cbfSDavid du Colombier } else {
42980ee5cbfSDavid du Colombier // old mail no longer in box mark deleted
43080ee5cbfSDavid du Colombier if(doplumb)
43180ee5cbfSDavid du Colombier mailplumb(mb, *l, 1);
43280ee5cbfSDavid du Colombier (*l)->inmbox = 0;
43380ee5cbfSDavid du Colombier (*l)->deleted = 1;
43480ee5cbfSDavid du Colombier l = &(*l)->next;
43580ee5cbfSDavid du Colombier }
43680ee5cbfSDavid du Colombier }
43780ee5cbfSDavid du Colombier if(ignore)
43880ee5cbfSDavid du Colombier continue;
43980ee5cbfSDavid du Colombier
44080ee5cbfSDavid du Colombier m = newmessage(mb->root);
44180ee5cbfSDavid du Colombier m->mallocd = 1;
44280ee5cbfSDavid du Colombier m->inmbox = 1;
44380ee5cbfSDavid du Colombier m->mesgno = mesgno;
44480ee5cbfSDavid du Colombier strcpy(m->uidl, uidl);
44580ee5cbfSDavid du Colombier
44680ee5cbfSDavid du Colombier // chain in; will fill in message later
44780ee5cbfSDavid du Colombier *l = m;
44880ee5cbfSDavid du Colombier l = &m->next;
44980ee5cbfSDavid du Colombier }
4509a747e4fSDavid du Colombier }
45180ee5cbfSDavid du Colombier
45280ee5cbfSDavid du Colombier // whatever is left has been removed from the mbox, mark as deleted
45380ee5cbfSDavid du Colombier while(*l != nil) {
45480ee5cbfSDavid du Colombier if(doplumb)
45580ee5cbfSDavid du Colombier mailplumb(mb, *l, 1);
45680ee5cbfSDavid du Colombier (*l)->inmbox = 0;
45780ee5cbfSDavid du Colombier (*l)->deleted = 1;
45880ee5cbfSDavid du Colombier l = &(*l)->next;
45980ee5cbfSDavid du Colombier }
46080ee5cbfSDavid du Colombier
46180ee5cbfSDavid du Colombier // download new messages
46280ee5cbfSDavid du Colombier nnew = 0;
4639a747e4fSDavid du Colombier if(pop->pipeline){
4649a747e4fSDavid du Colombier switch(rfork(RFPROC|RFMEM)){
4659a747e4fSDavid du Colombier case -1:
4669a747e4fSDavid du Colombier fprint(2, "rfork: %r\n");
4679a747e4fSDavid du Colombier pop->pipeline = 0;
4689a747e4fSDavid du Colombier
4699a747e4fSDavid du Colombier default:
4709a747e4fSDavid du Colombier break;
4719a747e4fSDavid du Colombier
4729a747e4fSDavid du Colombier case 0:
4739a747e4fSDavid du Colombier for(m = mb->root->part; m != nil; m = m->next){
4749a747e4fSDavid du Colombier if(m->start != nil)
4759a747e4fSDavid du Colombier continue;
4769a747e4fSDavid du Colombier Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno);
4779a747e4fSDavid du Colombier }
4789a747e4fSDavid du Colombier Bflush(&pop->bout);
4799a747e4fSDavid du Colombier _exits(nil);
4809a747e4fSDavid du Colombier }
4819a747e4fSDavid du Colombier }
4829a747e4fSDavid du Colombier
48380ee5cbfSDavid du Colombier for(m = mb->root->part; m != nil; m = next) {
48480ee5cbfSDavid du Colombier next = m->next;
48580ee5cbfSDavid du Colombier
48680ee5cbfSDavid du Colombier if(m->start != nil)
48780ee5cbfSDavid du Colombier continue;
48880ee5cbfSDavid du Colombier
48980ee5cbfSDavid du Colombier if(s = pop3download(pop, m)) {
49080ee5cbfSDavid du Colombier // message disappeared? unchain
49180ee5cbfSDavid du Colombier fprint(2, "download %d: %s\n", m->mesgno, s);
49280ee5cbfSDavid du Colombier delmessage(mb, m);
49380ee5cbfSDavid du Colombier mb->root->subname--;
49480ee5cbfSDavid du Colombier continue;
49580ee5cbfSDavid du Colombier }
49680ee5cbfSDavid du Colombier nnew++;
4977a02f3c0SDavid du Colombier parse(m, 0, mb, 1);
49880ee5cbfSDavid du Colombier
49980ee5cbfSDavid du Colombier if(doplumb)
50080ee5cbfSDavid du Colombier mailplumb(mb, m, 0);
50180ee5cbfSDavid du Colombier }
5029a747e4fSDavid du Colombier if(pop->pipeline)
5039a747e4fSDavid du Colombier waitpid();
50480ee5cbfSDavid du Colombier
505fa1160edSDavid du Colombier if(nnew || mb->vers == 0) {
50680ee5cbfSDavid du Colombier mb->vers++;
5079a747e4fSDavid du Colombier henter(PATH(0, Qtop), mb->name,
5089a747e4fSDavid du Colombier (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
50980ee5cbfSDavid du Colombier }
51080ee5cbfSDavid du Colombier
51180ee5cbfSDavid du Colombier return nil;
51280ee5cbfSDavid du Colombier }
51380ee5cbfSDavid du Colombier
51480ee5cbfSDavid du Colombier //
51580ee5cbfSDavid du Colombier // delete marked messages
51680ee5cbfSDavid du Colombier //
51780ee5cbfSDavid du Colombier static void
pop3purge(Pop * pop,Mailbox * mb)51880ee5cbfSDavid du Colombier pop3purge(Pop *pop, Mailbox *mb)
51980ee5cbfSDavid du Colombier {
52080ee5cbfSDavid du Colombier Message *m, *next;
52180ee5cbfSDavid du Colombier
52239734e7eSDavid du Colombier if(pop->pipeline){
52339734e7eSDavid du Colombier switch(rfork(RFPROC|RFMEM)){
52439734e7eSDavid du Colombier case -1:
52539734e7eSDavid du Colombier fprint(2, "rfork: %r\n");
52639734e7eSDavid du Colombier pop->pipeline = 0;
52739734e7eSDavid du Colombier
52839734e7eSDavid du Colombier default:
52939734e7eSDavid du Colombier break;
53039734e7eSDavid du Colombier
53139734e7eSDavid du Colombier case 0:
53239734e7eSDavid du Colombier for(m = mb->root->part; m != nil; m = next){
53339734e7eSDavid du Colombier next = m->next;
53439734e7eSDavid du Colombier if(m->deleted && m->refs == 0){
53539734e7eSDavid du Colombier if(m->inmbox)
53639734e7eSDavid du Colombier Bprint(&pop->bout, "DELE %d\r\n", m->mesgno);
53739734e7eSDavid du Colombier }
53839734e7eSDavid du Colombier }
53939734e7eSDavid du Colombier Bflush(&pop->bout);
54039734e7eSDavid du Colombier _exits(nil);
54139734e7eSDavid du Colombier }
54239734e7eSDavid du Colombier }
54380ee5cbfSDavid du Colombier for(m = mb->root->part; m != nil; m = next) {
54480ee5cbfSDavid du Colombier next = m->next;
54580ee5cbfSDavid du Colombier if(m->deleted && m->refs == 0) {
54680ee5cbfSDavid du Colombier if(m->inmbox) {
54739734e7eSDavid du Colombier if(!pop->pipeline)
54880ee5cbfSDavid du Colombier pop3cmd(pop, "DELE %d", m->mesgno);
54980ee5cbfSDavid du Colombier if(isokay(pop3resp(pop)))
55080ee5cbfSDavid du Colombier delmessage(mb, m);
55180ee5cbfSDavid du Colombier } else
55280ee5cbfSDavid du Colombier delmessage(mb, m);
55380ee5cbfSDavid du Colombier }
55480ee5cbfSDavid du Colombier }
55580ee5cbfSDavid du Colombier }
55680ee5cbfSDavid du Colombier
55780ee5cbfSDavid du Colombier
55880ee5cbfSDavid du Colombier // connect to pop3 server, sync mailbox
55980ee5cbfSDavid du Colombier static char*
pop3sync(Mailbox * mb,int doplumb)56080ee5cbfSDavid du Colombier pop3sync(Mailbox *mb, int doplumb)
56180ee5cbfSDavid du Colombier {
56280ee5cbfSDavid du Colombier char *err;
56380ee5cbfSDavid du Colombier Pop *pop;
56480ee5cbfSDavid du Colombier
56580ee5cbfSDavid du Colombier pop = mb->aux;
56680ee5cbfSDavid du Colombier
56780ee5cbfSDavid du Colombier if(err = pop3dial(pop)) {
56880ee5cbfSDavid du Colombier mb->waketime = time(0) + pop->refreshtime;
56980ee5cbfSDavid du Colombier return err;
57080ee5cbfSDavid du Colombier }
57180ee5cbfSDavid du Colombier
57280ee5cbfSDavid du Colombier if((err = pop3read(pop, mb, doplumb)) == nil){
57380ee5cbfSDavid du Colombier pop3purge(pop, mb);
5749a747e4fSDavid du Colombier mb->d->atime = mb->d->mtime = time(0);
57580ee5cbfSDavid du Colombier }
57680ee5cbfSDavid du Colombier pop3hangup(pop);
57780ee5cbfSDavid du Colombier mb->waketime = time(0) + pop->refreshtime;
57880ee5cbfSDavid du Colombier return err;
57980ee5cbfSDavid du Colombier }
58080ee5cbfSDavid du Colombier
58180ee5cbfSDavid du Colombier static char Epop3ctl[] = "bad pop3 control message";
58280ee5cbfSDavid du Colombier
58380ee5cbfSDavid du Colombier static char*
pop3ctl(Mailbox * mb,int argc,char ** argv)58480ee5cbfSDavid du Colombier pop3ctl(Mailbox *mb, int argc, char **argv)
58580ee5cbfSDavid du Colombier {
58680ee5cbfSDavid du Colombier int n;
58780ee5cbfSDavid du Colombier Pop *pop;
58880ee5cbfSDavid du Colombier
58980ee5cbfSDavid du Colombier pop = mb->aux;
59080ee5cbfSDavid du Colombier if(argc < 1)
59180ee5cbfSDavid du Colombier return Epop3ctl;
59280ee5cbfSDavid du Colombier
59380ee5cbfSDavid du Colombier if(argc==1 && strcmp(argv[0], "debug")==0){
59480ee5cbfSDavid du Colombier pop->debug = 1;
59580ee5cbfSDavid du Colombier return nil;
59680ee5cbfSDavid du Colombier }
59780ee5cbfSDavid du Colombier
59880ee5cbfSDavid du Colombier if(argc==1 && strcmp(argv[0], "nodebug")==0){
59980ee5cbfSDavid du Colombier pop->debug = 0;
60080ee5cbfSDavid du Colombier return nil;
60180ee5cbfSDavid du Colombier }
60280ee5cbfSDavid du Colombier
6039a747e4fSDavid du Colombier if(argc==1 && strcmp(argv[0], "thumbprint")==0){
6049a747e4fSDavid du Colombier if(pop->thumb)
6059a747e4fSDavid du Colombier freeThumbprints(pop->thumb);
6069a747e4fSDavid du Colombier pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
6079a747e4fSDavid du Colombier }
60880ee5cbfSDavid du Colombier if(strcmp(argv[0], "refresh")==0){
60980ee5cbfSDavid du Colombier if(argc==1){
61080ee5cbfSDavid du Colombier pop->refreshtime = 60;
61180ee5cbfSDavid du Colombier return nil;
61280ee5cbfSDavid du Colombier }
61380ee5cbfSDavid du Colombier if(argc==2){
61480ee5cbfSDavid du Colombier n = atoi(argv[1]);
61580ee5cbfSDavid du Colombier if(n < 15)
61680ee5cbfSDavid du Colombier return Epop3ctl;
61780ee5cbfSDavid du Colombier pop->refreshtime = n;
61880ee5cbfSDavid du Colombier return nil;
61980ee5cbfSDavid du Colombier }
62080ee5cbfSDavid du Colombier }
62180ee5cbfSDavid du Colombier
62280ee5cbfSDavid du Colombier return Epop3ctl;
62380ee5cbfSDavid du Colombier }
62480ee5cbfSDavid du Colombier
62580ee5cbfSDavid du Colombier // free extra memory associated with mb
62680ee5cbfSDavid du Colombier static void
pop3close(Mailbox * mb)62780ee5cbfSDavid du Colombier pop3close(Mailbox *mb)
62880ee5cbfSDavid du Colombier {
62980ee5cbfSDavid du Colombier Pop *pop;
63080ee5cbfSDavid du Colombier
63180ee5cbfSDavid du Colombier pop = mb->aux;
63280ee5cbfSDavid du Colombier free(pop->freep);
63380ee5cbfSDavid du Colombier free(pop);
63480ee5cbfSDavid du Colombier }
63580ee5cbfSDavid du Colombier
63680ee5cbfSDavid du Colombier //
63780ee5cbfSDavid du Colombier // open mailboxes of the form /pop/host/user or /apop/host/user
63880ee5cbfSDavid du Colombier //
63980ee5cbfSDavid du Colombier char*
pop3mbox(Mailbox * mb,char * path)64080ee5cbfSDavid du Colombier pop3mbox(Mailbox *mb, char *path)
64180ee5cbfSDavid du Colombier {
64280ee5cbfSDavid du Colombier char *f[10];
643b27b55e2SDavid du Colombier int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls;
64480ee5cbfSDavid du Colombier Pop *pop;
64580ee5cbfSDavid du Colombier
6469a747e4fSDavid du Colombier quotefmtinstall();
647b27b55e2SDavid du Colombier popssl = strncmp(path, "/pops/", 6) == 0;
648b27b55e2SDavid du Colombier apopssl = strncmp(path, "/apops/", 7) == 0;
6499a747e4fSDavid du Colombier poptls = strncmp(path, "/poptls/", 8) == 0;
65039734e7eSDavid du Colombier popnotls = strncmp(path, "/popnotls/", 10) == 0;
651b27b55e2SDavid du Colombier ppop = popssl || poptls || popnotls || strncmp(path, "/pop/", 5) == 0;
6529a747e4fSDavid du Colombier apoptls = strncmp(path, "/apoptls/", 9) == 0;
65339734e7eSDavid du Colombier apopnotls = strncmp(path, "/apopnotls/", 11) == 0;
654b27b55e2SDavid du Colombier apop = apopssl || apoptls || apopnotls || strncmp(path, "/apop/", 6) == 0;
6559a747e4fSDavid du Colombier
6569a747e4fSDavid du Colombier if(!ppop && !apop)
65780ee5cbfSDavid du Colombier return Enotme;
65880ee5cbfSDavid du Colombier
65980ee5cbfSDavid du Colombier path = strdup(path);
66080ee5cbfSDavid du Colombier if(path == nil)
66180ee5cbfSDavid du Colombier return "out of memory";
66280ee5cbfSDavid du Colombier
66380ee5cbfSDavid du Colombier nf = getfields(path, f, nelem(f), 0, "/");
6649a747e4fSDavid du Colombier if(nf != 3 && nf != 4) {
66580ee5cbfSDavid du Colombier free(path);
666b27b55e2SDavid du Colombier return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]";
66780ee5cbfSDavid du Colombier }
66880ee5cbfSDavid du Colombier
66980ee5cbfSDavid du Colombier pop = emalloc(sizeof(*pop));
67080ee5cbfSDavid du Colombier pop->freep = path;
67180ee5cbfSDavid du Colombier pop->host = f[2];
6729a747e4fSDavid du Colombier if(nf < 4)
6739a747e4fSDavid du Colombier pop->user = nil;
6749a747e4fSDavid du Colombier else
67580ee5cbfSDavid du Colombier pop->user = f[3];
6769a747e4fSDavid du Colombier pop->ppop = ppop;
677b27b55e2SDavid du Colombier pop->needssl = popssl || apopssl;
6789a747e4fSDavid du Colombier pop->needtls = poptls || apoptls;
6799a747e4fSDavid du Colombier pop->refreshtime = 60;
68039734e7eSDavid du Colombier pop->notls = popnotls || apopnotls;
6819a747e4fSDavid du Colombier pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
68280ee5cbfSDavid du Colombier
68380ee5cbfSDavid du Colombier mb->aux = pop;
68480ee5cbfSDavid du Colombier mb->sync = pop3sync;
68580ee5cbfSDavid du Colombier mb->close = pop3close;
68680ee5cbfSDavid du Colombier mb->ctl = pop3ctl;
6879a747e4fSDavid du Colombier mb->d = emalloc(sizeof(*mb->d));
68880ee5cbfSDavid du Colombier
68980ee5cbfSDavid du Colombier return nil;
69080ee5cbfSDavid du Colombier }
69180ee5cbfSDavid du Colombier
692