imap.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  1. /* $Id$
  2. *
  3. * isync - IMAP4 to maildir mailbox synchronizer
  4. * Copyright (C) 2000-1 Michael R. Elkins <me@mutt.org>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19. */
  20. #include <assert.h>
  21. #include <unistd.h>
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <errno.h>
  25. #include <string.h>
  26. #include <ctype.h>
  27. #include <sys/socket.h>
  28. #include <netinet/in.h>
  29. #include <arpa/inet.h>
  30. #include <netdb.h>
  31. #if HAVE_LIBSSL
  32. #include <openssl/err.h>
  33. #endif
  34. #include "isync.h"
  35. const char *Flags[] = {
  36. "\\Seen",
  37. "\\Answered",
  38. "\\Deleted",
  39. "\\Flagged",
  40. "\\Recent",
  41. "\\Draft"
  42. };
  43. void
  44. free_message (message_t * msg)
  45. {
  46. message_t *tmp;
  47. while (msg)
  48. {
  49. tmp = msg;
  50. msg = msg->next;
  51. if (tmp->file)
  52. free (tmp->file);
  53. free (tmp);
  54. }
  55. }
  56. #if HAVE_LIBSSL
  57. #define MAX_DEPTH 1
  58. SSL_CTX *SSLContext = 0;
  59. /* this gets called when a certificate is to be verified */
  60. static int
  61. verify_cert (SSL * ssl)
  62. {
  63. X509 *cert;
  64. int err;
  65. char buf[256];
  66. int ret = -1;
  67. BIO *bio;
  68. cert = SSL_get_peer_certificate (ssl);
  69. if (!cert)
  70. {
  71. puts ("Error, no server certificate");
  72. return -1;
  73. }
  74. err = SSL_get_verify_result (ssl);
  75. if (err == X509_V_OK)
  76. return 0;
  77. printf ("Error, can't verify certificate: %s (%d)\n",
  78. X509_verify_cert_error_string (err), err);
  79. X509_NAME_oneline (X509_get_subject_name (cert), buf, sizeof (buf));
  80. printf ("\nSubject: %s\n", buf);
  81. X509_NAME_oneline (X509_get_issuer_name (cert), buf, sizeof (buf));
  82. printf ("Issuer: %s\n", buf);
  83. bio = BIO_new (BIO_s_mem ());
  84. ASN1_TIME_print (bio, X509_get_notBefore (cert));
  85. memset (buf, 0, sizeof (buf));
  86. BIO_read (bio, buf, sizeof (buf) - 1);
  87. printf ("Valid from: %s\n", buf);
  88. ASN1_TIME_print (bio, X509_get_notAfter (cert));
  89. memset (buf, 0, sizeof (buf));
  90. BIO_read (bio, buf, sizeof (buf) - 1);
  91. BIO_free (bio);
  92. printf (" to: %s\n", buf);
  93. printf
  94. ("\n*** WARNING *** There is no way to verify this certificate. It is\n"
  95. " possible that a hostile attacker has replaced the\n"
  96. " server certificate. Continue at your own risk!\n");
  97. printf ("\nAccept this certificate anyway? [no]: ");
  98. fflush (stdout);
  99. if (fgets (buf, sizeof (buf), stdin) && (buf[0] == 'y' || buf[0] == 'Y'))
  100. {
  101. ret = 0;
  102. puts ("\n*** Fine, but don't say I didn't warn you!\n");
  103. }
  104. return ret;
  105. }
  106. static int
  107. init_ssl (config_t * conf)
  108. {
  109. SSL_METHOD *method;
  110. int options = 0;
  111. if (!conf->cert_file)
  112. {
  113. puts ("Error, CertificateFile not defined");
  114. return -1;
  115. }
  116. SSL_library_init ();
  117. SSL_load_error_strings ();
  118. if (conf->use_tlsv1 && !conf->use_sslv2 && !conf->use_sslv3)
  119. method = TLSv1_client_method ();
  120. else
  121. method = SSLv23_client_method ();
  122. SSLContext = SSL_CTX_new (method);
  123. if (access (conf->cert_file, F_OK))
  124. {
  125. if (errno != ENOENT)
  126. {
  127. perror ("access");
  128. return -1;
  129. }
  130. puts
  131. ("*** Warning, CertificateFile doesn't exist, can't verify server certificates");
  132. }
  133. else
  134. if (!SSL_CTX_load_verify_locations
  135. (SSLContext, conf->cert_file, NULL))
  136. {
  137. printf ("Error, SSL_CTX_load_verify_locations: %s\n",
  138. ERR_error_string (ERR_get_error (), 0));
  139. return -1;
  140. }
  141. if (!conf->use_sslv2)
  142. options |= SSL_OP_NO_SSLv2;
  143. if (!conf->use_sslv3)
  144. options |= SSL_OP_NO_SSLv3;
  145. if (!conf->use_tlsv1)
  146. options |= SSL_OP_NO_TLSv1;
  147. SSL_CTX_set_options (SSLContext, options);
  148. /* we check the result of the verification after SSL_connect() */
  149. SSL_CTX_set_verify (SSLContext, SSL_VERIFY_NONE, 0);
  150. return 0;
  151. }
  152. #endif /* HAVE_LIBSSL */
  153. static int
  154. socket_read (Socket_t * sock, char *buf, size_t len)
  155. {
  156. #if HAVE_LIBSSL
  157. if (sock->use_ssl)
  158. return SSL_read (sock->ssl, buf, len);
  159. #endif
  160. return read (sock->fd, buf, len);
  161. }
  162. static int
  163. socket_write (Socket_t * sock, char *buf, size_t len)
  164. {
  165. #if HAVE_LIBSSL
  166. if (sock->use_ssl)
  167. return SSL_write (sock->ssl, buf, len);
  168. #endif
  169. return write (sock->fd, buf, len);
  170. }
  171. static void
  172. socket_perror (const char *func, Socket_t *sock, int ret)
  173. {
  174. #if HAVE_LIBSSL
  175. int err;
  176. if (sock->use_ssl)
  177. {
  178. switch ((err = SSL_get_error (sock->ssl, ret)))
  179. {
  180. case SSL_ERROR_SYSCALL:
  181. case SSL_ERROR_SSL:
  182. if ((err = ERR_get_error ()) == 0)
  183. {
  184. if (ret == 0)
  185. fprintf (stderr, "SSL_%s:got EOF\n", func);
  186. else
  187. fprintf (stderr, "SSL_%s:%d:%s\n", func,
  188. errno, strerror (errno));
  189. }
  190. else
  191. fprintf (stderr, "SSL_%s:%d:%s\n", func, err,
  192. ERR_error_string (err, 0));
  193. return;
  194. default:
  195. fprintf (stderr, "SSL_%s:%d:unhandled SSL error\n", func, err);
  196. break;
  197. }
  198. return;
  199. }
  200. #endif
  201. perror (func);
  202. }
  203. /* simple line buffering */
  204. static int
  205. buffer_gets (buffer_t * b, char **s)
  206. {
  207. int n;
  208. int start = b->offset;
  209. *s = b->buf + start;
  210. for (;;)
  211. {
  212. /* make sure we have enough data to read the \r\n sequence */
  213. if (b->offset + 1 >= b->bytes)
  214. {
  215. if (start != 0)
  216. {
  217. /* shift down used bytes */
  218. *s = b->buf;
  219. assert (start <= b->bytes);
  220. n = b->bytes - start;
  221. if (n)
  222. memmove (b->buf, b->buf + start, n);
  223. b->offset -= start;
  224. b->bytes = n;
  225. start = 0;
  226. }
  227. n =
  228. socket_read (b->sock, b->buf + b->bytes,
  229. sizeof (b->buf) - b->bytes);
  230. if (n <= 0)
  231. {
  232. socket_perror ("read", b->sock, n);
  233. return -1;
  234. }
  235. b->bytes += n;
  236. }
  237. if (b->buf[b->offset] == '\r')
  238. {
  239. assert (b->offset + 1 < b->bytes);
  240. if (b->buf[b->offset + 1] == '\n')
  241. {
  242. b->buf[b->offset] = 0; /* terminate the string */
  243. b->offset += 2; /* next line */
  244. // assert (strchr (*s, '\r') == 0);
  245. return 0;
  246. }
  247. }
  248. b->offset++;
  249. }
  250. /* not reached */
  251. }
  252. static int
  253. parse_fetch (imap_t * imap, list_t * list)
  254. {
  255. list_t *tmp;
  256. unsigned int uid = 0;
  257. unsigned int mask = 0;
  258. unsigned int size = 0;
  259. message_t *cur;
  260. if (!is_list (list))
  261. return -1;
  262. for (tmp = list->child; tmp; tmp = tmp->next)
  263. {
  264. if (is_atom (tmp))
  265. {
  266. if (!strcmp ("UID", tmp->val))
  267. {
  268. tmp = tmp->next;
  269. if (is_atom (tmp))
  270. {
  271. uid = atoi (tmp->val);
  272. if (uid < imap->minuid)
  273. {
  274. /* already saw this message */
  275. return 0;
  276. }
  277. else if (uid > imap->maxuid)
  278. imap->maxuid = uid;
  279. }
  280. else
  281. puts ("Error, unable to parse UID");
  282. }
  283. else if (!strcmp ("FLAGS", tmp->val))
  284. {
  285. tmp = tmp->next;
  286. if (is_list (tmp))
  287. {
  288. list_t *flags = tmp->child;
  289. for (; flags; flags = flags->next)
  290. {
  291. if (is_atom (flags))
  292. {
  293. if (!strcmp ("\\Seen", flags->val))
  294. mask |= D_SEEN;
  295. else if (!strcmp ("\\Flagged", flags->val))
  296. mask |= D_FLAGGED;
  297. else if (!strcmp ("\\Deleted", flags->val))
  298. mask |= D_DELETED;
  299. else if (!strcmp ("\\Answered", flags->val))
  300. mask |= D_ANSWERED;
  301. else if (!strcmp ("\\Draft", flags->val))
  302. mask |= D_DRAFT;
  303. else if (!strcmp ("\\Recent", flags->val))
  304. mask |= D_RECENT;
  305. else
  306. printf ("Warning, unknown flag %s\n",
  307. flags->val);
  308. }
  309. else
  310. puts ("Error, unable to parse FLAGS list");
  311. }
  312. }
  313. else
  314. puts ("Error, unable to parse FLAGS");
  315. }
  316. else if (!strcmp ("RFC822.SIZE", tmp->val))
  317. {
  318. tmp = tmp->next;
  319. if (is_atom (tmp))
  320. size = atol (tmp->val);
  321. }
  322. }
  323. }
  324. cur = calloc (1, sizeof (message_t));
  325. cur->next = imap->msgs;
  326. imap->msgs = cur;
  327. if (mask & D_DELETED)
  328. imap->deleted++;
  329. cur->uid = uid;
  330. cur->flags = mask;
  331. cur->size = size;
  332. return 0;
  333. }
  334. static void
  335. parse_response_code (imap_t * imap, char *s)
  336. {
  337. char *arg;
  338. if (*s != '[')
  339. return; /* no response code */
  340. s++;
  341. arg = next_arg (&s);
  342. if (!strcmp ("UIDVALIDITY", arg))
  343. {
  344. arg = next_arg (&s);
  345. imap->uidvalidity = atol (arg);
  346. }
  347. else if (!strcmp ("ALERT", arg))
  348. {
  349. /* RFC2060 says that these messages MUST be displayed
  350. * to the user
  351. */
  352. fputs ("***ALERT*** ", stdout);
  353. puts (s);
  354. }
  355. }
  356. static int
  357. imap_exec (imap_t * imap, const char *fmt, ...)
  358. {
  359. va_list ap;
  360. char tmp[256];
  361. char buf[256];
  362. char *cmd;
  363. char *arg;
  364. char *arg1;
  365. int n;
  366. va_start (ap, fmt);
  367. vsnprintf (tmp, sizeof (tmp), fmt, ap);
  368. va_end (ap);
  369. snprintf (buf, sizeof (buf), "%d %s\r\n", ++Tag, tmp);
  370. if (Verbose)
  371. fputs (buf, stdout);
  372. n = socket_write (imap->sock, buf, strlen (buf));
  373. if (n <= 0)
  374. {
  375. socket_perror ("write", imap->sock, n);
  376. return -1;
  377. }
  378. for (;;)
  379. {
  380. if (buffer_gets (imap->buf, &cmd))
  381. return -1;
  382. if (Verbose)
  383. puts (cmd);
  384. arg = next_arg (&cmd);
  385. if (*arg == '*')
  386. {
  387. arg = next_arg (&cmd);
  388. if (!arg)
  389. {
  390. puts ("Error, unable to parse untagged command");
  391. return -1;
  392. }
  393. if (!strcmp ("NAMESPACE", arg))
  394. {
  395. imap->ns_personal = parse_list (cmd, &cmd);
  396. imap->ns_other = parse_list (cmd, &cmd);
  397. imap->ns_shared = parse_list (cmd, 0);
  398. }
  399. else if (!strcmp ("OK", arg) || !strcmp ("BAD", arg) ||
  400. !strcmp ("NO", arg) || !strcmp ("PREAUTH", arg) ||
  401. !strcmp ("BYE", arg))
  402. {
  403. parse_response_code (imap, cmd);
  404. }
  405. else if (!strcmp ("CAPABILITY", arg))
  406. {
  407. #if HAVE_LIBSSL
  408. while ((arg = next_arg (&cmd)))
  409. {
  410. if (!strcmp ("STARTTLS", arg))
  411. imap->have_starttls = 1;
  412. else if (!strcmp ("AUTH=CRAM-MD5", arg))
  413. imap->have_cram = 1;
  414. else if (!strcmp ("NAMESPACE", arg))
  415. imap->have_namespace = 1;
  416. }
  417. #endif
  418. }
  419. else if ((arg1 = next_arg (&cmd)))
  420. {
  421. if (!strcmp ("EXISTS", arg1))
  422. imap->count = atoi (arg);
  423. else if (!strcmp ("RECENT", arg1))
  424. imap->recent = atoi (arg);
  425. else if (!strcmp ("FETCH", arg1))
  426. {
  427. list_t *list;
  428. list = parse_list (cmd, 0);
  429. if (parse_fetch (imap, list))
  430. {
  431. free_list (list);
  432. return -1;
  433. }
  434. free_list (list);
  435. }
  436. }
  437. else
  438. {
  439. puts ("Error, unable to parse untagged command");
  440. return -1;
  441. }
  442. }
  443. #if HAVE_LIBSSL
  444. else if (*arg == '+')
  445. {
  446. char *resp;
  447. if (!imap->cram)
  448. {
  449. puts ("Error, not doing CRAM-MD5 authentication");
  450. return -1;
  451. }
  452. resp = cram (cmd, imap->box->user, imap->box->pass);
  453. n = socket_write (imap->sock, resp, strlen (resp));
  454. if (n <= 0)
  455. {
  456. socket_perror ("write", imap->sock, n);
  457. return -1;
  458. }
  459. if (Verbose)
  460. puts (resp);
  461. n = socket_write (imap->sock, "\r\n", 2);
  462. if (n <= 0)
  463. {
  464. socket_perror ("write", imap->sock, n);
  465. return -1;
  466. }
  467. free (resp);
  468. imap->cram = 0;
  469. }
  470. #endif
  471. else if ((size_t) atol (arg) != Tag)
  472. {
  473. puts ("wrong tag");
  474. return -1;
  475. }
  476. else
  477. {
  478. arg = next_arg (&cmd);
  479. parse_response_code (imap, cmd);
  480. if (!strcmp ("OK", arg))
  481. return 0;
  482. return -1;
  483. }
  484. }
  485. /* not reached */
  486. }
  487. /* `box' is the config info for the maildrop to sync. `minuid' is the
  488. * minimum UID to consider. in normal mode this will be 1, but in --fast
  489. * mode we only fetch messages newer than the last one seen in the local
  490. * mailbox.
  491. */
  492. imap_t *
  493. imap_open (config_t * box, unsigned int minuid, imap_t * imap)
  494. {
  495. int ret;
  496. int s;
  497. struct sockaddr_in sin;
  498. struct hostent *he;
  499. int reuse = 0;
  500. #if HAVE_LIBSSL
  501. int use_ssl = 0;
  502. #endif
  503. if (imap)
  504. {
  505. /* determine whether or not we can reuse the existing session */
  506. if (strcmp (box->host, imap->box->host) ||
  507. strcmp (box->user, imap->box->user) ||
  508. box->port != imap->box->port
  509. #if HAVE_LIBSSL
  510. /* ensure that security requirements are met */
  511. || (box->require_ssl ^ imap->box->require_ssl)
  512. || (box->require_cram ^ imap->box->require_cram)
  513. #endif
  514. )
  515. {
  516. /* can't reuse */
  517. imap_close (imap);
  518. imap = 0;
  519. }
  520. else
  521. {
  522. reuse = 1;
  523. /* reset mailbox-specific state info */
  524. imap->recent = 0;
  525. imap->deleted = 0;
  526. imap->count = 0;
  527. imap->maxuid = 0;
  528. free_message (imap->msgs);
  529. imap->msgs = 0;
  530. }
  531. }
  532. if (!imap)
  533. {
  534. imap = calloc (1, sizeof (imap_t));
  535. imap->sock = calloc (1, sizeof (Socket_t));
  536. imap->buf = calloc (1, sizeof (buffer_t));
  537. imap->buf->sock = imap->sock;
  538. }
  539. imap->box = box;
  540. imap->minuid = minuid;
  541. imap->prefix = "";
  542. if (!reuse)
  543. {
  544. /* open connection to IMAP server */
  545. memset (&sin, 0, sizeof (sin));
  546. sin.sin_port = htons (box->port);
  547. sin.sin_family = AF_INET;
  548. printf ("Resolving %s... ", box->host);
  549. fflush (stdout);
  550. he = gethostbyname (box->host);
  551. if (!he)
  552. {
  553. perror ("gethostbyname");
  554. return 0;
  555. }
  556. puts ("ok");
  557. sin.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
  558. s = socket (PF_INET, SOCK_STREAM, 0);
  559. printf ("Connecting to %s:%hu... ", inet_ntoa (sin.sin_addr),
  560. ntohs (sin.sin_port));
  561. fflush (stdout);
  562. if (connect (s, (struct sockaddr *) &sin, sizeof (sin)))
  563. {
  564. perror ("connect");
  565. exit (1);
  566. }
  567. puts ("ok");
  568. imap->sock->fd = s;
  569. }
  570. do
  571. {
  572. /* if we are reusing the existing connection, we can skip the
  573. * authentication steps.
  574. */
  575. if (!reuse)
  576. {
  577. #if HAVE_LIBSSL
  578. if (box->use_imaps)
  579. use_ssl = 1;
  580. else if (box->use_sslv2 || box->use_sslv3 || box->use_tlsv1)
  581. {
  582. /* let's see what this puppy can do... */
  583. if ((ret = imap_exec (imap, "CAPABILITY")))
  584. break;
  585. /* always try to select SSL support if available */
  586. if (imap->have_starttls)
  587. {
  588. if ((ret = imap_exec (imap, "STARTTLS")))
  589. break;
  590. use_ssl = 1;
  591. }
  592. }
  593. if (!use_ssl)
  594. {
  595. if (box->require_ssl)
  596. {
  597. puts ("Error, SSL support not available");
  598. ret = -1;
  599. break;
  600. }
  601. else
  602. puts ("Warning, SSL support not available");
  603. }
  604. else
  605. {
  606. /* initialize SSL */
  607. if (init_ssl (box))
  608. {
  609. ret = -1;
  610. break;
  611. }
  612. imap->sock->ssl = SSL_new (SSLContext);
  613. SSL_set_fd (imap->sock->ssl, imap->sock->fd);
  614. ret = SSL_connect (imap->sock->ssl);
  615. if (ret <= 0)
  616. {
  617. socket_perror ("connect", imap->sock, ret);
  618. break;
  619. }
  620. /* verify the server certificate */
  621. if ((ret = verify_cert (imap->sock->ssl)))
  622. break;
  623. imap->sock->use_ssl = 1;
  624. puts ("SSL support enabled");
  625. if (box->use_imaps)
  626. if ((ret = imap_exec (imap, "CAPABILITY")))
  627. break;
  628. }
  629. #else
  630. if ((ret = imap_exec (imap, "CAPABILITY")))
  631. break;
  632. #endif
  633. puts ("Logging in...");
  634. #if HAVE_LIBSSL
  635. if (imap->have_cram)
  636. {
  637. puts ("Authenticating with CRAM-MD5");
  638. imap->cram = 1;
  639. if ((ret = imap_exec (imap, "AUTHENTICATE CRAM-MD5")))
  640. break;
  641. }
  642. else if (imap->box->require_cram)
  643. {
  644. puts
  645. ("Error, CRAM-MD5 authentication is not supported by server");
  646. ret = -1;
  647. break;
  648. }
  649. else
  650. #endif
  651. {
  652. #if HAVE_LIBSSL
  653. if (!use_ssl)
  654. #endif
  655. puts
  656. ("*** Warning *** Password is being sent in the clear");
  657. if (
  658. (ret =
  659. imap_exec (imap, "LOGIN \"%s\" \"%s\"", box->user,
  660. box->pass)))
  661. {
  662. puts ("Error, LOGIN failed");
  663. break;
  664. }
  665. }
  666. /* get NAMESPACE info */
  667. if (box->use_namespace && imap->have_namespace)
  668. {
  669. if ((ret = imap_exec (imap, "NAMESPACE")))
  670. break;
  671. }
  672. } /* !reuse */
  673. /* XXX for now assume personal namespace */
  674. if (imap->box->use_namespace && is_list (imap->ns_personal) &&
  675. is_list (imap->ns_personal->child) &&
  676. is_atom (imap->ns_personal->child->child))
  677. {
  678. imap->prefix = imap->ns_personal->child->child->val;
  679. }
  680. fputs ("Selecting mailbox... ", stdout);
  681. fflush (stdout);
  682. if (
  683. (ret =
  684. imap_exec (imap, "SELECT \"%s%s\"", imap->prefix, box->box)))
  685. break;
  686. printf ("%d messages, %d recent\n", imap->count, imap->recent);
  687. puts ("Reading IMAP mailbox index");
  688. if (imap->count > 0)
  689. {
  690. if ((ret = imap_exec (imap, "UID FETCH %d:* (FLAGS RFC822.SIZE)",
  691. imap->minuid)))
  692. break;
  693. }
  694. }
  695. while (0);
  696. if (ret)
  697. {
  698. imap_close (imap);
  699. imap = 0;
  700. }
  701. return imap;
  702. }
  703. void
  704. imap_close (imap_t * imap)
  705. {
  706. imap_exec (imap, "LOGOUT");
  707. close (imap->sock->fd);
  708. free (imap->sock);
  709. free (imap->buf);
  710. free_message (imap->msgs);
  711. memset (imap, 0xff, sizeof (imap_t));
  712. free (imap);
  713. }
  714. /* write a buffer stripping all \r bytes */
  715. static int
  716. write_strip (int fd, char *buf, size_t len)
  717. {
  718. size_t start = 0;
  719. size_t end = 0;
  720. while (start < len)
  721. {
  722. while (end < len && buf[end] != '\r')
  723. end++;
  724. write (fd, buf + start, end - start);
  725. end++;
  726. start = end;
  727. }
  728. return 0;
  729. }
  730. static int
  731. send_server (Socket_t * sock, const char *fmt, ...)
  732. {
  733. char buf[128];
  734. char cmd[128];
  735. va_list ap;
  736. int n;
  737. va_start (ap, fmt);
  738. vsnprintf (buf, sizeof (buf), fmt, ap);
  739. va_end (ap);
  740. snprintf (cmd, sizeof (cmd), "%d %s\r\n", ++Tag, buf);
  741. n = socket_write (sock, cmd, strlen (cmd));
  742. if (n <= 0)
  743. {
  744. socket_perror ("write", sock, n);
  745. return -1;
  746. }
  747. if (Verbose)
  748. fputs (cmd, stdout);
  749. return 0;
  750. }
  751. int
  752. imap_fetch_message (imap_t * imap, unsigned int uid, int fd)
  753. {
  754. char *cmd;
  755. char *arg;
  756. size_t bytes;
  757. size_t n;
  758. char buf[1024];
  759. send_server (imap->sock, "UID FETCH %d BODY.PEEK[]", uid);
  760. for (;;)
  761. {
  762. if (buffer_gets (imap->buf, &cmd))
  763. return -1;
  764. if (Verbose)
  765. puts (cmd);
  766. if (*cmd == '*')
  767. {
  768. /* need to figure out how long the message is
  769. * * <msgno> FETCH (RFC822 {<size>}
  770. */
  771. next_arg (&cmd); /* * */
  772. next_arg (&cmd); /* <msgno> */
  773. next_arg (&cmd); /* FETCH */
  774. while ((arg = next_arg (&cmd)) && *arg != '{')
  775. ;
  776. if (!arg)
  777. {
  778. puts ("parse error getting size");
  779. return -1;
  780. }
  781. bytes = strtol (arg + 1, 0, 10);
  782. // printf ("receiving %d byte message\n", bytes);
  783. /* dump whats left over in the input buffer */
  784. n = imap->buf->bytes - imap->buf->offset;
  785. if (n > bytes)
  786. {
  787. /* the entire message fit in the buffer */
  788. n = bytes;
  789. }
  790. /* ick. we have to strip out the \r\n line endings, so
  791. * i can't just dump the raw bytes to disk.
  792. */
  793. write_strip (fd, imap->buf->buf + imap->buf->offset, n);
  794. bytes -= n;
  795. // printf ("wrote %d buffered bytes\n", n);
  796. /* mark that we used part of the buffer */
  797. imap->buf->offset += n;
  798. /* now read the rest of the message */
  799. while (bytes > 0)
  800. {
  801. n = bytes;
  802. if (n > sizeof (buf))
  803. n = sizeof (buf);
  804. n = socket_read (imap->sock, buf, n);
  805. if (n > 0)
  806. {
  807. // printf("imap_fetch_message:%d:read %d bytes\n", __LINE__, n);
  808. write_strip (fd, buf, n);
  809. bytes -= n;
  810. }
  811. else
  812. {
  813. socket_perror ("read", imap->sock, n);
  814. return -1;
  815. }
  816. }
  817. // puts ("finished fetching msg");
  818. buffer_gets (imap->buf, &cmd);
  819. if (Verbose)
  820. puts (cmd); /* last part of line */
  821. }
  822. else
  823. {
  824. arg = next_arg (&cmd);
  825. if (!arg || (size_t) atoi (arg) != Tag)
  826. {
  827. puts ("wrong tag");
  828. return -1;
  829. }
  830. arg = next_arg (&cmd);
  831. if (!strcmp ("OK", arg))
  832. return 0;
  833. return -1;
  834. }
  835. }
  836. /* not reached */
  837. }
  838. /* add flags to existing flags */
  839. int
  840. imap_set_flags (imap_t * imap, unsigned int uid, unsigned int flags)
  841. {
  842. char buf[256];
  843. int i;
  844. buf[0] = 0;
  845. for (i = 0; i < D_MAX; i++)
  846. {
  847. if (flags & (1 << i))
  848. snprintf (buf + strlen (buf),
  849. sizeof (buf) - strlen (buf), "%s%s",
  850. (buf[0] != 0) ? " " : "", Flags[i]);
  851. }
  852. return imap_exec (imap, "UID STORE %d +FLAGS.SILENT (%s)", uid, buf);
  853. }
  854. int
  855. imap_expunge (imap_t * imap)
  856. {
  857. return imap_exec (imap, "EXPUNGE");
  858. }
  859. int
  860. imap_copy_message (imap_t * imap, unsigned int uid, const char *mailbox)
  861. {
  862. return imap_exec (imap, "UID COPY %u \"%s%s\"", uid, imap->prefix,
  863. mailbox);
  864. }
  865. int
  866. imap_append_message (imap_t * imap, int fd, message_t * msg)
  867. {
  868. char buf[1024];
  869. size_t len;
  870. size_t sofar = 0;
  871. int lines = 0;
  872. char flagstr[128];
  873. char *s;
  874. size_t i;
  875. size_t start, end;
  876. char *arg;
  877. /* ugh, we need to count the number of newlines */
  878. while (sofar < msg->size)
  879. {
  880. len = msg->size - sofar;
  881. if (len > sizeof (buf))
  882. len = sizeof (buf);
  883. len = read (fd, buf, len);
  884. if (len == (size_t) - 1)
  885. {
  886. perror ("read");
  887. return -1;
  888. }
  889. for (i = 0; i < len; i++)
  890. if (buf[i] == '\n')
  891. lines++;
  892. sofar += len;
  893. }
  894. flagstr[0] = 0;
  895. if (msg->flags)
  896. {
  897. strcpy (flagstr, "(");
  898. if (msg->flags & D_DELETED)
  899. snprintf (flagstr + strlen (flagstr),
  900. sizeof (flagstr) - strlen (flagstr), "%s\\Deleted",
  901. flagstr[1] ? " " : "");
  902. if (msg->flags & D_ANSWERED)
  903. snprintf (flagstr + strlen (flagstr),
  904. sizeof (flagstr) - strlen (flagstr), "%s\\Answered",
  905. flagstr[1] ? " " : "");
  906. if (msg->flags & D_SEEN)
  907. snprintf (flagstr + strlen (flagstr),
  908. sizeof (flagstr) - strlen (flagstr), "%s\\Seen",
  909. flagstr[1] ? " " : "");
  910. if (msg->flags & D_FLAGGED)
  911. snprintf (flagstr + strlen (flagstr),
  912. sizeof (flagstr) - strlen (flagstr), "%s\\Flagged",
  913. flagstr[1] ? " " : "");
  914. if (msg->flags & D_DRAFT)
  915. snprintf (flagstr + strlen (flagstr),
  916. sizeof (flagstr) - strlen (flagstr), "%s\\Draft",
  917. flagstr[1] ? " " : "");
  918. snprintf (flagstr + strlen (flagstr),
  919. sizeof (flagstr) - strlen (flagstr), ") ");
  920. }
  921. send_server (imap->sock, "APPEND %s%s %s{%d}",
  922. imap->prefix, imap->box->box, flagstr, msg->size + lines);
  923. if (buffer_gets (imap->buf, &s))
  924. return -1;
  925. if (Verbose)
  926. puts (s);
  927. if (*s != '+')
  928. {
  929. puts ("Error, expected `+' from server (aborting)");
  930. return -1;
  931. }
  932. /* rewind */
  933. lseek (fd, 0, 0);
  934. sofar = 0;
  935. while (sofar < msg->size)
  936. {
  937. len = msg->size - sofar;
  938. if (len > sizeof (buf))
  939. len = sizeof (buf);
  940. len = read (fd, buf, len);
  941. if (len == (size_t) - 1)
  942. return -1;
  943. start = 0;
  944. while (start < len)
  945. {
  946. end = start;
  947. while (end < len && buf[end] != '\n')
  948. end++;
  949. if (start != end)
  950. socket_write (imap->sock, buf + start, end - start);
  951. /* only send a crlf if we actually hit the end of a line. we
  952. * might be in the middle of a line in which case we don't
  953. * send one.
  954. */
  955. if (end != len)
  956. socket_write (imap->sock, "\r\n", 2);
  957. start = end + 1;
  958. }
  959. sofar += len;
  960. }
  961. socket_write (imap->sock, "\r\n", 2);
  962. for (;;)
  963. {
  964. if (buffer_gets (imap->buf, &s))
  965. return -1;
  966. if (Verbose)
  967. puts (s);
  968. arg = next_arg (&s);
  969. if (*arg == '*')
  970. {
  971. /* XXX just ignore it for now */
  972. }
  973. else if (atoi (arg) != (int) Tag)
  974. {
  975. puts ("wrong tag");
  976. return -1;
  977. }
  978. else
  979. {
  980. int uid;
  981. arg = next_arg (&s);
  982. if (strcmp (arg, "OK"))
  983. return -1;
  984. arg = next_arg (&s);
  985. if (*arg != '[')
  986. break;
  987. arg++;
  988. if (strcasecmp ("APPENDUID", arg))
  989. {
  990. puts ("Error, expected APPENDUID");
  991. break;
  992. }
  993. arg = next_arg (&s);
  994. if (!arg)
  995. break;
  996. if (atoi (arg) != (int) imap->uidvalidity)
  997. {
  998. puts ("Error, UIDVALIDITY doesn't match APPENDUID");
  999. return -1;
  1000. }
  1001. arg = next_arg (&s);
  1002. if (!arg)
  1003. break;
  1004. uid = strtol (arg, &s, 10);
  1005. if (*s != ']')
  1006. {
  1007. /* parse error */
  1008. break;
  1009. }
  1010. return uid;
  1011. }
  1012. }
  1013. return 0;
  1014. }