|  | #include "common.h" | 
|  | #include <ctype.h> | 
|  | #include <plumb.h> | 
|  | #include <libsec.h> | 
|  | #include <auth.h> | 
|  | #include <thread.h> | 
|  | #include "dat.h" | 
|  |  | 
|  | #pragma varargck type "M" uchar* | 
|  | #pragma varargck argpos pop3cmd 2 | 
|  |  | 
|  | typedef struct Pop Pop; | 
|  | struct Pop { | 
|  | char *freep;	/* free this to free the strings below */ | 
|  |  | 
|  | char *host; | 
|  | char *user; | 
|  | char *port; | 
|  |  | 
|  | int ppop; | 
|  | int refreshtime; | 
|  | int debug; | 
|  | int pipeline; | 
|  | int encrypted; | 
|  | int needtls; | 
|  | int notls; | 
|  | int needssl; | 
|  |  | 
|  | /* open network connection */ | 
|  | Biobuf bin; | 
|  | Biobuf bout; | 
|  | int fd; | 
|  | char *lastline;	/* from Brdstr */ | 
|  |  | 
|  | Thumbprint *thumb; | 
|  | }; | 
|  |  | 
|  | char* | 
|  | geterrstr(void) | 
|  | { | 
|  | static char err[64]; | 
|  |  | 
|  | err[0] = '\0'; | 
|  | errstr(err, sizeof(err)); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | /* */ | 
|  | /* get pop3 response line , without worrying */ | 
|  | /* about multiline responses; the clients */ | 
|  | /* will deal with that. */ | 
|  | /* */ | 
|  | static int | 
|  | isokay(char *s) | 
|  | { | 
|  | return s!=nil && strncmp(s, "+OK", 3)==0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | pop3cmd(Pop *pop, char *fmt, ...) | 
|  | { | 
|  | char buf[128], *p; | 
|  | va_list va; | 
|  |  | 
|  | va_start(va, fmt); | 
|  | vseprint(buf, buf+sizeof(buf), fmt, va); | 
|  | va_end(va); | 
|  |  | 
|  | p = buf+strlen(buf); | 
|  | if(p > (buf+sizeof(buf)-3)) | 
|  | sysfatal("pop3 command too long"); | 
|  |  | 
|  | if(pop->debug) | 
|  | fprint(2, "<- %s\n", buf); | 
|  | strcpy(p, "\r\n"); | 
|  | Bwrite(&pop->bout, buf, strlen(buf)); | 
|  | Bflush(&pop->bout); | 
|  | } | 
|  |  | 
|  | static char* | 
|  | pop3resp(Pop *pop) | 
|  | { | 
|  | char *s; | 
|  | char *p; | 
|  |  | 
|  | alarm(60*1000); | 
|  | if((s = Brdstr(&pop->bin, '\n', 0)) == nil){ | 
|  | close(pop->fd); | 
|  | pop->fd = -1; | 
|  | alarm(0); | 
|  | return "unexpected eof"; | 
|  | } | 
|  | alarm(0); | 
|  |  | 
|  | p = s+strlen(s)-1; | 
|  | while(p >= s && (*p == '\r' || *p == '\n')) | 
|  | *p-- = '\0'; | 
|  |  | 
|  | if(pop->debug) | 
|  | fprint(2, "-> %s\n", s); | 
|  | free(pop->lastline); | 
|  | pop->lastline = s; | 
|  | return s; | 
|  | } | 
|  |  | 
|  | #if 0 /* jpc */ | 
|  | static int | 
|  | pop3log(char *fmt, ...) | 
|  | { | 
|  | va_list ap; | 
|  |  | 
|  | va_start(ap,fmt); | 
|  | syslog(0, "/sys/log/pop3", fmt, ap); | 
|  | va_end(ap); | 
|  | return 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static char* | 
|  | pop3pushtls(Pop *pop) | 
|  | { | 
|  | int fd; | 
|  | uchar digest[SHA1dlen]; | 
|  | TLSconn conn; | 
|  |  | 
|  | memset(&conn, 0, sizeof conn); | 
|  | /* conn.trace = pop3log; */ | 
|  | fd = tlsClient(pop->fd, &conn); | 
|  | if(fd < 0) | 
|  | return "tls error"; | 
|  | if(conn.cert==nil || conn.certlen <= 0){ | 
|  | close(fd); | 
|  | return "server did not provide TLS certificate"; | 
|  | } | 
|  | sha1(conn.cert, conn.certlen, digest, nil); | 
|  | if(!pop->thumb || !okThumbprint(digest, pop->thumb)){ | 
|  | fmtinstall('H', encodefmt); | 
|  | close(fd); | 
|  | free(conn.cert); | 
|  | fprint(2, "upas/fs pop3: server certificate %.*H not recognized\n", SHA1dlen, digest); | 
|  | return "bad server certificate"; | 
|  | } | 
|  | free(conn.cert); | 
|  | close(pop->fd); | 
|  | pop->fd = fd; | 
|  | pop->encrypted = 1; | 
|  | Binit(&pop->bin, pop->fd, OREAD); | 
|  | Binit(&pop->bout, pop->fd, OWRITE); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | /* */ | 
|  | /* get capability list, possibly start tls */ | 
|  | /* */ | 
|  | static char* | 
|  | pop3capa(Pop *pop) | 
|  | { | 
|  | char *s; | 
|  | int hastls; | 
|  |  | 
|  | pop3cmd(pop, "CAPA"); | 
|  | if(!isokay(pop3resp(pop))) | 
|  | return nil; | 
|  |  | 
|  | hastls = 0; | 
|  | for(;;){ | 
|  | s = pop3resp(pop); | 
|  | if(strcmp(s, ".") == 0 || strcmp(s, "unexpected eof") == 0) | 
|  | break; | 
|  | if(strcmp(s, "STLS") == 0) | 
|  | hastls = 1; | 
|  | if(strcmp(s, "PIPELINING") == 0) | 
|  | pop->pipeline = 1; | 
|  | } | 
|  |  | 
|  | if(hastls && !pop->notls){ | 
|  | pop3cmd(pop, "STLS"); | 
|  | if(!isokay(s = pop3resp(pop))) | 
|  | return s; | 
|  | if((s = pop3pushtls(pop)) != nil) | 
|  | return s; | 
|  | } | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | /* */ | 
|  | /* log in using APOP if possible, password if allowed by user */ | 
|  | /* */ | 
|  | static char* | 
|  | pop3login(Pop *pop) | 
|  | { | 
|  | int n; | 
|  | char *s, *p, *q; | 
|  | char ubuf[128], user[128]; | 
|  | char buf[500]; | 
|  | UserPasswd *up; | 
|  |  | 
|  | s = pop3resp(pop); | 
|  | if(!isokay(s)) | 
|  | return "error in initial handshake"; | 
|  |  | 
|  | if(pop->user) | 
|  | snprint(ubuf, sizeof ubuf, " user=%q", pop->user); | 
|  | else | 
|  | ubuf[0] = '\0'; | 
|  |  | 
|  | /* look for apop banner */ | 
|  | if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) { | 
|  | *++q = '\0'; | 
|  | if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s", | 
|  | pop->host, ubuf)) < 0) | 
|  | return "factotum failed"; | 
|  | if(user[0]=='\0') | 
|  | return "factotum did not return a user name"; | 
|  |  | 
|  | if(s = pop3capa(pop)) | 
|  | return s; | 
|  |  | 
|  | pop3cmd(pop, "APOP %s %.*s", user, n, buf); | 
|  | if(!isokay(s = pop3resp(pop))) | 
|  | return s; | 
|  |  | 
|  | return nil; | 
|  | } else { | 
|  | if(pop->ppop == 0) | 
|  | return "no APOP hdr from server"; | 
|  |  | 
|  | if(s = pop3capa(pop)) | 
|  | return s; | 
|  |  | 
|  | if(pop->needtls && !pop->encrypted) | 
|  | return "could not negotiate TLS"; | 
|  |  | 
|  | up = auth_getuserpasswd(auth_getkey, "proto=pass role=client service=pop dom=%q%s", | 
|  | pop->host, ubuf); | 
|  | if(up == nil) | 
|  | return "no usable keys found"; | 
|  |  | 
|  | pop3cmd(pop, "USER %s", up->user); | 
|  | if(!isokay(s = pop3resp(pop))){ | 
|  | free(up); | 
|  | return s; | 
|  | } | 
|  | pop3cmd(pop, "PASS %s", up->passwd); | 
|  | free(up); | 
|  | if(!isokay(s = pop3resp(pop))) | 
|  | return s; | 
|  |  | 
|  | return nil; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* */ | 
|  | /* dial and handshake with pop server */ | 
|  | /* */ | 
|  | static char* | 
|  | pop3dial(Pop *pop) | 
|  | { | 
|  | char *err; | 
|  |  | 
|  | if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0) | 
|  | return geterrstr(); | 
|  |  | 
|  | if(pop->needssl){ | 
|  | if((err = pop3pushtls(pop)) != nil) | 
|  | return err; | 
|  | }else{ | 
|  | Binit(&pop->bin, pop->fd, OREAD); | 
|  | Binit(&pop->bout, pop->fd, OWRITE); | 
|  | } | 
|  |  | 
|  | if(err = pop3login(pop)) { | 
|  | close(pop->fd); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | /* */ | 
|  | /* close connection */ | 
|  | /* */ | 
|  | static void | 
|  | pop3hangup(Pop *pop) | 
|  | { | 
|  | pop3cmd(pop, "QUIT"); | 
|  | pop3resp(pop); | 
|  | close(pop->fd); | 
|  | } | 
|  |  | 
|  | /* */ | 
|  | /* download a single message */ | 
|  | /* */ | 
|  | static char* | 
|  | pop3download(Pop *pop, Message *m) | 
|  | { | 
|  | char *s, *f[3], *wp, *ep; | 
|  | char sdigest[SHA1dlen*2+1]; | 
|  | int i, l, sz; | 
|  |  | 
|  | if(!pop->pipeline) | 
|  | pop3cmd(pop, "LIST %d", m->mesgno); | 
|  | if(!isokay(s = pop3resp(pop))) | 
|  | return s; | 
|  |  | 
|  | if(tokenize(s, f, 3) != 3) | 
|  | return "syntax error in LIST response"; | 
|  |  | 
|  | if(atoi(f[1]) != m->mesgno) | 
|  | return "out of sync with pop3 server"; | 
|  |  | 
|  | sz = atoi(f[2])+200;	/* 200 because the plan9 pop3 server lies */ | 
|  | if(sz == 0) | 
|  | return "invalid size in LIST response"; | 
|  |  | 
|  | m->start = wp = emalloc(sz+1); | 
|  | ep = wp+sz; | 
|  |  | 
|  | if(!pop->pipeline) | 
|  | pop3cmd(pop, "RETR %d", m->mesgno); | 
|  | if(!isokay(s = pop3resp(pop))) { | 
|  | m->start = nil; | 
|  | free(wp); | 
|  | return s; | 
|  | } | 
|  |  | 
|  | s = nil; | 
|  | while(wp <= ep) { | 
|  | s = pop3resp(pop); | 
|  | if(strcmp(s, "unexpected eof") == 0) { | 
|  | free(m->start); | 
|  | m->start = nil; | 
|  | return "unexpected end of conversation"; | 
|  | } | 
|  | if(strcmp(s, ".") == 0) | 
|  | break; | 
|  |  | 
|  | l = strlen(s)+1; | 
|  | if(s[0] == '.') { | 
|  | s++; | 
|  | l--; | 
|  | } | 
|  | /* | 
|  | * grow by 10%/200bytes - some servers | 
|  | *  lie about message sizes | 
|  | */ | 
|  | if(wp+l > ep) { | 
|  | int pos = wp - m->start; | 
|  | sz += ((sz / 10) < 200)? 200: sz/10; | 
|  | m->start = erealloc(m->start, sz+1); | 
|  | wp = m->start+pos; | 
|  | ep = m->start+sz; | 
|  | } | 
|  | memmove(wp, s, l-1); | 
|  | wp[l-1] = '\n'; | 
|  | wp += l; | 
|  | } | 
|  |  | 
|  | if(s == nil || strcmp(s, ".") != 0) | 
|  | return "out of sync with pop3 server"; | 
|  |  | 
|  | m->end = wp; | 
|  |  | 
|  | /* make sure there's a trailing null */ | 
|  | /* (helps in body searches) */ | 
|  | *m->end = 0; | 
|  | m->bend = m->rbend = m->end; | 
|  | m->header = m->start; | 
|  |  | 
|  | /* digest message */ | 
|  | sha1((uchar*)m->start, m->end - m->start, m->digest, nil); | 
|  | for(i = 0; i < SHA1dlen; i++) | 
|  | sprint(sdigest+2*i, "%2.2ux", m->digest[i]); | 
|  | m->sdigest = s_copy(sdigest); | 
|  |  | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | /* */ | 
|  | /* check for new messages on pop server */ | 
|  | /* UIDL is not required by RFC 1939, but  */ | 
|  | /* netscape requires it, so almost every server supports it. */ | 
|  | /* we'll use it to make our lives easier. */ | 
|  | /* */ | 
|  | static char* | 
|  | pop3read(Pop *pop, Mailbox *mb, int doplumb) | 
|  | { | 
|  | char *s, *p, *uidl, *f[2]; | 
|  | int mesgno, ignore, nnew; | 
|  | Message *m, *next, **l; | 
|  |  | 
|  | /* Some POP servers disallow UIDL if the maildrop is empty. */ | 
|  | pop3cmd(pop, "STAT"); | 
|  | if(!isokay(s = pop3resp(pop))) | 
|  | return s; | 
|  |  | 
|  | /* fetch message listing; note messages to grab */ | 
|  | l = &mb->root->part; | 
|  | if(strncmp(s, "+OK 0 ", 6) != 0) { | 
|  | pop3cmd(pop, "UIDL"); | 
|  | if(!isokay(s = pop3resp(pop))) | 
|  | return s; | 
|  |  | 
|  | for(;;){ | 
|  | p = pop3resp(pop); | 
|  | if(strcmp(p, ".") == 0 || strcmp(p, "unexpected eof") == 0) | 
|  | break; | 
|  |  | 
|  | if(tokenize(p, f, 2) != 2) | 
|  | continue; | 
|  |  | 
|  | mesgno = atoi(f[0]); | 
|  | uidl = f[1]; | 
|  | if(strlen(uidl) > 75)	/* RFC 1939 says 70 characters max */ | 
|  | continue; | 
|  |  | 
|  | ignore = 0; | 
|  | while(*l != nil) { | 
|  | if(strcmp((*l)->uidl, uidl) == 0) { | 
|  | /* matches mail we already have, note mesgno for deletion */ | 
|  | (*l)->mesgno = mesgno; | 
|  | ignore = 1; | 
|  | l = &(*l)->next; | 
|  | break; | 
|  | } else { | 
|  | /* old mail no longer in box mark deleted */ | 
|  | if(doplumb) | 
|  | mailplumb(mb, *l, 1); | 
|  | (*l)->inmbox = 0; | 
|  | (*l)->deleted = 1; | 
|  | l = &(*l)->next; | 
|  | } | 
|  | } | 
|  | if(ignore) | 
|  | continue; | 
|  |  | 
|  | m = newmessage(mb->root); | 
|  | m->mallocd = 1; | 
|  | m->inmbox = 1; | 
|  | m->mesgno = mesgno; | 
|  | strcpy(m->uidl, uidl); | 
|  |  | 
|  | /* chain in; will fill in message later */ | 
|  | *l = m; | 
|  | l = &m->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* whatever is left has been removed from the mbox, mark as deleted */ | 
|  | while(*l != nil) { | 
|  | if(doplumb) | 
|  | mailplumb(mb, *l, 1); | 
|  | (*l)->inmbox = 0; | 
|  | (*l)->deleted = 1; | 
|  | l = &(*l)->next; | 
|  | } | 
|  |  | 
|  | /* download new messages */ | 
|  | nnew = 0; | 
|  | if(pop->pipeline){ | 
|  | switch(rfork(RFPROC|RFMEM)){ | 
|  | case -1: | 
|  | fprint(2, "rfork: %r\n"); | 
|  | pop->pipeline = 0; | 
|  |  | 
|  | default: | 
|  | break; | 
|  |  | 
|  | case 0: | 
|  | for(m = mb->root->part; m != nil; m = m->next){ | 
|  | if(m->start != nil) | 
|  | continue; | 
|  | Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno); | 
|  | } | 
|  | Bflush(&pop->bout); | 
|  | threadexits(nil); | 
|  | /* _exits(nil); jpc */ | 
|  | } | 
|  | } | 
|  |  | 
|  | for(m = mb->root->part; m != nil; m = next) { | 
|  | next = m->next; | 
|  |  | 
|  | if(m->start != nil) | 
|  | continue; | 
|  |  | 
|  | if(s = pop3download(pop, m)) { | 
|  | /* message disappeared? unchain */ | 
|  | fprint(2, "download %d: %s\n", m->mesgno, s); | 
|  | delmessage(mb, m); | 
|  | mb->root->subname--; | 
|  | continue; | 
|  | } | 
|  | nnew++; | 
|  | parse(m, 0, mb, 1); | 
|  |  | 
|  | if(doplumb) | 
|  | mailplumb(mb, m, 0); | 
|  | } | 
|  | if(pop->pipeline) | 
|  | waitpid(); | 
|  |  | 
|  | if(nnew || mb->vers == 0) { | 
|  | mb->vers++; | 
|  | henter(PATH(0, Qtop), mb->name, | 
|  | (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb); | 
|  | } | 
|  |  | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | /* */ | 
|  | /* delete marked messages */ | 
|  | /* */ | 
|  | static void | 
|  | pop3purge(Pop *pop, Mailbox *mb) | 
|  | { | 
|  | Message *m, *next; | 
|  |  | 
|  | if(pop->pipeline){ | 
|  | switch(rfork(RFPROC|RFMEM)){ | 
|  | case -1: | 
|  | fprint(2, "rfork: %r\n"); | 
|  | pop->pipeline = 0; | 
|  |  | 
|  | default: | 
|  | break; | 
|  |  | 
|  | case 0: | 
|  | for(m = mb->root->part; m != nil; m = next){ | 
|  | next = m->next; | 
|  | if(m->deleted && m->refs == 0){ | 
|  | if(m->inmbox) | 
|  | Bprint(&pop->bout, "DELE %d\r\n", m->mesgno); | 
|  | } | 
|  | } | 
|  | Bflush(&pop->bout); | 
|  | /* _exits(nil); jpc */ | 
|  | threadexits(nil); | 
|  | } | 
|  | } | 
|  | for(m = mb->root->part; m != nil; m = next) { | 
|  | next = m->next; | 
|  | if(m->deleted && m->refs == 0) { | 
|  | if(m->inmbox) { | 
|  | if(!pop->pipeline) | 
|  | pop3cmd(pop, "DELE %d", m->mesgno); | 
|  | if(isokay(pop3resp(pop))) | 
|  | delmessage(mb, m); | 
|  | } else | 
|  | delmessage(mb, m); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* connect to pop3 server, sync mailbox */ | 
|  | static char* | 
|  | pop3sync(Mailbox *mb, int doplumb) | 
|  | { | 
|  | char *err; | 
|  | Pop *pop; | 
|  |  | 
|  | pop = mb->aux; | 
|  |  | 
|  | if(err = pop3dial(pop)) { | 
|  | mb->waketime = time(0) + pop->refreshtime; | 
|  | return err; | 
|  | } | 
|  |  | 
|  | if((err = pop3read(pop, mb, doplumb)) == nil){ | 
|  | pop3purge(pop, mb); | 
|  | mb->d->atime = mb->d->mtime = time(0); | 
|  | } | 
|  | pop3hangup(pop); | 
|  | mb->waketime = time(0) + pop->refreshtime; | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static char Epop3ctl[] = "bad pop3 control message"; | 
|  |  | 
|  | static char* | 
|  | pop3ctl(Mailbox *mb, int argc, char **argv) | 
|  | { | 
|  | int n; | 
|  | Pop *pop; | 
|  | char *m, *me; | 
|  |  | 
|  | pop = mb->aux; | 
|  | if(argc < 1) | 
|  | return Epop3ctl; | 
|  |  | 
|  | if(argc==1 && strcmp(argv[0], "debug")==0){ | 
|  | pop->debug = 1; | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | if(argc==1 && strcmp(argv[0], "nodebug")==0){ | 
|  | pop->debug = 0; | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | if(argc==1 && strcmp(argv[0], "thumbprint")==0){ | 
|  | if(pop->thumb) | 
|  | freeThumbprints(pop->thumb); | 
|  | /* pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); jpc */ | 
|  | m = unsharp("#9/sys/lib/tls/mail"); | 
|  | me = unsharp("#9/sys/lib/tls/mail.exclude"); | 
|  | pop->thumb = initThumbprints(m, me); | 
|  | } | 
|  | if(strcmp(argv[0], "refresh")==0){ | 
|  | if(argc==1){ | 
|  | pop->refreshtime = 60; | 
|  | return nil; | 
|  | } | 
|  | if(argc==2){ | 
|  | n = atoi(argv[1]); | 
|  | if(n < 15) | 
|  | return Epop3ctl; | 
|  | pop->refreshtime = n; | 
|  | return nil; | 
|  | } | 
|  | } | 
|  |  | 
|  | return Epop3ctl; | 
|  | } | 
|  |  | 
|  | /* free extra memory associated with mb */ | 
|  | static void | 
|  | pop3close(Mailbox *mb) | 
|  | { | 
|  | Pop *pop; | 
|  |  | 
|  | pop = mb->aux; | 
|  | free(pop->freep); | 
|  | free(pop); | 
|  | } | 
|  |  | 
|  | /* */ | 
|  | /* open mailboxes of the form /pop/host/user or /apop/host/user */ | 
|  | /* */ | 
|  | char* | 
|  | pop3mbox(Mailbox *mb, char *path) | 
|  | { | 
|  | char *f[10]; | 
|  | int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls; | 
|  | Pop *pop; | 
|  | char *m, *me; | 
|  |  | 
|  | quotefmtinstall(); | 
|  | popssl = strncmp(path, "/pops/", 6) == 0; | 
|  | apopssl = strncmp(path, "/apops/", 7) == 0; | 
|  | poptls = strncmp(path, "/poptls/", 8) == 0; | 
|  | popnotls = strncmp(path, "/popnotls/", 10) == 0; | 
|  | ppop = popssl || poptls || popnotls || strncmp(path, "/pop/", 5) == 0; | 
|  | apoptls = strncmp(path, "/apoptls/", 9) == 0; | 
|  | apopnotls = strncmp(path, "/apopnotls/", 11) == 0; | 
|  | apop = apopssl || apoptls || apopnotls || strncmp(path, "/apop/", 6) == 0; | 
|  |  | 
|  | if(!ppop && !apop) | 
|  | return Enotme; | 
|  |  | 
|  | path = strdup(path); | 
|  | if(path == nil) | 
|  | return "out of memory"; | 
|  |  | 
|  | nf = getfields(path, f, nelem(f), 0, "/"); | 
|  | if(nf != 3 && nf != 4) { | 
|  | free(path); | 
|  | return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]"; | 
|  | } | 
|  |  | 
|  | pop = emalloc(sizeof(*pop)); | 
|  | pop->freep = path; | 
|  | pop->host = f[2]; | 
|  | if(nf < 4) | 
|  | pop->user = nil; | 
|  | else | 
|  | pop->user = f[3]; | 
|  | pop->ppop = ppop; | 
|  | pop->needssl = popssl || apopssl; | 
|  | pop->needtls = poptls || apoptls; | 
|  | pop->refreshtime = 60; | 
|  | pop->notls = popnotls || apopnotls; | 
|  | /* pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); jpc */ | 
|  | m = unsharp("#9/sys/lib/tls/mail"); | 
|  | me = unsharp("#9/sys/lib/tls/mail.exclude"); | 
|  | pop->thumb = initThumbprints(m, me); | 
|  |  | 
|  | mb->aux = pop; | 
|  | mb->sync = pop3sync; | 
|  | mb->close = pop3close; | 
|  | mb->ctl = pop3ctl; | 
|  | mb->d = emalloc(sizeof(*mb->d)); | 
|  |  | 
|  | return nil; | 
|  | } | 
|  |  |