/* * download mail from pop server, * deliver to plan9 */ #include #include #include #include #include #include int Debug = 0; typedef struct Link Link; struct Link { Biobuf *i; Biobuf *o; }; /* this should be a library routine */ int system(char *cmd) { Waitmsg *w; switch(fork()){ case -1: return -1; case 0: execl("/bin/rc", "rc", "-c", cmd, 0); _exits(0); default: if((w = wait()) == nil) { werrstr("wait() fails"); return -1; } if(w->msg[0] != 0){ werrstr("wait: %s", w->msg); free(w); return -1; } free(w); return 0; } } static int isokay(char *s) { return s != nil && strncmp(s, "+OK", 3) == 0; } static void pop3cmd(Link *l, char *fmt, ...) { va_list va; char buf[128]; va_start(va, fmt); vseprint(buf, buf+sizeof(buf), fmt, va); va_end(va); Bprint(l->o, "%s\r\n", buf); Bflush(l->o); if(Debug) fprint(2, "<- %s\n", buf); } static char* pop3resp(Link *l) { char *s, *p; if((s = Brdline(l->i, '\n')) == nil) return nil; p = s+ Blinelen(l->i) -1; while(p >= s && (*p == '\r' || *p == '\n')) *p-- = '\0'; if(Debug) fprint(2, "-> %s\n", s); return s; } int pop3login(Link *l, char *host, char *uname, int passonly) { int n; char *s, *p, *q; UserPasswd *up; char user[128], ubuf[128], buf[256]; if((s = pop3resp(l)) == nil) sysfatal("missing pop3 login banner"); if(uname) snprint(ubuf, sizeof ubuf, " user=%s", uname); else ubuf[0] = '\0'; if(!passonly && (p=strchr(s, '<')) != 0 && (q=strchr(p+1, '>')) != 0){ *++q = '\0'; if((n = auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s", host, ubuf)) < 0) sysfatal("factotum failed %r"); pop3cmd(l, "APOP %s %.*s", user, n, buf); if(!isokay(s = pop3resp(l))) sysfatal("%s@%s %.*s: %s", user, host, n, buf, s); } else { if ((up = auth_getuserpasswd(auth_getkey, "proto=pass service=pop dom=%q%s", host, ubuf)) == nil) sysfatal("factotum cannot get key %r"); pop3cmd(l, "USER %s", up->user); if(!isokay(s = pop3resp(l))) sysfatal("%s@%s: %s", up->user, host, s); pop3cmd(l, "PASS %s", up->passwd); if(!isokay(s = pop3resp(l))) sysfatal("%s@%s: %s", up->user, host, s); } return 0; } int getmsg(Link *l, int num, char *mailto, int delete) { int n, tfd; char buf[256]; char *s, *es, *rp, *wp; char template[32] = "/tmp/p3g.XXXXXXXXXXX"; pop3cmd(l, "LIST %d", num); if(!isokay(s = pop3resp(l))) sysfatal("LIST %d: %s", num, s); s = buf + strlen(buf) -1; while(s > buf && *s == ' ' || *s == '\t') s--; while(s >= buf && '0' <= *s && *s <= '9') s--; // s++; // n = atoi(s); pop3cmd(l, "RETR %d", num); if(!isokay(s = pop3resp(l))) sysfatal("RETR %d: %s", num, s); mktemp(template); if((tfd = create(template, OWRITE, 0600)) < 0) sysfatal("cannot create temp file %s: %r\n", template); while((s = Brdline(l->i, '\n')) != 0) { n = Blinelen(l->i); if(n == 3 && strncmp(s, ".\r\n", 3) == 0) break; es = s+n; if(*s == '.') s++; for(wp = rp = s; rp < es; rp++) { if(*rp != '\r') *wp++ = *rp; } write(tfd, s, wp - s); } close(tfd); if(s == 0) { fprint(2, "error reading message %d\n", num); return -1; } // snprint(buf, sizeof buf, "upas/send -r %s < %s", mailto, template); snprint(buf, sizeof buf, "upas/deliver %s /dev/user /mail/box/%s/mbox < %s", mailto, mailto, template); if(system(buf) < 0) { fprint(2, "error adding msg %d: %r\n", num); remove(template); return -1; } remove(template); if(delete) { pop3cmd(l, "DELE %d", num); if(!isokay(s = pop3resp(l))) sysfatal("DELE %d: %s", num, s); } return 0; } int nmsg(Link *l) { char *s; pop3cmd(l, "STAT"); if((s = pop3resp(l)) == nil) sysfatal("STAT: %s", s); return atoi(s +3); } int pop3dial(char *host, int needtls) { int fd; fd = dial(netmkaddr(host, "net", needtls ? "pop3s" : "pop3"), nil, nil, nil); if(fd < 0) return -1; if(needtls){ int tfd; TLSconn conn; /* Thumbprint *thumb; uchar digest[SHA1dlen]; */ memset(&conn, 0, sizeof conn); tfd = tlsClient(fd, &conn); if(tfd < 0){ /* TLSError: */ close(fd); return -1; } /* if(conn.cert==nil || conn.certlen <=0) goto TLSError; thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude"); sha1(conn.cert, conn.certlen, digest, nil); if(!thumb || !okThumbprint(digest, thumb)){ close(tfd); goto TLSError; } */ if(conn.cert) free(conn.cert); close(fd); fd = tfd; } return fd; } char *usage = "usage: %s [-dDa] [-u pop3user] [-m mailto] pophost\n"; void main(int argc, char **argv) { char *user, *mailto, *server; int passonly = 0, delete = 0; int needtls = 0; int i, n, fd; Biobuf bi, bo; Link l; user = nil; mailto = getuser(); ARGBEGIN{ case 'a': /* use pop not apop, even if offered */ passonly++; break; case 'd': /* delete msgs from server */ delete++; break; case 'm': /* user to forward messages to */ mailto = ARGF(); break; case 'D': /* pop3 protocol debug */ Debug++; break; case 'u': /* user to login to server as */ user = ARGF(); break; case 's': needtls++; break; default: fprint(2, usage, argv0); exits("usage"); }ARGEND; if(argc != 1){ fprint(2, usage, argv0); exits("usage"); } if(mailto == 0) mailto = getuser(); server = argv[0]; if((fd = pop3dial(server, needtls)) < 0) sysfatal("can't dial %r"); Binit(&bi, fd, OREAD); l.i = &bi; Binit(&bo, fd, OWRITE); l.o = &bo; pop3login(&l, server, user, passonly); n = nmsg(&l); for(i=0; i < n; i++) if(getmsg(&l, i+1, mailto, delete) < 0) break; pop3cmd(&l, "QUIT"); Bterm(&bi); Bterm(&bo); if(i != n) sysfatal("error reading msgs: only read 1..%d %r\n", i); exits(0); }