6 #pragma varargck type "M" uchar*
7 #pragma varargck argpos pop3cmd 2
8 #define pdprint(p, ...) if((p)->debug) fprint(2, __VA_ARGS__); else{}
10 typedef struct Popm Popm;
15 typedef struct Pop Pop;
17 char *freep; /* free this to free the strings below */
31 Biobuf bin; /* open network connection */
34 char *lastline; /* from Brdstr */
52 errstr(err, sizeof(err));
57 * get pop3 response line , without worrying
58 * about multiline responses; the clients
59 * will deal with that.
64 return s!=nil && strncmp(s, "+OK", 3)==0;
68 pop3cmd(Pop *pop, char *fmt, ...)
74 vseprint(buf, buf + sizeof buf, fmt, va);
77 p = buf + strlen(buf);
78 if(p > buf + sizeof buf - 3)
79 sysfatal("pop3 command too long");
80 pdprint(pop, "<- %s\n", buf);
82 Bwrite(&pop->bout, buf, strlen(buf));
93 if((s = Brdstr(&pop->bin, '\n', 0)) == nil){
97 return "unexpected eof";
101 p = s + strlen(s) - 1;
102 while(p >= s && (*p == '\r' || *p == '\n'))
105 pdprint(pop, "-> %s\n", s);
112 * get capability list, possibly start tls
120 pop3cmd(pop, "CAPA");
121 if(!isokay(pop3resp(pop)))
127 if(strcmp(s, ".") == 0 || strcmp(s, "unexpected eof") == 0)
129 if(strcmp(s, "STLS") == 0)
131 if(strcmp(s, "PIPELINING") == 0)
133 if(strcmp(s, "EXPIRE 0") == 0)
134 return "server does not allow mail to be left on server";
137 if(hastls && !pop->notls){
138 pop3cmd(pop, "STLS");
139 if(!isokay(s = pop3resp(pop)))
143 if((pop->fd = wraptls(pop->fd, pop->host)) < 0)
146 Binit(&pop->bin, pop->fd, OREAD);
147 Binit(&pop->bout, pop->fd, OWRITE);
153 * log in using APOP if possible, password if allowed by user
160 char ubuf[128], user[128];
166 return "error in initial handshake";
169 snprint(ubuf, sizeof ubuf, " user=%q", pop->user);
173 /* look for apop banner */
174 if(pop->ppop == 0 && (p = strchr(s, '<')) && (q = strchr(p + 1, '>'))) {
176 if((n=auth_respond(p, q - p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
177 pop->host, ubuf)) < 0)
178 return "factotum failed";
180 return "factotum did not return a user name";
182 if(s = pop3capa(pop))
185 pop3cmd(pop, "APOP %s %.*s", user, utfnlen(buf, n), buf);
186 if(!isokay(s = pop3resp(pop)))
192 return "no APOP hdr from server";
194 if(s = pop3capa(pop))
197 if(pop->needtls && !pop->encrypted)
198 return "could not negotiate TLS";
200 up = auth_getuserpasswd(auth_getkey, "proto=pass service=pop dom=%q%s",
203 return "no usable keys found";
205 pop3cmd(pop, "USER %s", up->user);
206 if(!isokay(s = pop3resp(pop))){
210 pop3cmd(pop, "PASS %s", up->passwd);
212 if(!isokay(s = pop3resp(pop)))
220 * dial and handshake with pop server
227 if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0)
229 if(pop->needssl && (pop->fd = wraptls(pop->fd, pop->host)) < 0)
231 pop->encrypted = pop->needssl;
232 Binit(&pop->bin, pop->fd, OREAD);
233 Binit(&pop->bout, pop->fd, OWRITE);
234 if(err = pop3login(pop)) {
248 pop3cmd(pop, "QUIT");
254 * download a single message
257 pop3download(Mailbox *mb, Pop *pop, Message *m)
259 char *s, *f[3], *wp, *ep;
265 pop3cmd(pop, "LIST %d", a->mesgno);
266 if(!isokay(s = pop3resp(pop)))
269 if(tokenize(s, f, 3) != 3)
270 return "syntax error in LIST response";
272 if(atoi(f[1]) != a->mesgno)
273 return "out of sync with pop3 server";
275 sz = atoi(f[2]) + 200; /* 200 because the plan9 pop3 server lies */
277 return "invalid size in LIST response";
279 m->start = wp = emalloc(sz + 1);
283 pop3cmd(pop, "RETR %d", a->mesgno);
284 if(!isokay(s = pop3resp(pop))) {
293 if(strcmp(s, "unexpected eof") == 0) {
296 return "unexpected end of conversation";
298 if(strcmp(s, ".") == 0)
307 * grow by 10%/200bytes - some servers
308 * lie about message sizes
316 m->start = erealloc(m->start, sz + 1);
320 memmove(wp, s, l - 1);
325 if(s == nil || strcmp(s, ".") != 0)
326 return "out of sync with pop3 server";
331 * make sure there's a trailing null
332 * (helps in body searches)
335 m->bend = m->rbend = m->end;
336 m->header = m->start;
337 m->size = m->end - m->start;
339 digestmessage(mb, m);
345 * check for new messages on pop server
346 * UIDL is not required by RFC 1939, but
347 * netscape requires it, so almost every server supports it.
348 * we'll use it to make our lives easier.
351 pop3read(Pop *pop, Mailbox *mb)
353 char *s, *p, *uidl, *f[2];
355 Message *m, *next, **l;
358 /* Some POP servers disallow UIDL if the maildrop is empty. */
359 pop3cmd(pop, "STAT");
360 if(!isokay(s = pop3resp(pop)))
363 /* fetch message listing; note messages to grab */
365 if(strncmp(s, "+OK 0 ", 6) != 0) {
366 pop3cmd(pop, "UIDL");
367 if(!isokay(s = pop3resp(pop)))
372 if(strcmp(p, ".") == 0 || strcmp(p, "unexpected eof") == 0)
375 if(tokenize(p, f, 2) != 2)
380 if(strlen(uidl) > 75) /* RFC 1939 says 70 characters max */
386 if(strcmp((*l)->idxaux, uidl) == 0){
391 m->aux = a = emalloc(sizeof *a);
393 /* matches mail we already have, note mesgno for deletion */
399 /* old mail no longer in box mark deleted */
401 (*l)->deleted = Deleted;
408 m = newmessage(mb->root);
411 m->idxaux = strdup(uidl);
412 m->aux = a = emalloc(sizeof *a);
415 /* chain in; will fill in message later */
421 /* whatever is left has been removed from the mbox, mark as deleted */
424 (*l)->deleted = Disappear;
428 /* download new messages */
430 switch(rfork(RFPROC|RFMEM)){
432 eprint("pop3: rfork: %r\n");
439 for(m = mb->root->part; m != nil; m = m->next){
440 if(m->start != nil || m->deleted)
442 Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", mesgno(m), mesgno(m));
449 for(m = mb->root->part; m != nil; m = next) {
452 if(m->start != nil || m->deleted)
454 if(s = pop3download(mb, pop, m)) {
455 eprint("pop3: download %d: %s\n", mesgno(m), s);
456 unnewmessage(mb, mb->root, m);
467 * delete marked messages
470 pop3purge(Pop *pop, Mailbox *mb)
475 switch(rfork(RFPROC|RFMEM)){
477 eprint("pop3: rfork: %r\n");
484 for(m = mb->root->part; m != nil; m = m->next){
485 if(m->deleted && m->inmbox)
486 Bprint(&pop->bout, "DELE %d\r\n", mesgno(m));
492 for(m = mb->root->part; m != nil; m = m->next) {
493 if(m->deleted && m->inmbox) {
495 pop3cmd(pop, "DELE %d", mesgno(m));
496 if(!isokay(pop3resp(pop)))
504 /* connect to pop3 server, sync mailbox */
506 pop3sync(Mailbox *mb)
512 if(err = pop3dial(pop))
514 if((err = pop3read(pop, mb)) == nil)
518 mb->waketime = (ulong)time(0) + pop->refreshtime;
522 static char Epop3ctl[] = "bad pop3 control message";
525 pop3ctl(Mailbox *mb, int argc, char **argv)
534 if(argc==1 && strcmp(argv[0], "debug")==0){
539 if(argc==1 && strcmp(argv[0], "nodebug")==0){
544 if(strcmp(argv[0], "refresh")==0){
546 pop->refreshtime = 60;
553 pop->refreshtime = n;
561 /* free extra memory associated with mb */
563 pop3close(Mailbox *mb)
573 mkmbox(Pop *pop, char *p, char *e)
575 p = seprint(p, e, "%s/box/%s/pop.%s", MAILROOT, getlog(), pop->host);
576 if(pop->user && strcmp(pop->user, getlog()))
577 p = seprint(p, e, ".%s", pop->user);
582 * open mailboxes of the form /pop/host/user or /apop/host/user
585 pop3mbox(Mailbox *mb, char *path)
588 int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls;
591 popssl = strncmp(path, "/pops/", 6) == 0;
592 apopssl = strncmp(path, "/apops/", 7) == 0;
593 poptls = strncmp(path, "/poptls/", 8) == 0;
594 popnotls = strncmp(path, "/popnotls/", 10) == 0;
595 ppop = popssl || poptls || popnotls || strncmp(path, "/pop/", 5) == 0;
596 apoptls = strncmp(path, "/apoptls/", 9) == 0;
597 apopnotls = strncmp(path, "/apopnotls/", 11) == 0;
598 apop = apopssl || apoptls || apopnotls || strncmp(path, "/apop/", 6) == 0;
605 return "out of memory";
607 nf = getfields(path, f, nelem(f), 0, "/");
608 if(nf != 3 && nf != 4) {
610 return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]";
613 pop = emalloc(sizeof *pop);
621 pop->needssl = popssl || apopssl;
622 pop->needtls = poptls || apoptls;
623 pop->refreshtime = 60;
624 pop->notls = popnotls || apopnotls;
625 mkmbox(pop, mb->path, mb->path + sizeof mb->path);
628 mb->close = pop3close;