/*
* download mail from pop server,
* deliver to plan9
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include <auth.h>
#include <ctype.h>
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";
USED(mailto);
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);
fprint(tfd, "From %s %s", getuser(), ctime(time(nil)));
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/fs -f %s", template);
if(system(buf) < 0) {
fprint(2, "error starting upas/fs %d: %r\n", num);
remove(template);
return -1;
}
snprint(buf, sizeof buf, "upas/send %s </mail/fs/mbox/1/rawunix", getuser());
if(system(buf) < 0) {
fprint(2, "error sending 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;
rfork(RFNAMEG);
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);
}
|