pop3.c 11 KB

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