30-async-imap.dpatch 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. #!/bin/sh -e
  2. ## 30-aysnc-imap.dpatch by Theodore Y. Ts'o <tytso@mit.edu>
  3. ##
  4. ## DP: Add the beginnings of asynchronous IMAP support. So far, we only
  5. ## DP: support asynchronous flag setting, since that's the easist.
  6. ## DP: Eventually we need to support asynchronous message fetches and
  7. ## DP: uploads.
  8. if [ $# -ne 1 ]; then
  9. echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
  10. exit 1
  11. fi
  12. [ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts
  13. patch_opts="${patch_opts:--f --no-backup-if-mismatch}"
  14. case "$1" in
  15. -patch) patch $patch_opts -p1 < $0;;
  16. -unpatch) patch $patch_opts -p1 -R < $0;;
  17. *)
  18. echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
  19. exit 1;;
  20. esac
  21. exit 0
  22. @DPATCH@
  23. diff -urNad /usr/projects/isync/SF-cvs/isync/src/imap.c isync/src/imap.c
  24. --- /usr/projects/isync/SF-cvs/isync/src/imap.c 2004-01-15 14:24:40.000000000 -0500
  25. +++ isync/src/imap.c 2004-01-15 20:36:15.000000000 -0500
  26. @@ -3,6 +3,7 @@
  27. * isync - IMAP4 to maildir mailbox synchronizer
  28. * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
  29. * Copyright (C) 2002-2003 Oswald Buddenhagen <ossi@users.sf.net>
  30. + * Copyright (C) 2004 Theodore Ts'o <tytso@alum.mit.edu>
  31. *
  32. * This program is free software; you can redistribute it and/or modify
  33. * it under the terms of the GNU General Public License as published by
  34. @@ -35,13 +36,33 @@
  35. #include <string.h>
  36. #include <ctype.h>
  37. #include <sys/socket.h>
  38. +#include <sys/ioctl.h>
  39. #include <netinet/in.h>
  40. +#include <netinet/tcp.h>
  41. #include <arpa/inet.h>
  42. #include <netdb.h>
  43. #if HAVE_LIBSSL
  44. # include <openssl/err.h>
  45. #endif
  46. +struct imap_cmd {
  47. + unsigned int tag;
  48. + char *cmd;
  49. + int flags;
  50. + int response;
  51. + struct imap_cmd *next;
  52. + int (*complete_fn) (imap_t *imap, struct imap_cmd * cmd);
  53. +};
  54. +
  55. +#define IMAP_FLAG_DONE 0x0001
  56. +
  57. +static struct imap_cmd *in_progress = NULL;
  58. +static int num_in_progress = 0;
  59. +int max_in_progress_high = 50;
  60. +int max_in_progress_low = 10;
  61. +
  62. +static struct imap_cmd *get_cmd_result(imap_t *imap);
  63. +
  64. const char *Flags[] = {
  65. "\\Seen",
  66. "\\Answered",
  67. @@ -199,6 +220,22 @@
  68. return write (sock->fd, buf, len);
  69. }
  70. +static int
  71. +socket_pending(Socket_t *sock)
  72. +{
  73. + int num = -1;
  74. +
  75. + if (ioctl(sock->fd, FIONREAD, &num) < 0)
  76. + return -1;
  77. + if (num > 0)
  78. + return num;
  79. +#if HAVE_LIBSSL
  80. + if (sock->use_ssl)
  81. + return SSL_pending (sock->ssl);
  82. +#endif
  83. + return 0;
  84. +}
  85. +
  86. static void
  87. socket_perror (const char *func, Socket_t *sock, int ret)
  88. {
  89. @@ -301,16 +338,20 @@
  90. }
  91. static int
  92. -parse_fetch (imap_t * imap, list_t * list)
  93. +parse_fetch (imap_t * imap, char *cmd)
  94. {
  95. - list_t *tmp;
  96. + list_t *tmp, *list;
  97. unsigned int uid = 0;
  98. unsigned int mask = 0;
  99. unsigned int size = 0;
  100. message_t *cur;
  101. - if (!is_list (list))
  102. + list = parse_list (cmd, 0);
  103. +
  104. + if (!is_list (list)) {
  105. + free_list(list);
  106. return -1;
  107. + }
  108. for (tmp = list->child; tmp; tmp = tmp->next)
  109. {
  110. @@ -325,6 +366,7 @@
  111. if (uid < imap->minuid)
  112. {
  113. /* already saw this message */
  114. + free_list(list);
  115. return 0;
  116. }
  117. else if (uid > imap->maxuid)
  118. @@ -387,6 +429,7 @@
  119. cur->flags = mask;
  120. cur->size = size;
  121. + free_list(list);
  122. return 0;
  123. }
  124. @@ -415,39 +458,121 @@
  125. }
  126. }
  127. -static int
  128. -imap_exec (imap_t * imap, const char *fmt, ...)
  129. +static void print_imap_command(const char *cmd, FILE *f)
  130. +{
  131. + if (strncmp(cmd, "LOGIN", 5))
  132. + fputs(cmd, f);
  133. + else
  134. + fputs("LOGIN USERNAME PASSWORD", f);
  135. +}
  136. +
  137. +static struct imap_cmd *issue_imap_cmd(imap_t *imap,
  138. + const char *fmt, ...)
  139. {
  140. va_list ap;
  141. - char tmp[256];
  142. - char buf[256];
  143. - char *cmd;
  144. - char *arg;
  145. - char *arg1;
  146. - config_t *box;
  147. + char tmp[1024];
  148. + char buf[1024];
  149. + struct imap_cmd *cmd;
  150. int n;
  151. + cmd = malloc(sizeof(struct imap_cmd));
  152. + if (!cmd)
  153. + return NULL;
  154. +
  155. + cmd->tag = ++Tag;
  156. + cmd->flags = 0;
  157. + cmd->response = 0;
  158. + cmd->complete_fn = 0;
  159. +
  160. va_start (ap, fmt);
  161. vsnprintf (tmp, sizeof (tmp), fmt, ap);
  162. va_end (ap);
  163. - snprintf (buf, sizeof (buf), "%d %s\r\n", ++Tag, tmp);
  164. + cmd->cmd = malloc(strlen(tmp)+1);
  165. + if (cmd->cmd)
  166. + strcpy(cmd->cmd, tmp);
  167. +
  168. + snprintf (buf, sizeof (buf), "%d %s\r\n", cmd->tag, tmp);
  169. if (Verbose) {
  170. - printf (">>> %s", buf);
  171. + if (num_in_progress)
  172. + printf("(%d in progress) ", num_in_progress);
  173. + printf(">>> %d ", cmd->tag);
  174. + print_imap_command(tmp, stdout);
  175. + fputc('\n', stdout);
  176. fflush (stdout);
  177. }
  178. n = socket_write (imap->sock, buf, strlen (buf));
  179. if (n <= 0)
  180. {
  181. socket_perror ("write", imap->sock, n);
  182. - return -1;
  183. + free(cmd);
  184. + return NULL;
  185. }
  186. + cmd->next = in_progress;
  187. + in_progress = cmd;
  188. + num_in_progress++;
  189. + if ((num_in_progress > max_in_progress_high) ||
  190. + socket_pending(imap->sock)) {
  191. + while ((num_in_progress > max_in_progress_low) ||
  192. + socket_pending(imap->sock)) {
  193. + if (Verbose && socket_pending(imap->sock))
  194. + printf("(Socket input pending)\n");
  195. + get_cmd_result(imap);
  196. + }
  197. + }
  198. + return cmd;
  199. +}
  200. +
  201. +static struct imap_cmd *find_imap_cmd(unsigned int tag)
  202. +{
  203. + struct imap_cmd *cmd, *prev;
  204. +
  205. + for (prev=NULL, cmd=in_progress; cmd; cmd = cmd->next) {
  206. + if (tag == cmd->tag) {
  207. + return cmd;
  208. + }
  209. + prev = cmd;
  210. + }
  211. + return NULL;
  212. +}
  213. +
  214. +static void dequeue_imap_cmd(unsigned int tag)
  215. +{
  216. + struct imap_cmd *cmd, *prev;
  217. +
  218. + for (prev=NULL, cmd=in_progress; cmd; cmd = cmd->next) {
  219. + if (tag != cmd->tag) {
  220. + prev = cmd;
  221. + continue;
  222. + }
  223. + if (prev)
  224. + prev->next = cmd->next;
  225. + else
  226. + in_progress = cmd->next;
  227. + cmd->next = 0;
  228. + if (cmd->cmd)
  229. + free(cmd->cmd);
  230. + cmd->cmd = 0;
  231. + free(cmd);
  232. + break;
  233. + }
  234. +}
  235. +
  236. +static struct imap_cmd *get_cmd_result(imap_t *imap)
  237. +{
  238. + char *cmd;
  239. + char *arg;
  240. + char *arg1;
  241. + config_t *box;
  242. + int n;
  243. + unsigned int tag;
  244. + struct imap_cmd *cmdp;
  245. for (;;)
  246. {
  247. next:
  248. if (buffer_gets (imap->buf, &cmd))
  249. - return -1;
  250. + return NULL;
  251. arg = next_arg (&cmd);
  252. if (*arg == '*')
  253. @@ -456,7 +581,7 @@
  254. if (!arg)
  255. {
  256. fprintf (stderr, "IMAP error: unable to parse untagged response\n");
  257. - return -1;
  258. + return NULL;
  259. }
  260. if (!strcmp ("NAMESPACE", arg))
  261. @@ -528,23 +653,14 @@
  262. imap->recent = atoi (arg);
  263. else if (!strcmp ("FETCH", arg1))
  264. {
  265. - list_t *list;
  266. -
  267. - list = parse_list (cmd, 0);
  268. -
  269. - if (parse_fetch (imap, list))
  270. - {
  271. - free_list (list);
  272. - return -1;
  273. - }
  274. -
  275. - free_list (list);
  276. + if (parse_fetch (imap, cmd))
  277. + return NULL;
  278. }
  279. }
  280. else
  281. {
  282. fprintf (stderr, "IMAP error: unable to parse untagged response\n");
  283. - return -1;
  284. + return NULL;
  285. }
  286. }
  287. #if HAVE_LIBSSL
  288. @@ -555,7 +671,7 @@
  289. if (!imap->cram)
  290. {
  291. fprintf (stderr, "IMAP error, not doing CRAM-MD5 authentication\n");
  292. - return -1;
  293. + return NULL;
  294. }
  295. resp = cram (cmd, imap->box->user, imap->box->pass);
  296. @@ -568,34 +684,94 @@
  297. if (n <= 0)
  298. {
  299. socket_perror ("write", imap->sock, n);
  300. - return -1;
  301. + return NULL;
  302. }
  303. n = socket_write (imap->sock, "\r\n", 2);
  304. if (n <= 0)
  305. {
  306. socket_perror ("write", imap->sock, n);
  307. - return -1;
  308. + return NULL;
  309. }
  310. imap->cram = 0;
  311. }
  312. #endif
  313. - else if ((size_t) atol (arg) != Tag)
  314. - {
  315. - fprintf (stderr, "IMAP error: wrong tag\n");
  316. - return -1;
  317. - }
  318. - else
  319. - {
  320. - arg = next_arg (&cmd);
  321. - parse_response_code (imap, cmd);
  322. - if (!strcmp ("OK", arg))
  323. - return 0;
  324. - return -1;
  325. + else {
  326. + tag = (unsigned int) atol (arg);
  327. + cmdp = find_imap_cmd(tag);
  328. + if (!cmdp) {
  329. + fprintf(stderr, "IMAP error: sent unknown tag: %u\n",
  330. + tag);
  331. + return NULL;
  332. + }
  333. + arg = next_arg (&cmd);
  334. + if (strncmp("OK", arg, 2)) {
  335. + if (cmdp->cmd) {
  336. + fputc('\'', stderr);
  337. + print_imap_command(cmdp->cmd, stderr);
  338. + fputc('\'', stderr);
  339. + } else
  340. + fprintf(stderr, "tag %u", tag);
  341. + fprintf(stderr, " returned an error (%s): %s\n",
  342. + arg, cmd ? cmd : "");
  343. + cmdp->response = -1;
  344. + }
  345. + parse_response_code (imap, cmd);
  346. + num_in_progress--;
  347. + cmdp->flags |= IMAP_FLAG_DONE;
  348. + if (Verbose)
  349. + printf("Tag %u completed with response %d\n",
  350. + cmdp->tag, cmdp->response);
  351. + return cmdp;
  352. }
  353. }
  354. /* not reached */
  355. }
  356. +static void flush_imap_cmds(imap_t *imap)
  357. +{
  358. + struct imap_cmd *cmdp;
  359. +
  360. + while (num_in_progress) {
  361. + if (in_progress && in_progress->flags & IMAP_FLAG_DONE) {
  362. + dequeue_imap_cmd(in_progress->tag);
  363. + continue;
  364. + }
  365. + cmdp = get_cmd_result(imap);
  366. + if (!cmdp)
  367. + printf("Error trying to flush pending imap cmds\n");
  368. + }
  369. +}
  370. +
  371. +static int
  372. +imap_exec (imap_t * imap, const char *fmt, ...)
  373. +{
  374. + va_list ap;
  375. + char tmp[1024];
  376. + struct imap_cmd *cmdp, *waitp;
  377. + int result;
  378. +
  379. + va_start (ap, fmt);
  380. + vsnprintf (tmp, sizeof (tmp), fmt, ap);
  381. + va_end (ap);
  382. +
  383. + cmdp = issue_imap_cmd(imap, "%s", tmp);
  384. + if (!cmdp)
  385. + return -1;
  386. +
  387. + if (cmdp->flags & IMAP_FLAG_DONE)
  388. + return cmdp->response;
  389. +
  390. + do {
  391. + waitp = get_cmd_result(imap);
  392. + } while (waitp->tag != cmdp->tag);
  393. +
  394. + result = cmdp->response;
  395. + dequeue_imap_cmd(cmdp->tag);
  396. +
  397. + return cmdp->response;
  398. +
  399. +}
  400. +
  401. #ifdef HAVE_LIBSSL
  402. static int
  403. start_tls (imap_t *imap, config_t * cfg)
  404. @@ -1039,6 +1215,7 @@
  405. size_t n;
  406. char buf[1024];
  407. + flush_imap_cmds(imap);
  408. send_server (imap->sock, "UID FETCH %d BODY.PEEK[]", uid);
  409. for (;;)
  410. @@ -1160,7 +1337,9 @@
  411. (buf[0] != 0) ? " " : "", Flags[i]);
  412. }
  413. - return imap_exec (imap, "UID STORE %d +FLAGS.SILENT (%s)", uid, buf);
  414. + if (issue_imap_cmd(imap, "UID STORE %d +FLAGS.SILENT (%s)", uid, buf))
  415. + return 0;
  416. + return -1;
  417. }
  418. int
  419. @@ -1249,6 +1428,7 @@
  420. strcat (flagstr,") ");
  421. }
  422. + flush_imap_cmds(imap);
  423. send_server (imap->sock, "APPEND %s%s %s{%d}",
  424. imap->prefix, imap->box->box, flagstr, len + extra);
  425. @@ -1341,6 +1521,7 @@
  426. }
  427. /* didn't receive an APPENDUID */
  428. + flush_imap_cmds(imap);
  429. send_server (imap->sock,
  430. "UID SEARCH HEADER X-TUID %08lx%05lx%04x",
  431. tv.tv_sec, tv.tv_usec, pid);