imap.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. /* $Id$
  2. *
  3. * isync - IMAP4 to maildir mailbox synchronizer
  4. * Copyright (C) 2000 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. #include "isync.h"
  32. const char *Flags[] = {
  33. "\\Seen",
  34. "\\Answered",
  35. "\\Deleted",
  36. "\\Flagged",
  37. "\\Recent",
  38. "\\Draft"
  39. };
  40. /* simple line buffering */
  41. static int
  42. buffer_gets (buffer_t * b, char **s)
  43. {
  44. int n;
  45. int start = b->offset;
  46. *s = b->buf + start;
  47. for (;;)
  48. {
  49. if (b->offset + 2 > b->bytes)
  50. {
  51. /* shift down used bytes */
  52. *s = b->buf;
  53. assert (start <= b->bytes);
  54. n = b->bytes - start;
  55. if (n)
  56. memmove (b->buf, b->buf + start, n);
  57. b->offset = n;
  58. start = 0;
  59. n = read (b->fd, b->buf + b->offset, sizeof (b->buf) - b->offset);
  60. if (n <= 0)
  61. {
  62. if (n == -1)
  63. perror ("read");
  64. else
  65. puts ("EOF");
  66. return -1;
  67. }
  68. b->bytes = b->offset + n;
  69. // printf ("buffer_gets:read %d bytes\n", n);
  70. }
  71. if (b->buf[b->offset] == '\r')
  72. {
  73. if (b->buf[b->offset + 1] == '\n')
  74. {
  75. b->buf[b->offset] = 0; /* terminate the string */
  76. b->offset += 2; /* next line */
  77. return 0;
  78. }
  79. }
  80. b->offset++;
  81. }
  82. /* not reached */
  83. }
  84. static int
  85. imap_exec (imap_t * imap, const char *fmt, ...)
  86. {
  87. va_list ap;
  88. char tmp[256];
  89. char buf[256];
  90. char *cmd;
  91. char *arg;
  92. char *arg1;
  93. message_t **cur = 0;
  94. message_t **rec = 0;
  95. va_start (ap, fmt);
  96. vsnprintf (tmp, sizeof (tmp), fmt, ap);
  97. va_end (ap);
  98. snprintf (buf, sizeof (buf), "%d %s\r\n", ++Tag, tmp);
  99. if (Verbose)
  100. fputs (buf, stdout);
  101. write (imap->fd, buf, strlen (buf));
  102. for (;;)
  103. {
  104. if (buffer_gets (imap->buf, &cmd))
  105. return -1;
  106. if (Verbose)
  107. puts (cmd);
  108. arg = next_arg (&cmd);
  109. if (*arg == '*')
  110. {
  111. arg = next_arg (&cmd);
  112. arg1 = next_arg (&cmd);
  113. if (arg1 && !strcmp ("EXISTS", arg1))
  114. imap->count = atoi (arg);
  115. else if (arg1 && !strcmp ("RECENT", arg1))
  116. imap->recent = atoi (arg);
  117. else if (!strcmp ("SEARCH", arg))
  118. {
  119. if (!rec)
  120. {
  121. rec = &imap->recent_msgs;
  122. while (*rec)
  123. rec = &(*rec)->next;
  124. }
  125. /* need to add arg1 */
  126. *rec = calloc (1, sizeof (message_t));
  127. (*rec)->uid = atoi (arg1);
  128. rec = &(*rec)->next;
  129. /* parse rest of `cmd' */
  130. while ((arg = next_arg (&cmd)))
  131. {
  132. *rec = calloc (1, sizeof (message_t));
  133. (*rec)->uid = atoi (arg);
  134. rec = &(*rec)->next;
  135. }
  136. }
  137. else if (arg1 && !strcmp ("FETCH", arg1))
  138. {
  139. if (!cur)
  140. {
  141. cur = &imap->msgs;
  142. while (*cur)
  143. cur = &(*cur)->next;
  144. }
  145. /* new message
  146. * * <N> FETCH (UID <uid> FLAGS (...))
  147. */
  148. arg = next_arg (&cmd); /* (UID */
  149. arg = next_arg (&cmd); /* <uid> */
  150. *cur = calloc (1, sizeof (message_t));
  151. (*cur)->uid = atoi (arg);
  152. arg = next_arg (&cmd); /* FLAGS */
  153. if (!arg || strcmp ("FLAGS", arg))
  154. {
  155. printf ("FETCH parse error: expected FLAGS at %s\n", arg);
  156. return -1;
  157. }
  158. /* if we need to parse additional info, we should keep
  159. * a copy of this `arg' pointer
  160. */
  161. cmd++;
  162. arg = strchr (cmd, ')');
  163. if (!arg)
  164. {
  165. puts ("FETCH parse error");
  166. return -1;
  167. }
  168. *arg = 0;
  169. /* parse message flags */
  170. while ((arg = next_arg (&cmd)))
  171. {
  172. if (!strcmp ("\\Seen", arg))
  173. (*cur)->flags |= D_SEEN;
  174. else if (!strcmp ("\\Flagged", arg))
  175. (*cur)->flags |= D_FLAGGED;
  176. else if (!strcmp ("\\Deleted", arg))
  177. {
  178. (*cur)->flags |= D_DELETED;
  179. imap->deleted++;
  180. }
  181. else if (!strcmp ("\\Answered", arg))
  182. (*cur)->flags |= D_ANSWERED;
  183. else if (!strcmp ("\\Draft", arg))
  184. (*cur)->flags |= D_DRAFT;
  185. else if (!strcmp ("\\Recent", arg))
  186. (*cur)->flags |= D_RECENT;
  187. else
  188. printf ("warning, unknown flag %s\n", arg);
  189. }
  190. cur = &(*cur)->next;
  191. }
  192. }
  193. else if ((size_t) atol (arg) != Tag)
  194. {
  195. puts ("wrong tag");
  196. return -1;
  197. }
  198. else
  199. {
  200. arg = next_arg (&cmd);
  201. if (!strcmp ("OK", arg))
  202. return 0;
  203. puts ("IMAP command failed");
  204. return -1;
  205. }
  206. }
  207. /* not reached */
  208. }
  209. static int
  210. fetch_recent_flags (imap_t * imap)
  211. {
  212. char buf[1024];
  213. message_t **cur = &imap->recent_msgs;
  214. message_t *tmp;
  215. unsigned int start = -1;
  216. unsigned int last = -1;
  217. int ret = 0;
  218. buf[0] = 0;
  219. while (*cur)
  220. {
  221. tmp = *cur;
  222. if (last == (unsigned int) -1)
  223. {
  224. /* init */
  225. start = tmp->uid;
  226. last = tmp->uid;
  227. }
  228. else if (tmp->uid == last + 1)
  229. last++;
  230. else
  231. {
  232. /* out of sequence */
  233. if (start == last)
  234. ret = imap_exec (imap, "UID FETCH %d (UID FLAGS)", start);
  235. else
  236. ret =
  237. imap_exec (imap, "UID FETCH %d:%d (UID FLAGS)", start,
  238. last);
  239. start = tmp->uid;
  240. last = tmp->uid;
  241. }
  242. free (tmp);
  243. *cur = (*cur)->next;
  244. }
  245. if (start != (unsigned int) -1)
  246. {
  247. if (start == last)
  248. ret = imap_exec (imap, "UID FETCH %d (UID FLAGS)", start);
  249. else
  250. ret =
  251. imap_exec (imap, "UID FETCH %d:%d (UID FLAGS)", start, last);
  252. }
  253. return ret;
  254. }
  255. imap_t *
  256. imap_open (config_t * box, int fast)
  257. {
  258. int ret;
  259. imap_t *imap;
  260. int s;
  261. struct sockaddr_in sin;
  262. struct hostent *he;
  263. /* open connection to IMAP server */
  264. memset (&sin, 0, sizeof (sin));
  265. sin.sin_port = htons (box->port);
  266. sin.sin_family = AF_INET;
  267. printf ("Resolving %s... ", box->host);
  268. fflush (stdout);
  269. he = gethostbyname (box->host);
  270. if (!he)
  271. {
  272. perror ("gethostbyname");
  273. return 0;
  274. }
  275. puts ("ok");
  276. sin.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
  277. s = socket (PF_INET, SOCK_STREAM, 0);
  278. printf ("Connecting to %s:%hu... ", inet_ntoa (sin.sin_addr),
  279. ntohs (sin.sin_port));
  280. fflush (stdout);
  281. if (connect (s, (struct sockaddr *) &sin, sizeof (sin)))
  282. {
  283. perror ("connect");
  284. exit (1);
  285. }
  286. puts ("ok");
  287. imap = calloc (1, sizeof (imap_t));
  288. imap->fd = s;
  289. //imap->state = imap_state_init;
  290. imap->buf = calloc (1, sizeof (buffer_t));
  291. imap->buf->fd = s;
  292. imap->box = box;
  293. puts ("Logging in...");
  294. ret = imap_exec (imap, "LOGIN %s %s", box->user, box->pass);
  295. if (!ret)
  296. {
  297. fputs ("Selecting mailbox... ", stdout);
  298. fflush (stdout);
  299. ret = imap_exec (imap, "SELECT %s", box->box);
  300. if (!ret)
  301. printf ("%d messages, %d recent\n", imap->count, imap->recent);
  302. }
  303. if (!ret)
  304. {
  305. if (fast)
  306. {
  307. if (imap->recent > 0)
  308. {
  309. puts ("Fetching info for recent messages");
  310. ret = imap_exec (imap, "UID SEARCH RECENT");
  311. if (!ret)
  312. ret = fetch_recent_flags (imap);
  313. }
  314. }
  315. else if (imap->count > 0)
  316. {
  317. puts ("Reading IMAP mailbox index");
  318. ret = imap_exec (imap, "FETCH 1:%d (UID FLAGS)", imap->count);
  319. }
  320. }
  321. if (ret)
  322. {
  323. imap_exec (imap, "LOGOUT");
  324. close (s);
  325. free (imap->buf);
  326. free (imap);
  327. imap = 0;
  328. }
  329. return imap;
  330. }
  331. void
  332. imap_close (imap_t * imap)
  333. {
  334. puts ("Closing IMAP connection");
  335. imap_exec (imap, "LOGOUT");
  336. }
  337. /* write a buffer stripping all \r bytes */
  338. static int
  339. write_strip (int fd, char *buf, size_t len)
  340. {
  341. size_t start = 0;
  342. size_t end = 0;
  343. while (start < len)
  344. {
  345. while (end < len && buf[end] != '\r')
  346. end++;
  347. write (fd, buf + start, end - start);
  348. end++;
  349. start = end;
  350. }
  351. return 0;
  352. }
  353. static void
  354. send_server (int fd, const char *fmt, ...)
  355. {
  356. char buf[128];
  357. char cmd[128];
  358. va_list ap;
  359. va_start (ap, fmt);
  360. vsnprintf (buf, sizeof (buf), fmt, ap);
  361. va_end (ap);
  362. snprintf (cmd, sizeof (cmd), "%d %s\r\n", ++Tag, buf);
  363. write (fd, cmd, strlen (cmd));
  364. if (Verbose)
  365. fputs (cmd, stdout);
  366. }
  367. int
  368. imap_fetch_message (imap_t * imap, unsigned int uid, int fd)
  369. {
  370. char *cmd;
  371. char *arg;
  372. size_t bytes;
  373. size_t n;
  374. char buf[1024];
  375. send_server (imap->fd, "UID FETCH %d RFC822.PEEK", uid);
  376. for (;;)
  377. {
  378. if (buffer_gets (imap->buf, &cmd))
  379. return -1;
  380. if (Verbose)
  381. puts (cmd);
  382. if (*cmd == '*')
  383. {
  384. /* need to figure out how long the message is
  385. * * <msgno> FETCH (RFC822 {<size>}
  386. */
  387. next_arg (&cmd); /* * */
  388. next_arg (&cmd); /* <msgno> */
  389. next_arg (&cmd); /* FETCH */
  390. next_arg (&cmd); /* (RFC822 */
  391. arg = next_arg (&cmd);
  392. if (*arg != '{')
  393. {
  394. puts ("parse error getting size");
  395. return -1;
  396. }
  397. bytes = strtol (arg + 1, 0, 10);
  398. // printf ("receiving %d byte message\n", bytes);
  399. /* dump whats left over in the input buffer */
  400. n = imap->buf->bytes - imap->buf->offset;
  401. if (n > bytes)
  402. {
  403. /* the entire message fit in the buffer */
  404. n = bytes;
  405. }
  406. /* ick. we have to strip out the \r\n line endings, so
  407. * i can't just dump the raw bytes to disk.
  408. */
  409. write_strip (fd, imap->buf->buf + imap->buf->offset, n);
  410. bytes -= n;
  411. // printf ("wrote %d buffered bytes\n", n);
  412. /* mark that we used part of the buffer */
  413. imap->buf->offset += n;
  414. /* now read the rest of the message */
  415. while (bytes > 0)
  416. {
  417. n = bytes;
  418. if (n > sizeof (buf))
  419. n = sizeof (buf);
  420. n = read (imap->fd, buf, n);
  421. if (n > 0)
  422. {
  423. // printf("imap_fetch_message:%d:read %d bytes\n", __LINE__, n);
  424. write_strip (fd, buf, n);
  425. bytes -= n;
  426. }
  427. else
  428. {
  429. if (n == (size_t) - 1)
  430. perror ("read");
  431. else
  432. puts ("EOF");
  433. return -1;
  434. }
  435. }
  436. // puts ("finished fetching msg");
  437. buffer_gets (imap->buf, &cmd);
  438. if (Verbose)
  439. puts (cmd); /* last part of line */
  440. }
  441. else
  442. {
  443. arg = next_arg (&cmd);
  444. if (!arg || (size_t) atoi (arg) != Tag)
  445. {
  446. puts ("wrong tag");
  447. return -1;
  448. }
  449. break;
  450. }
  451. }
  452. return 0;
  453. }
  454. /* add flags to existing flags */
  455. int
  456. imap_set_flags (imap_t * imap, unsigned int uid, unsigned int flags)
  457. {
  458. char buf[256];
  459. int i;
  460. buf[0] = 0;
  461. for (i = 0; i < D_MAX; i++)
  462. {
  463. if (flags & (1 << i))
  464. snprintf (buf + strlen (buf),
  465. sizeof (buf) - strlen (buf), "%s%s",
  466. (buf[0] != 0) ? " " : "", Flags[i]);
  467. }
  468. return imap_exec (imap, "UID STORE %d +FLAGS.SILENT (%s)", uid, buf);
  469. }
  470. int
  471. imap_expunge (imap_t * imap)
  472. {
  473. return imap_exec (imap, "EXPUNGE");
  474. }