9 #pragma varargck type "M" uchar*
10 #pragma varargck argpos pop3cmd 2
12 typedef struct Pop Pop;
14 char *freep; // free this to free the strings below
29 // open network connection
33 char *lastline; // from Brdstr
44 errstr(err, sizeof(err));
49 // get pop3 response line , without worrying
50 // about multiline responses; the clients
51 // will deal with that.
56 return s!=nil && strncmp(s, "+OK", 3)==0;
60 pop3cmd(Pop *pop, char *fmt, ...)
66 vseprint(buf, buf+sizeof(buf), fmt, va);
70 if(p > (buf+sizeof(buf)-3))
71 sysfatal("pop3 command too long");
74 fprint(2, "<- %s\n", buf);
76 Bwrite(&pop->bout, buf, strlen(buf));
87 if((s = Brdstr(&pop->bin, '\n', 0)) == nil){
91 return "unexpected eof";
96 while(p >= s && (*p == '\r' || *p == '\n'))
100 fprint(2, "-> %s\n", s);
108 pop3log(char *fmt, ...)
113 syslog(0, "/sys/log/pop3", fmt, ap);
120 pop3pushtls(Pop *pop)
123 uchar digest[SHA1dlen];
126 memset(&conn, 0, sizeof conn);
127 // conn.trace = pop3log;
128 fd = tlsClient(pop->fd, &conn);
131 if(conn.cert==nil || conn.certlen <= 0){
133 return "server did not provide TLS certificate";
135 sha1(conn.cert, conn.certlen, digest, nil);
136 if(!pop->thumb || !okThumbprint(digest, pop->thumb)){
137 fmtinstall('H', encodefmt);
140 fprint(2, "upas/fs pop3: server certificate %.*H not recognized\n", SHA1dlen, digest);
141 return "bad server certificate";
147 Binit(&pop->bin, pop->fd, OREAD);
148 Binit(&pop->bout, pop->fd, OWRITE);
153 // get capability list, possibly start tls
161 pop3cmd(pop, "CAPA");
162 if(!isokay(pop3resp(pop)))
168 if(strcmp(s, ".") == 0 || strcmp(s, "unexpected eof") == 0)
170 if(strcmp(s, "STLS") == 0)
172 if(strcmp(s, "PIPELINING") == 0)
176 if(hastls && !pop->notls){
177 pop3cmd(pop, "STLS");
178 if(!isokay(s = pop3resp(pop)))
180 if((s = pop3pushtls(pop)) != nil)
187 // log in using APOP if possible, password if allowed by user
194 char ubuf[128], user[128];
200 return "error in initial handshake";
203 snprint(ubuf, sizeof ubuf, " user=%q", pop->user);
207 // look for apop banner
208 if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) {
210 if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
211 pop->host, ubuf)) < 0)
212 return "factotum failed";
214 return "factotum did not return a user name";
216 if(s = pop3capa(pop))
219 pop3cmd(pop, "APOP %s %.*s", user, n, buf);
220 if(!isokay(s = pop3resp(pop)))
226 return "no APOP hdr from server";
228 if(s = pop3capa(pop))
231 if(pop->needtls && !pop->encrypted)
232 return "could not negotiate TLS";
234 up = auth_getuserpasswd(auth_getkey, "role=client proto=pass service=pop dom=%q%s",
236 /* up = auth_getuserpasswd(auth_getkey, "proto=pass service=pop dom=%q%s",
237 pop->host, ubuf); jpc */
239 return "no usable keys found";
241 pop3cmd(pop, "USER %s", up->user);
242 if(!isokay(s = pop3resp(pop))){
246 pop3cmd(pop, "PASS %s", up->passwd);
248 if(!isokay(s = pop3resp(pop)))
256 // dial and handshake with pop server
263 if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0)
267 if((err = pop3pushtls(pop)) != nil)
270 Binit(&pop->bin, pop->fd, OREAD);
271 Binit(&pop->bout, pop->fd, OWRITE);
274 if(err = pop3login(pop)) {
288 pop3cmd(pop, "QUIT");
294 // download a single message
297 pop3download(Pop *pop, Message *m)
299 char *s, *f[3], *wp, *ep;
300 char sdigest[SHA1dlen*2+1];
304 pop3cmd(pop, "LIST %d", m->mesgno);
305 if(!isokay(s = pop3resp(pop)))
308 if(tokenize(s, f, 3) != 3)
309 return "syntax error in LIST response";
311 if(atoi(f[1]) != m->mesgno)
312 return "out of sync with pop3 server";
314 sz = atoi(f[2])+200; /* 200 because the plan9 pop3 server lies */
316 return "invalid size in LIST response";
318 m->start = wp = emalloc(sz+1);
322 pop3cmd(pop, "RETR %d", m->mesgno);
323 if(!isokay(s = pop3resp(pop))) {
332 if(strcmp(s, "unexpected eof") == 0) {
335 return "unexpected end of conversation";
337 if(strcmp(s, ".") == 0)
346 * grow by 10%/200bytes - some servers
347 * lie about message sizes
350 int pos = wp - m->start;
351 sz += ((sz / 10) < 200)? 200: sz/10;
352 m->start = erealloc(m->start, sz+1);
361 if(s == nil || strcmp(s, ".") != 0)
362 return "out of sync with pop3 server";
366 // make sure there's a trailing null
367 // (helps in body searches)
369 m->bend = m->rbend = m->end;
370 m->header = m->start;
373 sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
374 for(i = 0; i < SHA1dlen; i++)
375 sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
376 m->sdigest = s_copy(sdigest);
382 // check for new messages on pop server
383 // UIDL is not required by RFC 1939, but
384 // netscape requires it, so almost every server supports it.
385 // we'll use it to make our lives easier.
388 pop3read(Pop *pop, Mailbox *mb, int doplumb)
390 char *s, *p, *uidl, *f[2];
391 int mesgno, ignore, nnew;
392 Message *m, *next, **l;
394 // Some POP servers disallow UIDL if the maildrop is empty.
395 pop3cmd(pop, "STAT");
396 if(!isokay(s = pop3resp(pop)))
399 // fetch message listing; note messages to grab
401 if(strncmp(s, "+OK 0 ", 6) != 0) {
402 pop3cmd(pop, "UIDL");
403 if(!isokay(s = pop3resp(pop)))
408 if(strcmp(p, ".") == 0 || strcmp(p, "unexpected eof") == 0)
411 if(tokenize(p, f, 2) != 2)
416 if(strlen(uidl) > 75) // RFC 1939 says 70 characters max
421 if(strcmp((*l)->uidl, uidl) == 0) {
422 // matches mail we already have, note mesgno for deletion
423 (*l)->mesgno = mesgno;
428 // old mail no longer in box mark deleted
430 mailplumb(mb, *l, 1);
439 m = newmessage(mb->root);
443 strcpy(m->uidl, uidl);
445 // chain in; will fill in message later
451 // whatever is left has been removed from the mbox, mark as deleted
454 mailplumb(mb, *l, 1);
460 // download new messages
463 switch(rfork(RFPROC|RFMEM)){
465 fprint(2, "rfork: %r\n");
472 for(m = mb->root->part; m != nil; m = m->next){
475 Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno);
479 /* _exits(nil); jpc */
483 for(m = mb->root->part; m != nil; m = next) {
489 if(s = pop3download(pop, m)) {
490 // message disappeared? unchain
491 fprint(2, "download %d: %s\n", m->mesgno, s);
505 if(nnew || mb->vers == 0) {
507 henter(PATH(0, Qtop), mb->name,
508 (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
515 // delete marked messages
518 pop3purge(Pop *pop, Mailbox *mb)
523 switch(rfork(RFPROC|RFMEM)){
525 fprint(2, "rfork: %r\n");
532 for(m = mb->root->part; m != nil; m = next){
534 if(m->deleted && m->refs == 0){
536 Bprint(&pop->bout, "DELE %d\r\n", m->mesgno);
540 /* _exits(nil); jpc */
544 for(m = mb->root->part; m != nil; m = next) {
546 if(m->deleted && m->refs == 0) {
549 pop3cmd(pop, "DELE %d", m->mesgno);
550 if(isokay(pop3resp(pop)))
559 // connect to pop3 server, sync mailbox
561 pop3sync(Mailbox *mb, int doplumb)
568 if(err = pop3dial(pop)) {
569 mb->waketime = time(0) + pop->refreshtime;
573 if((err = pop3read(pop, mb, doplumb)) == nil){
575 mb->d->atime = mb->d->mtime = time(0);
578 mb->waketime = time(0) + pop->refreshtime;
582 static char Epop3ctl[] = "bad pop3 control message";
585 pop3ctl(Mailbox *mb, int argc, char **argv)
595 if(argc==1 && strcmp(argv[0], "debug")==0){
600 if(argc==1 && strcmp(argv[0], "nodebug")==0){
605 if(argc==1 && strcmp(argv[0], "thumbprint")==0){
607 freeThumbprints(pop->thumb);
608 /* pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); jpc */
609 m = unsharp("#9/sys/lib/tls/mail");
610 me = unsharp("#9/sys/lib/tls/mail.exclude");
611 pop->thumb = initThumbprints(m, me);
613 if(strcmp(argv[0], "refresh")==0){
615 pop->refreshtime = 60;
622 pop->refreshtime = n;
630 // free extra memory associated with mb
632 pop3close(Mailbox *mb)
642 // open mailboxes of the form /pop/host/user or /apop/host/user
645 pop3mbox(Mailbox *mb, char *path)
648 int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls;
653 popssl = strncmp(path, "/pops/", 6) == 0;
654 apopssl = strncmp(path, "/apops/", 7) == 0;
655 poptls = strncmp(path, "/poptls/", 8) == 0;
656 popnotls = strncmp(path, "/popnotls/", 10) == 0;
657 ppop = popssl || poptls || popnotls || strncmp(path, "/pop/", 5) == 0;
658 apoptls = strncmp(path, "/apoptls/", 9) == 0;
659 apopnotls = strncmp(path, "/apopnotls/", 11) == 0;
660 apop = apopssl || apoptls || apopnotls || strncmp(path, "/apop/", 6) == 0;
667 return "out of memory";
669 nf = getfields(path, f, nelem(f), 0, "/");
670 if(nf != 3 && nf != 4) {
672 return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]";
675 pop = emalloc(sizeof(*pop));
683 pop->needssl = popssl || apopssl;
684 pop->needtls = poptls || apoptls;
685 pop->refreshtime = 60;
686 pop->notls = popnotls || apopnotls;
687 /* pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); jpc */
688 m = unsharp("#9/sys/lib/tls/mail");
689 me = unsharp("#9/sys/lib/tls/mail.exclude");
690 pop->thumb = initThumbprints(m, me);
694 mb->close = pop3close;
696 mb->d = emalloc(sizeof(*mb->d));