imap4.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  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. fprint(2, "unexpected line: %s\n", p);
  292. }
  293. }
  294. snprint(error, sizeof error, "i/o error: %r\n");
  295. return error;
  296. }
  297. static int
  298. isokay(char *resp)
  299. {
  300. return strncmp(resp, "OK", 2)==0;
  301. }
  302. //
  303. // log in to IMAP4 server, select mailbox, no SSL at the moment
  304. //
  305. static char*
  306. imap4login(Imap *imap)
  307. {
  308. char *s;
  309. UserPasswd *up;
  310. imap->tag = 0;
  311. s = imap4resp(imap);
  312. if(!isokay(s))
  313. return "error in initial IMAP handshake";
  314. if(imap->user != nil)
  315. up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q user=%q", imap->host, imap->user);
  316. else
  317. up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q", imap->host);
  318. if(up == nil)
  319. return "cannot find IMAP password";
  320. imap->tag = 1;
  321. imap4cmd(imap, "LOGIN %Z %Z", up->user, up->passwd);
  322. free(up);
  323. if(!isokay(s = imap4resp(imap)))
  324. return s;
  325. imap4cmd(imap, "SELECT %Z", imap->mbox);
  326. if(!isokay(s = imap4resp(imap)))
  327. return s;
  328. return nil;
  329. }
  330. //
  331. // push tls onto a connection
  332. //
  333. int
  334. mypushtls(int fd)
  335. {
  336. int p[2];
  337. char buf[10];
  338. if(pipe(p) < 0)
  339. return -1;
  340. switch(fork()){
  341. case -1:
  342. close(p[0]);
  343. close(p[1]);
  344. return -1;
  345. case 0:
  346. close(p[1]);
  347. dup(p[0], 0);
  348. dup(p[0], 1);
  349. sprint(buf, "/fd/%d", fd);
  350. execl("/bin/tlsrelay", "tlsrelay", "-f", buf, nil);
  351. _exits(nil);
  352. default:
  353. break;
  354. }
  355. close(fd);
  356. close(p[0]);
  357. return p[1];
  358. }
  359. //
  360. // dial and handshake with the imap server
  361. //
  362. static char*
  363. imap4dial(Imap *imap)
  364. {
  365. char *err, *port;
  366. uchar digest[SHA1dlen];
  367. int sfd;
  368. TLSconn conn;
  369. if(imap->fd >= 0){
  370. imap4cmd(imap, "noop");
  371. if(isokay(imap4resp(imap)))
  372. return nil;
  373. close(imap->fd);
  374. imap->fd = -1;
  375. }
  376. if(imap->mustssl)
  377. port = "imaps";
  378. else
  379. port = "imap4";
  380. if((imap->fd = dial(netmkaddr(imap->host, "net", port), 0, 0, 0)) < 0)
  381. return geterrstr();
  382. if(imap->mustssl){
  383. memset(&conn, 0, sizeof conn);
  384. sfd = tlsClient(imap->fd, &conn);
  385. if(sfd < 0)
  386. sysfatal("tlsClient: %r");
  387. if(conn.cert==nil || conn.certlen <= 0)
  388. sysfatal("server did not provide TLS certificate");
  389. sha1(conn.cert, conn.certlen, digest, nil);
  390. if(!imap->thumb || !okThumbprint(digest, imap->thumb)){
  391. fmtinstall('H', encodefmt);
  392. sysfatal("server certificate %.*H not recognized", SHA1dlen, digest);
  393. }
  394. free(conn.cert);
  395. close(imap->fd);
  396. imap->fd = sfd;
  397. if(imap->debug){
  398. char fn[128];
  399. int fd;
  400. snprint(fn, sizeof fn, "%s/ctl", conn.dir);
  401. fd = open(fn, ORDWR);
  402. if(fd < 0)
  403. fprint(2, "opening ctl: %r\n");
  404. if(fprint(fd, "debug") < 0)
  405. fprint(2, "writing ctl: %r\n");
  406. close(fd);
  407. }
  408. }
  409. Binit(&imap->bin, imap->fd, OREAD);
  410. Binit(&imap->bout, imap->fd, OWRITE);
  411. if(err = imap4login(imap)) {
  412. close(imap->fd);
  413. return err;
  414. }
  415. return nil;
  416. }
  417. //
  418. // close connection
  419. //
  420. static void
  421. imap4hangup(Imap *imap)
  422. {
  423. imap4cmd(imap, "LOGOUT");
  424. imap4resp(imap);
  425. close(imap->fd);
  426. }
  427. //
  428. // download a single message
  429. //
  430. static char*
  431. imap4fetch(Mailbox *mb, Message *m)
  432. {
  433. int i;
  434. char *p, *s, sdigest[2*SHA1dlen+1];
  435. Imap *imap;
  436. imap = mb->aux;
  437. imap->size = 0;
  438. if(!isokay(s = imap4resp(imap)))
  439. return s;
  440. p = imap->base;
  441. if(p == nil)
  442. return "did not get message body";
  443. removecr(p);
  444. free(m->start);
  445. m->start = p;
  446. m->end = p+strlen(p);
  447. m->bend = m->rbend = m->end;
  448. m->header = m->start;
  449. imap->base = nil;
  450. imap->data = nil;
  451. parse(m, 0, mb, 1);
  452. // digest headers
  453. sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
  454. for(i = 0; i < SHA1dlen; i++)
  455. sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
  456. m->sdigest = s_copy(sdigest);
  457. return nil;
  458. }
  459. //
  460. // check for new messages on imap4 server
  461. // download new messages, mark deleted messages
  462. //
  463. static char*
  464. imap4read(Imap *imap, Mailbox *mb, int doplumb)
  465. {
  466. char *s;
  467. int i, ignore, nnew, t;
  468. Message *m, *next, **l;
  469. imap4cmd(imap, "STATUS %Z (MESSAGES UIDVALIDITY)", imap->mbox);
  470. if(!isokay(s = imap4resp(imap)))
  471. return s;
  472. imap->nuid = 0;
  473. imap->uid = erealloc(imap->uid, imap->nmsg*sizeof(imap->uid[0]));
  474. imap->muid = imap->nmsg;
  475. if(imap->nmsg > 0){
  476. imap4cmd(imap, "UID FETCH 1:* UID");
  477. if(!isokay(s = imap4resp(imap)))
  478. return s;
  479. }
  480. l = &mb->root->part;
  481. for(i=0; i<imap->nuid; i++){
  482. ignore = 0;
  483. while(*l != nil){
  484. if((*l)->imapuid == imap->uid[i]){
  485. ignore = 1;
  486. l = &(*l)->next;
  487. break;
  488. }else{
  489. // old mail, we don't have it anymore
  490. if(doplumb)
  491. mailplumb(mb, *l, 1);
  492. (*l)->inmbox = 0;
  493. (*l)->deleted = 1;
  494. l = &(*l)->next;
  495. }
  496. }
  497. if(ignore)
  498. continue;
  499. // new message
  500. m = newmessage(mb->root);
  501. m->mallocd = 1;
  502. m->inmbox = 1;
  503. m->imapuid = imap->uid[i];
  504. // add to chain, will download soon
  505. *l = m;
  506. l = &m->next;
  507. }
  508. // whatever is left at the end of the chain is gone
  509. while(*l != nil){
  510. if(doplumb)
  511. mailplumb(mb, *l, 1);
  512. (*l)->inmbox = 0;
  513. (*l)->deleted = 1;
  514. l = &(*l)->next;
  515. }
  516. // download new messages
  517. t = imap->tag;
  518. if(pipeline)
  519. switch(rfork(RFPROC|RFMEM)){
  520. case -1:
  521. sysfatal("rfork: %r");
  522. default:
  523. break;
  524. case 0:
  525. for(m = mb->root->part; m != nil; m = m->next){
  526. if(m->start != nil)
  527. continue;
  528. if(imap->debug)
  529. fprint(2, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
  530. t, (ulong)m->imapuid);
  531. Bprint(&imap->bout, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
  532. t++, (ulong)m->imapuid);
  533. }
  534. Bflush(&imap->bout);
  535. _exits(nil);
  536. }
  537. nnew = 0;
  538. for(m=mb->root->part; m!=nil; m=next){
  539. next = m->next;
  540. if(m->start != nil)
  541. continue;
  542. if(!pipeline){
  543. Bprint(&imap->bout, "9X%lud UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
  544. (ulong)imap->tag, (ulong)m->imapuid);
  545. Bflush(&imap->bout);
  546. }
  547. if(s = imap4fetch(mb, m)){
  548. // message disappeared? unchain
  549. fprint(2, "download %lud: %s\n", (ulong)m->imapuid, s);
  550. delmessage(mb, m);
  551. mb->root->subname--;
  552. continue;
  553. }
  554. nnew++;
  555. if(doplumb)
  556. mailplumb(mb, m, 0);
  557. }
  558. if(pipeline)
  559. waitpid();
  560. if(nnew || mb->vers == 0){
  561. mb->vers++;
  562. henter(PATH(0, Qtop), mb->name,
  563. (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
  564. }
  565. return nil;
  566. }
  567. //
  568. // sync mailbox
  569. //
  570. static void
  571. imap4purge(Imap *imap, Mailbox *mb)
  572. {
  573. int ndel;
  574. Message *m, *next;
  575. ndel = 0;
  576. for(m=mb->root->part; m!=nil; m=next){
  577. next = m->next;
  578. if(m->deleted && m->refs==0){
  579. if(m->inmbox && (ulong)(m->imapuid>>32)==imap->validity){
  580. imap4cmd(imap, "UID STORE %lud +FLAGS (\\Deleted)", (ulong)m->imapuid);
  581. if(isokay(imap4resp(imap))){
  582. ndel++;
  583. delmessage(mb, m);
  584. }
  585. }else
  586. delmessage(mb, m);
  587. }
  588. }
  589. if(ndel){
  590. imap4cmd(imap, "EXPUNGE");
  591. imap4resp(imap);
  592. }
  593. }
  594. //
  595. // connect to imap4 server, sync mailbox
  596. //
  597. static char*
  598. imap4sync(Mailbox *mb, int doplumb)
  599. {
  600. char *err;
  601. Imap *imap;
  602. imap = mb->aux;
  603. if(err = imap4dial(imap)){
  604. mb->waketime = time(0) + imap->refreshtime;
  605. return err;
  606. }
  607. if((err = imap4read(imap, mb, doplumb)) == nil){
  608. imap4purge(imap, mb);
  609. mb->d->atime = mb->d->mtime = time(0);
  610. }
  611. /*
  612. * don't hang up; leave connection open for next time.
  613. */
  614. // imap4hangup(imap);
  615. mb->waketime = time(0) + imap->refreshtime;
  616. return err;
  617. }
  618. static char Eimap4ctl[] = "bad imap4 control message";
  619. static char*
  620. imap4ctl(Mailbox *mb, int argc, char **argv)
  621. {
  622. int n;
  623. Imap *imap;
  624. imap = mb->aux;
  625. if(argc < 1)
  626. return Eimap4ctl;
  627. if(argc==1 && strcmp(argv[0], "debug")==0){
  628. imap->debug = 1;
  629. return nil;
  630. }
  631. if(argc==1 && strcmp(argv[0], "nodebug")==0){
  632. imap->debug = 0;
  633. return nil;
  634. }
  635. if(argc==1 && strcmp(argv[0], "thumbprint")==0){
  636. if(imap->thumb)
  637. freeThumbprints(imap->thumb);
  638. imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
  639. }
  640. if(strcmp(argv[0], "refresh")==0){
  641. if(argc==1){
  642. imap->refreshtime = 60;
  643. return nil;
  644. }
  645. if(argc==2){
  646. n = atoi(argv[1]);
  647. if(n < 15)
  648. return Eimap4ctl;
  649. imap->refreshtime = n;
  650. return nil;
  651. }
  652. }
  653. return Eimap4ctl;
  654. }
  655. //
  656. // free extra memory associated with mb
  657. //
  658. static void
  659. imap4close(Mailbox *mb)
  660. {
  661. Imap *imap;
  662. imap = mb->aux;
  663. free(imap->freep);
  664. free(imap->base);
  665. free(imap->uid);
  666. if(imap->fd >= 0)
  667. close(imap->fd);
  668. free(imap);
  669. }
  670. //
  671. // open mailboxes of the form /imap/host/user
  672. //
  673. char*
  674. imap4mbox(Mailbox *mb, char *path)
  675. {
  676. char *f[10];
  677. int mustssl, nf;
  678. Imap *imap;
  679. quotefmtinstall();
  680. fmtinstall('Z', doublequote);
  681. if(strncmp(path, "/imap/", 6) != 0 && strncmp(path, "/imaps/", 7) != 0)
  682. return Enotme;
  683. mustssl = (strncmp(path, "/imaps/", 7) == 0);
  684. path = strdup(path);
  685. if(path == nil)
  686. return "out of memory";
  687. nf = getfields(path, f, 5, 0, "/");
  688. if(nf < 3){
  689. free(path);
  690. return "bad imap path syntax /imap[s]/system[/user[/mailbox]]";
  691. }
  692. imap = emalloc(sizeof(*imap));
  693. imap->fd = -1;
  694. imap->debug = debug;
  695. imap->freep = path;
  696. imap->mustssl = mustssl;
  697. imap->host = f[2];
  698. if(nf < 4)
  699. imap->user = nil;
  700. else
  701. imap->user = f[3];
  702. if(nf < 5)
  703. imap->mbox = "Inbox";
  704. else
  705. imap->mbox = f[4];
  706. imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
  707. mb->aux = imap;
  708. mb->sync = imap4sync;
  709. mb->close = imap4close;
  710. mb->ctl = imap4ctl;
  711. mb->d = emalloc(sizeof(*mb->d));
  712. //mb->fetch = imap4fetch;
  713. return nil;
  714. }
  715. //
  716. // Formatter for %"
  717. // Use double quotes to protect white space, frogs, \ and "
  718. //
  719. enum
  720. {
  721. Qok = 0,
  722. Qquote,
  723. Qbackslash,
  724. };
  725. static int
  726. needtoquote(Rune r)
  727. {
  728. if(r >= Runeself)
  729. return Qquote;
  730. if(r <= ' ')
  731. return Qquote;
  732. if(r=='\\' || r=='"')
  733. return Qbackslash;
  734. return Qok;
  735. }
  736. int
  737. doublequote(Fmt *f)
  738. {
  739. char *s, *t;
  740. int w, quotes;
  741. Rune r;
  742. s = va_arg(f->args, char*);
  743. if(s == nil || *s == '\0')
  744. return fmtstrcpy(f, "\"\"");
  745. quotes = 0;
  746. for(t=s; *t; t+=w){
  747. w = chartorune(&r, t);
  748. quotes |= needtoquote(r);
  749. }
  750. if(quotes == 0)
  751. return fmtstrcpy(f, s);
  752. fmtrune(f, '"');
  753. for(t=s; *t; t+=w){
  754. w = chartorune(&r, t);
  755. if(needtoquote(r) == Qbackslash)
  756. fmtrune(f, '\\');
  757. fmtrune(f, r);
  758. }
  759. return fmtrune(f, '"');
  760. }