imap4.c 16 KB

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