pop3.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. #include "common.h"
  2. #include <ctype.h>
  3. #include <plumb.h>
  4. #include <libsec.h>
  5. #include <auth.h>
  6. #include "dat.h"
  7. #pragma varargck type "M" uchar*
  8. #pragma varargck argpos pop3cmd 2
  9. typedef struct Pop Pop;
  10. struct Pop {
  11. char *freep; // free this to free the strings below
  12. char *host;
  13. char *user;
  14. char *port;
  15. int ppop;
  16. int refreshtime;
  17. int debug;
  18. int pipeline;
  19. int encrypted;
  20. int needtls;
  21. int notls;
  22. int needssl;
  23. // open network connection
  24. Biobuf bin;
  25. Biobuf bout;
  26. int fd;
  27. char *lastline; // from Brdstr
  28. Thumbprint *thumb;
  29. };
  30. char*
  31. geterrstr(void)
  32. {
  33. static char err[64];
  34. err[0] = '\0';
  35. errstr(err, sizeof(err));
  36. return err;
  37. }
  38. //
  39. // get pop3 response line , without worrying
  40. // about multiline responses; the clients
  41. // will deal with that.
  42. //
  43. static int
  44. isokay(char *s)
  45. {
  46. return s!=nil && strncmp(s, "+OK", 3)==0;
  47. }
  48. static void
  49. pop3cmd(Pop *pop, char *fmt, ...)
  50. {
  51. char buf[128], *p;
  52. va_list va;
  53. va_start(va, fmt);
  54. vseprint(buf, buf+sizeof(buf), fmt, va);
  55. va_end(va);
  56. p = buf+strlen(buf);
  57. if(p > (buf+sizeof(buf)-3))
  58. sysfatal("pop3 command too long");
  59. if(pop->debug)
  60. fprint(2, "<- %s\n", buf);
  61. strcpy(p, "\r\n");
  62. Bwrite(&pop->bout, buf, strlen(buf));
  63. Bflush(&pop->bout);
  64. }
  65. static char*
  66. pop3resp(Pop *pop)
  67. {
  68. char *s;
  69. char *p;
  70. alarm(60*1000);
  71. if((s = Brdstr(&pop->bin, '\n', 0)) == nil){
  72. close(pop->fd);
  73. pop->fd = -1;
  74. alarm(0);
  75. return "unexpected eof";
  76. }
  77. alarm(0);
  78. p = s+strlen(s)-1;
  79. while(p >= s && (*p == '\r' || *p == '\n'))
  80. *p-- = '\0';
  81. if(pop->debug)
  82. fprint(2, "-> %s\n", s);
  83. free(pop->lastline);
  84. pop->lastline = s;
  85. return s;
  86. }
  87. static int
  88. pop3log(char *fmt, ...)
  89. {
  90. va_list ap;
  91. va_start(ap,fmt);
  92. syslog(0, "/sys/log/pop3", fmt, ap);
  93. va_end(ap);
  94. return 0;
  95. }
  96. static char*
  97. pop3pushtls(Pop *pop)
  98. {
  99. int fd;
  100. uchar digest[SHA1dlen];
  101. TLSconn conn;
  102. memset(&conn, 0, sizeof conn);
  103. // conn.trace = pop3log;
  104. fd = tlsClient(pop->fd, &conn);
  105. if(fd < 0)
  106. return "tls error";
  107. if(conn.cert==nil || conn.certlen <= 0){
  108. close(fd);
  109. return "server did not provide TLS certificate";
  110. }
  111. sha1(conn.cert, conn.certlen, digest, nil);
  112. if(!pop->thumb || !okThumbprint(digest, pop->thumb)){
  113. fmtinstall('H', encodefmt);
  114. close(fd);
  115. free(conn.cert);
  116. fprint(2, "upas/fs pop3: server certificate %.*H not recognized\n", SHA1dlen, digest);
  117. return "bad server certificate";
  118. }
  119. free(conn.cert);
  120. close(pop->fd);
  121. pop->fd = fd;
  122. pop->encrypted = 1;
  123. Binit(&pop->bin, pop->fd, OREAD);
  124. Binit(&pop->bout, pop->fd, OWRITE);
  125. return nil;
  126. }
  127. //
  128. // get capability list, possibly start tls
  129. //
  130. static char*
  131. pop3capa(Pop *pop)
  132. {
  133. char *s;
  134. int hastls;
  135. pop3cmd(pop, "CAPA");
  136. if(!isokay(pop3resp(pop)))
  137. return nil;
  138. hastls = 0;
  139. for(;;){
  140. s = pop3resp(pop);
  141. if(strcmp(s, ".") == 0 || strcmp(s, "unexpected eof") == 0)
  142. break;
  143. if(strcmp(s, "STLS") == 0)
  144. hastls = 1;
  145. if(strcmp(s, "PIPELINING") == 0)
  146. pop->pipeline = 1;
  147. if(strcmp(s, "EXPIRE 0") == 0)
  148. return "server does not allow mail to be left on server";
  149. }
  150. if(hastls && !pop->notls){
  151. pop3cmd(pop, "STLS");
  152. if(!isokay(s = pop3resp(pop)))
  153. return s;
  154. if((s = pop3pushtls(pop)) != nil)
  155. return s;
  156. }
  157. return nil;
  158. }
  159. //
  160. // log in using APOP if possible, password if allowed by user
  161. //
  162. static char*
  163. pop3login(Pop *pop)
  164. {
  165. int n;
  166. char *s, *p, *q;
  167. char ubuf[128], user[128];
  168. char buf[500];
  169. UserPasswd *up;
  170. s = pop3resp(pop);
  171. if(!isokay(s))
  172. return "error in initial handshake";
  173. if(pop->user)
  174. snprint(ubuf, sizeof ubuf, " user=%q", pop->user);
  175. else
  176. ubuf[0] = '\0';
  177. // look for apop banner
  178. if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) {
  179. *++q = '\0';
  180. if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
  181. pop->host, ubuf)) < 0)
  182. return "factotum failed";
  183. if(user[0]=='\0')
  184. return "factotum did not return a user name";
  185. if(s = pop3capa(pop))
  186. return s;
  187. pop3cmd(pop, "APOP %s %.*s", user, n, buf);
  188. if(!isokay(s = pop3resp(pop)))
  189. return s;
  190. return nil;
  191. } else {
  192. if(pop->ppop == 0)
  193. return "no APOP hdr from server";
  194. if(s = pop3capa(pop))
  195. return s;
  196. if(pop->needtls && !pop->encrypted)
  197. return "could not negotiate TLS";
  198. up = auth_getuserpasswd(auth_getkey, "proto=pass service=pop dom=%q%s",
  199. pop->host, ubuf);
  200. if(up == nil)
  201. return "no usable keys found";
  202. pop3cmd(pop, "USER %s", up->user);
  203. if(!isokay(s = pop3resp(pop))){
  204. free(up);
  205. return s;
  206. }
  207. pop3cmd(pop, "PASS %s", up->passwd);
  208. free(up);
  209. if(!isokay(s = pop3resp(pop)))
  210. return s;
  211. return nil;
  212. }
  213. }
  214. //
  215. // dial and handshake with pop server
  216. //
  217. static char*
  218. pop3dial(Pop *pop)
  219. {
  220. char *err;
  221. if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0)
  222. return geterrstr();
  223. if(pop->needssl){
  224. if((err = pop3pushtls(pop)) != nil)
  225. return err;
  226. }else{
  227. Binit(&pop->bin, pop->fd, OREAD);
  228. Binit(&pop->bout, pop->fd, OWRITE);
  229. }
  230. if(err = pop3login(pop)) {
  231. close(pop->fd);
  232. return err;
  233. }
  234. return nil;
  235. }
  236. //
  237. // close connection
  238. //
  239. static void
  240. pop3hangup(Pop *pop)
  241. {
  242. pop3cmd(pop, "QUIT");
  243. pop3resp(pop);
  244. close(pop->fd);
  245. }
  246. //
  247. // download a single message
  248. //
  249. static char*
  250. pop3download(Pop *pop, Message *m)
  251. {
  252. char *s, *f[3], *wp, *ep;
  253. char sdigest[SHA1dlen*2+1];
  254. int i, l, sz;
  255. if(!pop->pipeline)
  256. pop3cmd(pop, "LIST %d", m->mesgno);
  257. if(!isokay(s = pop3resp(pop)))
  258. return s;
  259. if(tokenize(s, f, 3) != 3)
  260. return "syntax error in LIST response";
  261. if(atoi(f[1]) != m->mesgno)
  262. return "out of sync with pop3 server";
  263. sz = atoi(f[2])+200; /* 200 because the plan9 pop3 server lies */
  264. if(sz == 0)
  265. return "invalid size in LIST response";
  266. m->start = wp = emalloc(sz+1);
  267. ep = wp+sz;
  268. if(!pop->pipeline)
  269. pop3cmd(pop, "RETR %d", m->mesgno);
  270. if(!isokay(s = pop3resp(pop))) {
  271. m->start = nil;
  272. free(wp);
  273. return s;
  274. }
  275. s = nil;
  276. while(wp <= ep) {
  277. s = pop3resp(pop);
  278. if(strcmp(s, "unexpected eof") == 0) {
  279. free(m->start);
  280. m->start = nil;
  281. return "unexpected end of conversation";
  282. }
  283. if(strcmp(s, ".") == 0)
  284. break;
  285. l = strlen(s)+1;
  286. if(s[0] == '.') {
  287. s++;
  288. l--;
  289. }
  290. /*
  291. * grow by 10%/200bytes - some servers
  292. * lie about message sizes
  293. */
  294. if(wp+l > ep) {
  295. int pos = wp - m->start;
  296. sz += ((sz / 10) < 200)? 200: sz/10;
  297. m->start = erealloc(m->start, sz+1);
  298. wp = m->start+pos;
  299. ep = m->start+sz;
  300. }
  301. memmove(wp, s, l-1);
  302. wp[l-1] = '\n';
  303. wp += l;
  304. }
  305. if(s == nil || strcmp(s, ".") != 0)
  306. return "out of sync with pop3 server";
  307. m->end = wp;
  308. // make sure there's a trailing null
  309. // (helps in body searches)
  310. *m->end = 0;
  311. m->bend = m->rbend = m->end;
  312. m->header = m->start;
  313. // digest message
  314. sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
  315. for(i = 0; i < SHA1dlen; i++)
  316. sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
  317. m->sdigest = s_copy(sdigest);
  318. return nil;
  319. }
  320. //
  321. // check for new messages on pop server
  322. // UIDL is not required by RFC 1939, but
  323. // netscape requires it, so almost every server supports it.
  324. // we'll use it to make our lives easier.
  325. //
  326. static char*
  327. pop3read(Pop *pop, Mailbox *mb, int doplumb)
  328. {
  329. char *s, *p, *uidl, *f[2];
  330. int mesgno, ignore, nnew;
  331. Message *m, *next, **l;
  332. // Some POP servers disallow UIDL if the maildrop is empty.
  333. pop3cmd(pop, "STAT");
  334. if(!isokay(s = pop3resp(pop)))
  335. return s;
  336. // fetch message listing; note messages to grab
  337. l = &mb->root->part;
  338. if(strncmp(s, "+OK 0 ", 6) != 0) {
  339. pop3cmd(pop, "UIDL");
  340. if(!isokay(s = pop3resp(pop)))
  341. return s;
  342. for(;;){
  343. p = pop3resp(pop);
  344. if(strcmp(p, ".") == 0 || strcmp(p, "unexpected eof") == 0)
  345. break;
  346. if(tokenize(p, f, 2) != 2)
  347. continue;
  348. mesgno = atoi(f[0]);
  349. uidl = f[1];
  350. if(strlen(uidl) > 75) // RFC 1939 says 70 characters max
  351. continue;
  352. ignore = 0;
  353. while(*l != nil) {
  354. if(strcmp((*l)->uidl, uidl) == 0) {
  355. // matches mail we already have, note mesgno for deletion
  356. (*l)->mesgno = mesgno;
  357. ignore = 1;
  358. l = &(*l)->next;
  359. break;
  360. } else {
  361. // old mail no longer in box mark deleted
  362. if(doplumb)
  363. mailplumb(mb, *l, 1);
  364. (*l)->inmbox = 0;
  365. (*l)->deleted = 1;
  366. l = &(*l)->next;
  367. }
  368. }
  369. if(ignore)
  370. continue;
  371. m = newmessage(mb->root);
  372. m->mallocd = 1;
  373. m->inmbox = 1;
  374. m->mesgno = mesgno;
  375. strcpy(m->uidl, uidl);
  376. // chain in; will fill in message later
  377. *l = m;
  378. l = &m->next;
  379. }
  380. }
  381. // whatever is left has been removed from the mbox, mark as deleted
  382. while(*l != nil) {
  383. if(doplumb)
  384. mailplumb(mb, *l, 1);
  385. (*l)->inmbox = 0;
  386. (*l)->deleted = 1;
  387. l = &(*l)->next;
  388. }
  389. // download new messages
  390. nnew = 0;
  391. if(pop->pipeline){
  392. switch(rfork(RFPROC|RFMEM)){
  393. case -1:
  394. fprint(2, "rfork: %r\n");
  395. pop->pipeline = 0;
  396. default:
  397. break;
  398. case 0:
  399. for(m = mb->root->part; m != nil; m = m->next){
  400. if(m->start != nil)
  401. continue;
  402. Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno);
  403. }
  404. Bflush(&pop->bout);
  405. _exits(nil);
  406. }
  407. }
  408. for(m = mb->root->part; m != nil; m = next) {
  409. next = m->next;
  410. if(m->start != nil)
  411. continue;
  412. if(s = pop3download(pop, m)) {
  413. // message disappeared? unchain
  414. fprint(2, "download %d: %s\n", m->mesgno, s);
  415. delmessage(mb, m);
  416. mb->root->subname--;
  417. continue;
  418. }
  419. nnew++;
  420. parse(m, 0, mb, 1);
  421. if(doplumb)
  422. mailplumb(mb, m, 0);
  423. }
  424. if(pop->pipeline)
  425. waitpid();
  426. if(nnew || mb->vers == 0) {
  427. mb->vers++;
  428. henter(PATH(0, Qtop), mb->name,
  429. (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
  430. }
  431. return nil;
  432. }
  433. //
  434. // delete marked messages
  435. //
  436. static void
  437. pop3purge(Pop *pop, Mailbox *mb)
  438. {
  439. Message *m, *next;
  440. if(pop->pipeline){
  441. switch(rfork(RFPROC|RFMEM)){
  442. case -1:
  443. fprint(2, "rfork: %r\n");
  444. pop->pipeline = 0;
  445. default:
  446. break;
  447. case 0:
  448. for(m = mb->root->part; m != nil; m = next){
  449. next = m->next;
  450. if(m->deleted && m->refs == 0){
  451. if(m->inmbox)
  452. Bprint(&pop->bout, "DELE %d\r\n", m->mesgno);
  453. }
  454. }
  455. Bflush(&pop->bout);
  456. _exits(nil);
  457. }
  458. }
  459. for(m = mb->root->part; m != nil; m = next) {
  460. next = m->next;
  461. if(m->deleted && m->refs == 0) {
  462. if(m->inmbox) {
  463. if(!pop->pipeline)
  464. pop3cmd(pop, "DELE %d", m->mesgno);
  465. if(isokay(pop3resp(pop)))
  466. delmessage(mb, m);
  467. } else
  468. delmessage(mb, m);
  469. }
  470. }
  471. }
  472. // connect to pop3 server, sync mailbox
  473. static char*
  474. pop3sync(Mailbox *mb, int doplumb)
  475. {
  476. char *err;
  477. Pop *pop;
  478. pop = mb->aux;
  479. if(err = pop3dial(pop)) {
  480. mb->waketime = time(0) + pop->refreshtime;
  481. return err;
  482. }
  483. if((err = pop3read(pop, mb, doplumb)) == nil){
  484. pop3purge(pop, mb);
  485. mb->d->atime = mb->d->mtime = time(0);
  486. }
  487. pop3hangup(pop);
  488. mb->waketime = time(0) + pop->refreshtime;
  489. return err;
  490. }
  491. static char Epop3ctl[] = "bad pop3 control message";
  492. static char*
  493. pop3ctl(Mailbox *mb, int argc, char **argv)
  494. {
  495. int n;
  496. Pop *pop;
  497. pop = mb->aux;
  498. if(argc < 1)
  499. return Epop3ctl;
  500. if(argc==1 && strcmp(argv[0], "debug")==0){
  501. pop->debug = 1;
  502. return nil;
  503. }
  504. if(argc==1 && strcmp(argv[0], "nodebug")==0){
  505. pop->debug = 0;
  506. return nil;
  507. }
  508. if(argc==1 && strcmp(argv[0], "thumbprint")==0){
  509. if(pop->thumb)
  510. freeThumbprints(pop->thumb);
  511. pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
  512. }
  513. if(strcmp(argv[0], "refresh")==0){
  514. if(argc==1){
  515. pop->refreshtime = 60;
  516. return nil;
  517. }
  518. if(argc==2){
  519. n = atoi(argv[1]);
  520. if(n < 15)
  521. return Epop3ctl;
  522. pop->refreshtime = n;
  523. return nil;
  524. }
  525. }
  526. return Epop3ctl;
  527. }
  528. // free extra memory associated with mb
  529. static void
  530. pop3close(Mailbox *mb)
  531. {
  532. Pop *pop;
  533. pop = mb->aux;
  534. free(pop->freep);
  535. free(pop);
  536. }
  537. //
  538. // open mailboxes of the form /pop/host/user or /apop/host/user
  539. //
  540. char*
  541. pop3mbox(Mailbox *mb, char *path)
  542. {
  543. char *f[10];
  544. int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls;
  545. Pop *pop;
  546. quotefmtinstall();
  547. popssl = strncmp(path, "/pops/", 6) == 0;
  548. apopssl = strncmp(path, "/apops/", 7) == 0;
  549. poptls = strncmp(path, "/poptls/", 8) == 0;
  550. popnotls = strncmp(path, "/popnotls/", 10) == 0;
  551. ppop = popssl || poptls || popnotls || strncmp(path, "/pop/", 5) == 0;
  552. apoptls = strncmp(path, "/apoptls/", 9) == 0;
  553. apopnotls = strncmp(path, "/apopnotls/", 11) == 0;
  554. apop = apopssl || apoptls || apopnotls || strncmp(path, "/apop/", 6) == 0;
  555. if(!ppop && !apop)
  556. return Enotme;
  557. path = strdup(path);
  558. if(path == nil)
  559. return "out of memory";
  560. nf = getfields(path, f, nelem(f), 0, "/");
  561. if(nf != 3 && nf != 4) {
  562. free(path);
  563. return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]";
  564. }
  565. pop = emalloc(sizeof(*pop));
  566. pop->freep = path;
  567. pop->host = f[2];
  568. if(nf < 4)
  569. pop->user = nil;
  570. else
  571. pop->user = f[3];
  572. pop->ppop = ppop;
  573. pop->needssl = popssl || apopssl;
  574. pop->needtls = poptls || apoptls;
  575. pop->refreshtime = 60;
  576. pop->notls = popnotls || apopnotls;
  577. pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
  578. mb->aux = pop;
  579. mb->sync = pop3sync;
  580. mb->close = pop3close;
  581. mb->ctl = pop3ctl;
  582. mb->d = emalloc(sizeof(*mb->d));
  583. return nil;
  584. }