pop3.c 11 KB

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