imap4.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  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 argpos imap4cmd 2
  8. #pragma varargck type "Z" char*
  9. int doublequote(Fmt*);
  10. int pipeline = 1;
  11. static char Eio[] = "i/o error";
  12. typedef struct Imap Imap;
  13. struct Imap {
  14. char *freep; // free this to free the strings below
  15. char *host;
  16. char *user;
  17. char *mbox;
  18. int mustssl;
  19. int refreshtime;
  20. int debug;
  21. ulong tag;
  22. ulong validity;
  23. int nmsg;
  24. int size;
  25. char *base;
  26. char *data;
  27. vlong *uid;
  28. int nuid;
  29. int muid;
  30. Thumbprint *thumb;
  31. // open network connection
  32. Biobuf bin;
  33. Biobuf bout;
  34. int fd;
  35. };
  36. static char*
  37. removecr(char *s)
  38. {
  39. char *r, *w;
  40. for(r=w=s; *r; r++)
  41. if(*r != '\r')
  42. *w++ = *r;
  43. *w = '\0';
  44. return s;
  45. }
  46. //
  47. // send imap4 command
  48. //
  49. static void
  50. imap4cmd(Imap *imap, char *fmt, ...)
  51. {
  52. char buf[128], *p;
  53. va_list va;
  54. va_start(va, fmt);
  55. p = buf+sprint(buf, "9X%lud ", imap->tag);
  56. vseprint(p, buf+sizeof(buf), fmt, va);
  57. va_end(va);
  58. p = buf+strlen(buf);
  59. if(p > (buf+sizeof(buf)-3))
  60. sysfatal("imap4 command too long");
  61. if(imap->debug)
  62. fprint(2, "-> %s\n", buf);
  63. strcpy(p, "\r\n");
  64. Bwrite(&imap->bout, buf, strlen(buf));
  65. Bflush(&imap->bout);
  66. }
  67. enum {
  68. OK,
  69. NO,
  70. BAD,
  71. BYE,
  72. EXISTS,
  73. STATUS,
  74. FETCH,
  75. UNKNOWN,
  76. };
  77. static char *verblist[] = {
  78. [OK] "OK",
  79. [NO] "NO",
  80. [BAD] "BAD",
  81. [BYE] "BYE",
  82. [EXISTS] "EXISTS",
  83. [STATUS] "STATUS",
  84. [FETCH] "FETCH",
  85. };
  86. static int
  87. verbcode(char *verb)
  88. {
  89. int i;
  90. char *q;
  91. if(q = strchr(verb, ' '))
  92. *q = '\0';
  93. for(i=0; i<nelem(verblist); i++)
  94. if(verblist[i] && strcmp(verblist[i], verb)==0){
  95. if(q)
  96. *q = ' ';
  97. return i;
  98. }
  99. if(q)
  100. *q = ' ';
  101. return UNKNOWN;
  102. }
  103. static void
  104. strupr(char *s)
  105. {
  106. for(; *s; s++)
  107. if('a' <= *s && *s <= 'z')
  108. *s += 'A'-'a';
  109. }
  110. static void
  111. imapgrow(Imap *imap, int n)
  112. {
  113. int i;
  114. if(imap->data == nil){
  115. imap->base = emalloc(n+1);
  116. imap->data = imap->base;
  117. imap->size = n+1;
  118. }
  119. if(n >= imap->size){
  120. // friggin microsoft - reallocate
  121. i = imap->data - imap->base;
  122. imap->base = erealloc(imap->base, i+n+1);
  123. imap->data = imap->base + i;
  124. imap->size = n+1;
  125. }
  126. }
  127. //
  128. // get imap4 response line. there might be various
  129. // data or other informational lines mixed in.
  130. //
  131. static char*
  132. imap4resp(Imap *imap)
  133. {
  134. char *line, *p, *ep, *op, *q, *r, *en, *verb;
  135. int i, n;
  136. static char error[256];
  137. while(p = Brdline(&imap->bin, '\n')){
  138. ep = p+Blinelen(&imap->bin);
  139. while(ep > p && (ep[-1]=='\n' || ep[-1]=='\r'))
  140. *--ep = '\0';
  141. if(imap->debug)
  142. fprint(2, "<- %s\n", p);
  143. strupr(p);
  144. switch(p[0]){
  145. case '+':
  146. if(imap->tag == 0)
  147. fprint(2, "unexpected: %s\n", p);
  148. break;
  149. // ``unsolicited'' information; everything happens here.
  150. case '*':
  151. if(p[1]!=' ')
  152. continue;
  153. p += 2;
  154. line = p;
  155. n = strtol(p, &p, 10);
  156. if(*p==' ')
  157. p++;
  158. verb = p;
  159. if(p = strchr(verb, ' '))
  160. p++;
  161. else
  162. p = verb+strlen(verb);
  163. switch(verbcode(verb)){
  164. case OK:
  165. case NO:
  166. case BAD:
  167. // human readable text at p;
  168. break;
  169. case BYE:
  170. // early disconnect
  171. // human readable text at p;
  172. break;
  173. // * 32 EXISTS
  174. case EXISTS:
  175. imap->nmsg = n;
  176. break;
  177. // * STATUS Inbox (MESSAGES 2 UIDVALIDITY 960164964)
  178. case STATUS:
  179. if(q = strstr(p, "MESSAGES"))
  180. imap->nmsg = atoi(q+8);
  181. if(q = strstr(p, "UIDVALIDITY"))
  182. imap->validity = strtoul(q+11, 0, 10);
  183. break;
  184. case FETCH:
  185. // * 1 FETCH (uid 8889 RFC822.SIZE 3031 body[] {3031}
  186. // <3031 bytes of data>
  187. // )
  188. if(strstr(p, "RFC822.SIZE") && strstr(p, "BODY[]")){
  189. if((q = strchr(p, '{'))
  190. && (n=strtol(q+1, &en, 0), *en=='}')){
  191. if(imap->data == nil || n >= imap->size)
  192. imapgrow(imap, n);
  193. if((i = Bread(&imap->bin, imap->data, n)) != n){
  194. snprint(error, sizeof error,
  195. "short read %d != %d: %r\n",
  196. i, n);
  197. return error;
  198. }
  199. if(imap->debug)
  200. fprint(2, "<- read %d bytes\n", n);
  201. imap->data[n] = '\0';
  202. if(imap->debug)
  203. fprint(2, "<- %s\n", imap->data);
  204. imap->data += n;
  205. imap->size -= n;
  206. p = Brdline(&imap->bin, '\n');
  207. if(imap->debug)
  208. fprint(2, "<- ignoring %.*s\n",
  209. Blinelen(&imap->bin), p);
  210. }else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
  211. *r = '\0';
  212. q++;
  213. n = r-q;
  214. if(imap->data == nil || n >= imap->size)
  215. imapgrow(imap, n);
  216. memmove(imap->data, q, n);
  217. imap->data[n] = '\0';
  218. imap->data += n;
  219. imap->size -= n;
  220. }else
  221. return "confused about FETCH response";
  222. break;
  223. }
  224. // * 1 FETCH (UID 1 RFC822.SIZE 511)
  225. if(q=strstr(p, "RFC822.SIZE")){
  226. imap->size = atoi(q+11);
  227. break;
  228. }
  229. // * 1 FETCH (UID 1 RFC822.HEADER {496}
  230. // <496 bytes of data>
  231. // )
  232. // * 1 FETCH (UID 1 RFC822.HEADER "data")
  233. if(strstr(p, "RFC822.HEADER") || strstr(p, "RFC822.TEXT")){
  234. if((q = strchr(p, '{'))
  235. && (n=strtol(q+1, &en, 0), *en=='}')){
  236. if(imap->data == nil || n >= imap->size)
  237. imapgrow(imap, n);
  238. if((i = Bread(&imap->bin, imap->data, n)) != n){
  239. snprint(error, sizeof error,
  240. "short read %d != %d: %r\n",
  241. i, n);
  242. return error;
  243. }
  244. if(imap->debug)
  245. fprint(2, "<- read %d bytes\n", n);
  246. imap->data[n] = '\0';
  247. if(imap->debug)
  248. fprint(2, "<- %s\n", imap->data);
  249. imap->data += n;
  250. imap->size -= n;
  251. p = Brdline(&imap->bin, '\n');
  252. if(imap->debug)
  253. fprint(2, "<- ignoring %.*s\n",
  254. Blinelen(&imap->bin), p);
  255. }else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
  256. *r = '\0';
  257. q++;
  258. n = r-q;
  259. if(imap->data == nil || n >= imap->size)
  260. imapgrow(imap, n);
  261. memmove(imap->data, q, n);
  262. imap->data[n] = '\0';
  263. imap->data += n;
  264. imap->size -= n;
  265. }else
  266. return "confused about FETCH response";
  267. break;
  268. }
  269. // * 1 FETCH (UID 1)
  270. // * 2 FETCH (UID 6)
  271. if(q = strstr(p, "UID")){
  272. if(imap->nuid < imap->muid)
  273. imap->uid[imap->nuid++] = ((vlong)imap->validity<<32)|strtoul(q+3, nil, 10);
  274. break;
  275. }
  276. }
  277. if(imap->tag == 0)
  278. return line;
  279. break;
  280. case '9': // response to our message
  281. op = p;
  282. if(p[1]=='X' && strtoul(p+2, &p, 10)==imap->tag){
  283. while(*p==' ')
  284. p++;
  285. imap->tag++;
  286. return p;
  287. }
  288. fprint(2, "expected %lud; got %s\n", imap->tag, op);
  289. break;
  290. default:
  291. if(imap->debug || *p)
  292. fprint(2, "unexpected line: %s\n", p);
  293. }
  294. }
  295. snprint(error, sizeof error, "i/o error: %r\n");
  296. return error;
  297. }
  298. static int
  299. isokay(char *resp)
  300. {
  301. return strncmp(resp, "OK", 2)==0;
  302. }
  303. //
  304. // log in to IMAP4 server, select mailbox, no SSL at the moment
  305. //
  306. static char*
  307. imap4login(Imap *imap)
  308. {
  309. char *s;
  310. UserPasswd *up;
  311. imap->tag = 0;
  312. s = imap4resp(imap);
  313. if(!isokay(s))
  314. return "error in initial IMAP handshake";
  315. if(imap->user != nil)
  316. up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q user=%q", imap->host, imap->user);
  317. else
  318. up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q", imap->host);
  319. if(up == nil)
  320. return "cannot find IMAP password";
  321. imap->tag = 1;
  322. imap4cmd(imap, "LOGIN %Z %Z", up->user, up->passwd);
  323. free(up);
  324. if(!isokay(s = imap4resp(imap)))
  325. return s;
  326. imap4cmd(imap, "SELECT %Z", imap->mbox);
  327. if(!isokay(s = imap4resp(imap)))
  328. return s;
  329. return nil;
  330. }
  331. //
  332. // push tls onto a connection
  333. //
  334. int
  335. mypushtls(int fd)
  336. {
  337. int p[2];
  338. char buf[10];
  339. if(pipe(p) < 0)
  340. return -1;
  341. switch(fork()){
  342. case -1:
  343. close(p[0]);
  344. close(p[1]);
  345. return -1;
  346. case 0:
  347. close(p[1]);
  348. dup(p[0], 0);
  349. dup(p[0], 1);
  350. sprint(buf, "/fd/%d", fd);
  351. execl("/bin/tlsrelay", "tlsrelay", "-f", buf, nil);
  352. _exits(nil);
  353. default:
  354. break;
  355. }
  356. close(fd);
  357. close(p[0]);
  358. return p[1];
  359. }
  360. static char*
  361. imaperrstr(char *host, char *port)
  362. {
  363. static char mess[256];
  364. char err[64];
  365. err[0] = '\0';
  366. errstr(err, sizeof(err));
  367. snprint(mess, sizeof(mess), "%s/%s:%s", host, port, err);
  368. return mess;
  369. }
  370. //
  371. // dial and handshake with the imap server
  372. //
  373. static char*
  374. imap4dial(Imap *imap)
  375. {
  376. char *err, *port;
  377. uchar digest[SHA1dlen];
  378. int sfd;
  379. TLSconn conn;
  380. if(imap->fd >= 0){
  381. imap4cmd(imap, "noop");
  382. if(isokay(imap4resp(imap)))
  383. return nil;
  384. close(imap->fd);
  385. imap->fd = -1;
  386. }
  387. if(imap->mustssl)
  388. port = "imaps";
  389. else
  390. port = "imap";
  391. if((imap->fd = dial(netmkaddr(imap->host, "net", port), 0, 0, 0)) < 0)
  392. return imaperrstr(imap->host, port);
  393. if(imap->mustssl){
  394. memset(&conn, 0, sizeof conn);
  395. sfd = tlsClient(imap->fd, &conn);
  396. if(sfd < 0)
  397. sysfatal("tlsClient: %r");
  398. if(conn.cert==nil || conn.certlen <= 0)
  399. sysfatal("server did not provide TLS certificate");
  400. sha1(conn.cert, conn.certlen, digest, nil);
  401. if(!imap->thumb || !okThumbprint(digest, imap->thumb)){
  402. fmtinstall('H', encodefmt);
  403. sysfatal("server certificate %.*H not recognized", SHA1dlen, digest);
  404. }
  405. free(conn.cert);
  406. close(imap->fd);
  407. imap->fd = sfd;
  408. if(imap->debug){
  409. char fn[128];
  410. int fd;
  411. snprint(fn, sizeof fn, "%s/ctl", conn.dir);
  412. fd = open(fn, ORDWR);
  413. if(fd < 0)
  414. fprint(2, "opening ctl: %r\n");
  415. if(fprint(fd, "debug") < 0)
  416. fprint(2, "writing ctl: %r\n");
  417. close(fd);
  418. }
  419. }
  420. Binit(&imap->bin, imap->fd, OREAD);
  421. Binit(&imap->bout, imap->fd, OWRITE);
  422. if(err = imap4login(imap)) {
  423. close(imap->fd);
  424. return err;
  425. }
  426. return nil;
  427. }
  428. //
  429. // close connection
  430. //
  431. static void
  432. imap4hangup(Imap *imap)
  433. {
  434. imap4cmd(imap, "LOGOUT");
  435. imap4resp(imap);
  436. close(imap->fd);
  437. }
  438. //
  439. // download a single message
  440. //
  441. static char*
  442. imap4fetch(Mailbox *mb, Message *m)
  443. {
  444. int i;
  445. char *p, *s, sdigest[2*SHA1dlen+1];
  446. Imap *imap;
  447. imap = mb->aux;
  448. imap->size = 0;
  449. if(!isokay(s = imap4resp(imap)))
  450. return s;
  451. p = imap->base;
  452. if(p == nil)
  453. return "did not get message body";
  454. removecr(p);
  455. free(m->start);
  456. m->start = p;
  457. m->end = p+strlen(p);
  458. m->bend = m->rbend = m->end;
  459. m->header = m->start;
  460. imap->base = nil;
  461. imap->data = nil;
  462. parse(m, 0, mb, 1);
  463. // digest headers
  464. sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
  465. for(i = 0; i < SHA1dlen; i++)
  466. sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
  467. m->sdigest = s_copy(sdigest);
  468. return nil;
  469. }
  470. //
  471. // check for new messages on imap4 server
  472. // download new messages, mark deleted messages
  473. //
  474. static char*
  475. imap4read(Imap *imap, Mailbox *mb, int doplumb)
  476. {
  477. char *s;
  478. int i, ignore, nnew, t;
  479. Message *m, *next, **l;
  480. imap4cmd(imap, "STATUS %Z (MESSAGES UIDVALIDITY)", imap->mbox);
  481. if(!isokay(s = imap4resp(imap)))
  482. return s;
  483. imap->nuid = 0;
  484. imap->uid = erealloc(imap->uid, imap->nmsg*sizeof(imap->uid[0]));
  485. imap->muid = imap->nmsg;
  486. if(imap->nmsg > 0){
  487. imap4cmd(imap, "UID FETCH 1:* UID");
  488. if(!isokay(s = imap4resp(imap)))
  489. return s;
  490. }
  491. l = &mb->root->part;
  492. for(i=0; i<imap->nuid; i++){
  493. ignore = 0;
  494. while(*l != nil){
  495. if((*l)->imapuid == imap->uid[i]){
  496. ignore = 1;
  497. l = &(*l)->next;
  498. break;
  499. }else{
  500. // old mail, we don't have it anymore
  501. if(doplumb)
  502. mailplumb(mb, *l, 1);
  503. (*l)->inmbox = 0;
  504. (*l)->deleted = 1;
  505. l = &(*l)->next;
  506. }
  507. }
  508. if(ignore)
  509. continue;
  510. // new message
  511. m = newmessage(mb->root);
  512. m->mallocd = 1;
  513. m->inmbox = 1;
  514. m->imapuid = imap->uid[i];
  515. // add to chain, will download soon
  516. *l = m;
  517. l = &m->next;
  518. }
  519. // whatever is left at the end of the chain is gone
  520. while(*l != nil){
  521. if(doplumb)
  522. mailplumb(mb, *l, 1);
  523. (*l)->inmbox = 0;
  524. (*l)->deleted = 1;
  525. l = &(*l)->next;
  526. }
  527. // download new messages
  528. t = imap->tag;
  529. if(pipeline)
  530. switch(rfork(RFPROC|RFMEM)){
  531. case -1:
  532. sysfatal("rfork: %r");
  533. default:
  534. break;
  535. case 0:
  536. for(m = mb->root->part; m != nil; m = m->next){
  537. if(m->start != nil)
  538. continue;
  539. if(imap->debug)
  540. fprint(2, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
  541. t, (ulong)m->imapuid);
  542. Bprint(&imap->bout, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
  543. t++, (ulong)m->imapuid);
  544. }
  545. Bflush(&imap->bout);
  546. _exits(nil);
  547. }
  548. nnew = 0;
  549. for(m=mb->root->part; m!=nil; m=next){
  550. next = m->next;
  551. if(m->start != nil)
  552. continue;
  553. if(!pipeline){
  554. Bprint(&imap->bout, "9X%lud UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
  555. (ulong)imap->tag, (ulong)m->imapuid);
  556. Bflush(&imap->bout);
  557. }
  558. if(s = imap4fetch(mb, m)){
  559. // message disappeared? unchain
  560. fprint(2, "download %lud: %s\n", (ulong)m->imapuid, s);
  561. delmessage(mb, m);
  562. mb->root->subname--;
  563. continue;
  564. }
  565. nnew++;
  566. if(doplumb)
  567. mailplumb(mb, m, 0);
  568. }
  569. if(pipeline)
  570. waitpid();
  571. if(nnew || mb->vers == 0){
  572. mb->vers++;
  573. henter(PATH(0, Qtop), mb->name,
  574. (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
  575. }
  576. return nil;
  577. }
  578. //
  579. // sync mailbox
  580. //
  581. static void
  582. imap4purge(Imap *imap, Mailbox *mb)
  583. {
  584. int ndel;
  585. Message *m, *next;
  586. ndel = 0;
  587. for(m=mb->root->part; m!=nil; m=next){
  588. next = m->next;
  589. if(m->deleted && m->refs==0){
  590. if(m->inmbox && (ulong)(m->imapuid>>32)==imap->validity){
  591. imap4cmd(imap, "UID STORE %lud +FLAGS (\\Deleted)", (ulong)m->imapuid);
  592. if(isokay(imap4resp(imap))){
  593. ndel++;
  594. delmessage(mb, m);
  595. }
  596. }else
  597. delmessage(mb, m);
  598. }
  599. }
  600. if(ndel){
  601. imap4cmd(imap, "EXPUNGE");
  602. imap4resp(imap);
  603. }
  604. }
  605. //
  606. // connect to imap4 server, sync mailbox
  607. //
  608. static char*
  609. imap4sync(Mailbox *mb, int doplumb)
  610. {
  611. char *err;
  612. Imap *imap;
  613. imap = mb->aux;
  614. if(err = imap4dial(imap)){
  615. mb->waketime = time(0) + imap->refreshtime;
  616. return err;
  617. }
  618. if((err = imap4read(imap, mb, doplumb)) == nil){
  619. imap4purge(imap, mb);
  620. mb->d->atime = mb->d->mtime = time(0);
  621. }
  622. /*
  623. * don't hang up; leave connection open for next time.
  624. */
  625. // imap4hangup(imap);
  626. mb->waketime = time(0) + imap->refreshtime;
  627. return err;
  628. }
  629. static char Eimap4ctl[] = "bad imap4 control message";
  630. static char*
  631. imap4ctl(Mailbox *mb, int argc, char **argv)
  632. {
  633. int n;
  634. Imap *imap;
  635. imap = mb->aux;
  636. if(argc < 1)
  637. return Eimap4ctl;
  638. if(argc==1 && strcmp(argv[0], "debug")==0){
  639. imap->debug = 1;
  640. return nil;
  641. }
  642. if(argc==1 && strcmp(argv[0], "nodebug")==0){
  643. imap->debug = 0;
  644. return nil;
  645. }
  646. if(argc==1 && strcmp(argv[0], "thumbprint")==0){
  647. if(imap->thumb)
  648. freeThumbprints(imap->thumb);
  649. imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
  650. }
  651. if(strcmp(argv[0], "refresh")==0){
  652. if(argc==1){
  653. imap->refreshtime = 60;
  654. return nil;
  655. }
  656. if(argc==2){
  657. n = atoi(argv[1]);
  658. if(n < 15)
  659. return Eimap4ctl;
  660. imap->refreshtime = n;
  661. return nil;
  662. }
  663. }
  664. return Eimap4ctl;
  665. }
  666. //
  667. // free extra memory associated with mb
  668. //
  669. static void
  670. imap4close(Mailbox *mb)
  671. {
  672. Imap *imap;
  673. imap = mb->aux;
  674. free(imap->freep);
  675. free(imap->base);
  676. free(imap->uid);
  677. if(imap->fd >= 0)
  678. close(imap->fd);
  679. free(imap);
  680. }
  681. //
  682. // open mailboxes of the form /imap/host/user
  683. //
  684. char*
  685. imap4mbox(Mailbox *mb, char *path)
  686. {
  687. char *f[10];
  688. int mustssl, nf;
  689. Imap *imap;
  690. quotefmtinstall();
  691. fmtinstall('Z', doublequote);
  692. if(strncmp(path, "/imap/", 6) != 0 && strncmp(path, "/imaps/", 7) != 0)
  693. return Enotme;
  694. mustssl = (strncmp(path, "/imaps/", 7) == 0);
  695. path = strdup(path);
  696. if(path == nil)
  697. return "out of memory";
  698. nf = getfields(path, f, 5, 0, "/");
  699. if(nf < 3){
  700. free(path);
  701. return "bad imap path syntax /imap[s]/system[/user[/mailbox]]";
  702. }
  703. imap = emalloc(sizeof(*imap));
  704. imap->fd = -1;
  705. imap->debug = debug;
  706. imap->freep = path;
  707. imap->mustssl = mustssl;
  708. imap->host = f[2];
  709. if(nf < 4)
  710. imap->user = nil;
  711. else
  712. imap->user = f[3];
  713. if(nf < 5)
  714. imap->mbox = "Inbox";
  715. else
  716. imap->mbox = f[4];
  717. imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
  718. mb->aux = imap;
  719. mb->sync = imap4sync;
  720. mb->close = imap4close;
  721. mb->ctl = imap4ctl;
  722. mb->d = emalloc(sizeof(*mb->d));
  723. //mb->fetch = imap4fetch;
  724. return nil;
  725. }
  726. //
  727. // Formatter for %"
  728. // Use double quotes to protect white space, frogs, \ and "
  729. //
  730. enum
  731. {
  732. Qok = 0,
  733. Qquote,
  734. Qbackslash,
  735. };
  736. static int
  737. needtoquote(Rune r)
  738. {
  739. if(r >= Runeself)
  740. return Qquote;
  741. if(r <= ' ')
  742. return Qquote;
  743. if(r=='\\' || r=='"')
  744. return Qbackslash;
  745. return Qok;
  746. }
  747. int
  748. doublequote(Fmt *f)
  749. {
  750. char *s, *t;
  751. int w, quotes;
  752. Rune r;
  753. s = va_arg(f->args, char*);
  754. if(s == nil || *s == '\0')
  755. return fmtstrcpy(f, "\"\"");
  756. quotes = 0;
  757. for(t=s; *t; t+=w){
  758. w = chartorune(&r, t);
  759. quotes |= needtoquote(r);
  760. }
  761. if(quotes == 0)
  762. return fmtstrcpy(f, s);
  763. fmtrune(f, '"');
  764. for(t=s; *t; t+=w){
  765. w = chartorune(&r, t);
  766. if(needtoquote(r) == Qbackslash)
  767. fmtrune(f, '\\');
  768. fmtrune(f, r);
  769. }
  770. return fmtrune(f, '"');
  771. }