pop3.c 12 KB

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