mirror of https://git.code.sf.net/p/isync/isync
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
458 lines
9.9 KiB
458 lines
9.9 KiB
#!/bin/sh -e |
|
## 30-aysnc-imap.dpatch by Theodore Y. Ts'o <tytso@mit.edu> |
|
## |
|
## DP: Add the beginnings of asynchronous IMAP support. So far, we only |
|
## DP: support asynchronous flag setting, since that's the easist. |
|
## DP: Eventually we need to support asynchronous message fetches and |
|
## DP: uploads. |
|
|
|
if [ $# -ne 1 ]; then |
|
echo >&2 "`basename $0`: script expects -patch|-unpatch as argument" |
|
exit 1 |
|
fi |
|
|
|
[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts |
|
patch_opts="${patch_opts:--f --no-backup-if-mismatch}" |
|
|
|
case "$1" in |
|
-patch) patch $patch_opts -p1 < $0;; |
|
-unpatch) patch $patch_opts -p1 -R < $0;; |
|
*) |
|
echo >&2 "`basename $0`: script expects -patch|-unpatch as argument" |
|
exit 1;; |
|
esac |
|
|
|
exit 0 |
|
|
|
@DPATCH@ |
|
diff -urNad /usr/projects/isync/SF-cvs/isync/src/imap.c isync/src/imap.c |
|
--- /usr/projects/isync/SF-cvs/isync/src/imap.c 2004-01-15 14:24:40.000000000 -0500 |
|
+++ isync/src/imap.c 2004-01-15 20:36:15.000000000 -0500 |
|
@@ -3,6 +3,7 @@ |
|
* isync - IMAP4 to maildir mailbox synchronizer |
|
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org> |
|
* Copyright (C) 2002-2003 Oswald Buddenhagen <ossi@users.sf.net> |
|
+ * Copyright (C) 2004 Theodore Ts'o <tytso@alum.mit.edu> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
@@ -35,13 +36,33 @@ |
|
#include <string.h> |
|
#include <ctype.h> |
|
#include <sys/socket.h> |
|
+#include <sys/ioctl.h> |
|
#include <netinet/in.h> |
|
+#include <netinet/tcp.h> |
|
#include <arpa/inet.h> |
|
#include <netdb.h> |
|
#if HAVE_LIBSSL |
|
# include <openssl/err.h> |
|
#endif |
|
|
|
+struct imap_cmd { |
|
+ unsigned int tag; |
|
+ char *cmd; |
|
+ int flags; |
|
+ int response; |
|
+ struct imap_cmd *next; |
|
+ int (*complete_fn) (imap_t *imap, struct imap_cmd * cmd); |
|
+}; |
|
+ |
|
+#define IMAP_FLAG_DONE 0x0001 |
|
+ |
|
+static struct imap_cmd *in_progress = NULL; |
|
+static int num_in_progress = 0; |
|
+int max_in_progress_high = 50; |
|
+int max_in_progress_low = 10; |
|
+ |
|
+static struct imap_cmd *get_cmd_result(imap_t *imap); |
|
+ |
|
const char *Flags[] = { |
|
"\\Seen", |
|
"\\Answered", |
|
@@ -199,6 +220,22 @@ |
|
return write (sock->fd, buf, len); |
|
} |
|
|
|
+static int |
|
+socket_pending(Socket_t *sock) |
|
+{ |
|
+ int num = -1; |
|
+ |
|
+ if (ioctl(sock->fd, FIONREAD, &num) < 0) |
|
+ return -1; |
|
+ if (num > 0) |
|
+ return num; |
|
+#if HAVE_LIBSSL |
|
+ if (sock->use_ssl) |
|
+ return SSL_pending (sock->ssl); |
|
+#endif |
|
+ return 0; |
|
+} |
|
+ |
|
static void |
|
socket_perror (const char *func, Socket_t *sock, int ret) |
|
{ |
|
@@ -301,16 +338,20 @@ |
|
} |
|
|
|
static int |
|
-parse_fetch (imap_t * imap, list_t * list) |
|
+parse_fetch (imap_t * imap, char *cmd) |
|
{ |
|
- list_t *tmp; |
|
+ list_t *tmp, *list; |
|
unsigned int uid = 0; |
|
unsigned int mask = 0; |
|
unsigned int size = 0; |
|
message_t *cur; |
|
|
|
- if (!is_list (list)) |
|
+ list = parse_list (cmd, 0); |
|
+ |
|
+ if (!is_list (list)) { |
|
+ free_list(list); |
|
return -1; |
|
+ } |
|
|
|
for (tmp = list->child; tmp; tmp = tmp->next) |
|
{ |
|
@@ -325,6 +366,7 @@ |
|
if (uid < imap->minuid) |
|
{ |
|
/* already saw this message */ |
|
+ free_list(list); |
|
return 0; |
|
} |
|
else if (uid > imap->maxuid) |
|
@@ -387,6 +429,7 @@ |
|
cur->flags = mask; |
|
cur->size = size; |
|
|
|
+ free_list(list); |
|
return 0; |
|
} |
|
|
|
@@ -415,39 +458,121 @@ |
|
} |
|
} |
|
|
|
-static int |
|
-imap_exec (imap_t * imap, const char *fmt, ...) |
|
+static void print_imap_command(const char *cmd, FILE *f) |
|
+{ |
|
+ if (strncmp(cmd, "LOGIN", 5)) |
|
+ fputs(cmd, f); |
|
+ else |
|
+ fputs("LOGIN USERNAME PASSWORD", f); |
|
+} |
|
+ |
|
+static struct imap_cmd *issue_imap_cmd(imap_t *imap, |
|
+ const char *fmt, ...) |
|
{ |
|
va_list ap; |
|
- char tmp[256]; |
|
- char buf[256]; |
|
- char *cmd; |
|
- char *arg; |
|
- char *arg1; |
|
- config_t *box; |
|
+ char tmp[1024]; |
|
+ char buf[1024]; |
|
+ struct imap_cmd *cmd; |
|
int n; |
|
|
|
+ cmd = malloc(sizeof(struct imap_cmd)); |
|
+ if (!cmd) |
|
+ return NULL; |
|
+ |
|
+ cmd->tag = ++Tag; |
|
+ cmd->flags = 0; |
|
+ cmd->response = 0; |
|
+ cmd->complete_fn = 0; |
|
+ |
|
va_start (ap, fmt); |
|
vsnprintf (tmp, sizeof (tmp), fmt, ap); |
|
va_end (ap); |
|
|
|
- snprintf (buf, sizeof (buf), "%d %s\r\n", ++Tag, tmp); |
|
+ cmd->cmd = malloc(strlen(tmp)+1); |
|
+ if (cmd->cmd) |
|
+ strcpy(cmd->cmd, tmp); |
|
+ |
|
+ snprintf (buf, sizeof (buf), "%d %s\r\n", cmd->tag, tmp); |
|
if (Verbose) { |
|
- printf (">>> %s", buf); |
|
+ if (num_in_progress) |
|
+ printf("(%d in progress) ", num_in_progress); |
|
+ printf(">>> %d ", cmd->tag); |
|
+ print_imap_command(tmp, stdout); |
|
+ fputc('\n', stdout); |
|
fflush (stdout); |
|
} |
|
n = socket_write (imap->sock, buf, strlen (buf)); |
|
if (n <= 0) |
|
{ |
|
socket_perror ("write", imap->sock, n); |
|
- return -1; |
|
+ free(cmd); |
|
+ return NULL; |
|
} |
|
+ cmd->next = in_progress; |
|
+ in_progress = cmd; |
|
+ num_in_progress++; |
|
+ if ((num_in_progress > max_in_progress_high) || |
|
+ socket_pending(imap->sock)) { |
|
+ while ((num_in_progress > max_in_progress_low) || |
|
+ socket_pending(imap->sock)) { |
|
+ if (Verbose && socket_pending(imap->sock)) |
|
+ printf("(Socket input pending)\n"); |
|
+ get_cmd_result(imap); |
|
+ } |
|
+ } |
|
+ return cmd; |
|
+} |
|
+ |
|
+static struct imap_cmd *find_imap_cmd(unsigned int tag) |
|
+{ |
|
+ struct imap_cmd *cmd, *prev; |
|
+ |
|
+ for (prev=NULL, cmd=in_progress; cmd; cmd = cmd->next) { |
|
+ if (tag == cmd->tag) { |
|
+ return cmd; |
|
+ } |
|
+ prev = cmd; |
|
+ } |
|
+ return NULL; |
|
+} |
|
+ |
|
+static void dequeue_imap_cmd(unsigned int tag) |
|
+{ |
|
+ struct imap_cmd *cmd, *prev; |
|
+ |
|
+ for (prev=NULL, cmd=in_progress; cmd; cmd = cmd->next) { |
|
+ if (tag != cmd->tag) { |
|
+ prev = cmd; |
|
+ continue; |
|
+ } |
|
+ if (prev) |
|
+ prev->next = cmd->next; |
|
+ else |
|
+ in_progress = cmd->next; |
|
+ cmd->next = 0; |
|
+ if (cmd->cmd) |
|
+ free(cmd->cmd); |
|
+ cmd->cmd = 0; |
|
+ free(cmd); |
|
+ break; |
|
+ } |
|
+} |
|
+ |
|
+static struct imap_cmd *get_cmd_result(imap_t *imap) |
|
+{ |
|
+ char *cmd; |
|
+ char *arg; |
|
+ char *arg1; |
|
+ config_t *box; |
|
+ int n; |
|
+ unsigned int tag; |
|
+ struct imap_cmd *cmdp; |
|
|
|
for (;;) |
|
{ |
|
next: |
|
if (buffer_gets (imap->buf, &cmd)) |
|
- return -1; |
|
+ return NULL; |
|
|
|
arg = next_arg (&cmd); |
|
if (*arg == '*') |
|
@@ -456,7 +581,7 @@ |
|
if (!arg) |
|
{ |
|
fprintf (stderr, "IMAP error: unable to parse untagged response\n"); |
|
- return -1; |
|
+ return NULL; |
|
} |
|
|
|
if (!strcmp ("NAMESPACE", arg)) |
|
@@ -528,23 +653,14 @@ |
|
imap->recent = atoi (arg); |
|
else if (!strcmp ("FETCH", arg1)) |
|
{ |
|
- list_t *list; |
|
- |
|
- list = parse_list (cmd, 0); |
|
- |
|
- if (parse_fetch (imap, list)) |
|
- { |
|
- free_list (list); |
|
- return -1; |
|
- } |
|
- |
|
- free_list (list); |
|
+ if (parse_fetch (imap, cmd)) |
|
+ return NULL; |
|
} |
|
} |
|
else |
|
{ |
|
fprintf (stderr, "IMAP error: unable to parse untagged response\n"); |
|
- return -1; |
|
+ return NULL; |
|
} |
|
} |
|
#if HAVE_LIBSSL |
|
@@ -555,7 +671,7 @@ |
|
if (!imap->cram) |
|
{ |
|
fprintf (stderr, "IMAP error, not doing CRAM-MD5 authentication\n"); |
|
- return -1; |
|
+ return NULL; |
|
} |
|
resp = cram (cmd, imap->box->user, imap->box->pass); |
|
|
|
@@ -568,34 +684,94 @@ |
|
if (n <= 0) |
|
{ |
|
socket_perror ("write", imap->sock, n); |
|
- return -1; |
|
+ return NULL; |
|
} |
|
n = socket_write (imap->sock, "\r\n", 2); |
|
if (n <= 0) |
|
{ |
|
socket_perror ("write", imap->sock, n); |
|
- return -1; |
|
+ return NULL; |
|
} |
|
imap->cram = 0; |
|
} |
|
#endif |
|
- else if ((size_t) atol (arg) != Tag) |
|
- { |
|
- fprintf (stderr, "IMAP error: wrong tag\n"); |
|
- return -1; |
|
- } |
|
- else |
|
- { |
|
- arg = next_arg (&cmd); |
|
- parse_response_code (imap, cmd); |
|
- if (!strcmp ("OK", arg)) |
|
- return 0; |
|
- return -1; |
|
+ else { |
|
+ tag = (unsigned int) atol (arg); |
|
+ cmdp = find_imap_cmd(tag); |
|
+ if (!cmdp) { |
|
+ fprintf(stderr, "IMAP error: sent unknown tag: %u\n", |
|
+ tag); |
|
+ return NULL; |
|
+ } |
|
+ arg = next_arg (&cmd); |
|
+ if (strncmp("OK", arg, 2)) { |
|
+ if (cmdp->cmd) { |
|
+ fputc('\'', stderr); |
|
+ print_imap_command(cmdp->cmd, stderr); |
|
+ fputc('\'', stderr); |
|
+ } else |
|
+ fprintf(stderr, "tag %u", tag); |
|
+ fprintf(stderr, " returned an error (%s): %s\n", |
|
+ arg, cmd ? cmd : ""); |
|
+ cmdp->response = -1; |
|
+ } |
|
+ parse_response_code (imap, cmd); |
|
+ num_in_progress--; |
|
+ cmdp->flags |= IMAP_FLAG_DONE; |
|
+ if (Verbose) |
|
+ printf("Tag %u completed with response %d\n", |
|
+ cmdp->tag, cmdp->response); |
|
+ return cmdp; |
|
} |
|
} |
|
/* not reached */ |
|
} |
|
|
|
+static void flush_imap_cmds(imap_t *imap) |
|
+{ |
|
+ struct imap_cmd *cmdp; |
|
+ |
|
+ while (num_in_progress) { |
|
+ if (in_progress && in_progress->flags & IMAP_FLAG_DONE) { |
|
+ dequeue_imap_cmd(in_progress->tag); |
|
+ continue; |
|
+ } |
|
+ cmdp = get_cmd_result(imap); |
|
+ if (!cmdp) |
|
+ printf("Error trying to flush pending imap cmds\n"); |
|
+ } |
|
+} |
|
+ |
|
+static int |
|
+imap_exec (imap_t * imap, const char *fmt, ...) |
|
+{ |
|
+ va_list ap; |
|
+ char tmp[1024]; |
|
+ struct imap_cmd *cmdp, *waitp; |
|
+ int result; |
|
+ |
|
+ va_start (ap, fmt); |
|
+ vsnprintf (tmp, sizeof (tmp), fmt, ap); |
|
+ va_end (ap); |
|
+ |
|
+ cmdp = issue_imap_cmd(imap, "%s", tmp); |
|
+ if (!cmdp) |
|
+ return -1; |
|
+ |
|
+ if (cmdp->flags & IMAP_FLAG_DONE) |
|
+ return cmdp->response; |
|
+ |
|
+ do { |
|
+ waitp = get_cmd_result(imap); |
|
+ } while (waitp->tag != cmdp->tag); |
|
+ |
|
+ result = cmdp->response; |
|
+ dequeue_imap_cmd(cmdp->tag); |
|
+ |
|
+ return cmdp->response; |
|
+ |
|
+} |
|
+ |
|
#ifdef HAVE_LIBSSL |
|
static int |
|
start_tls (imap_t *imap, config_t * cfg) |
|
@@ -1039,6 +1215,7 @@ |
|
size_t n; |
|
char buf[1024]; |
|
|
|
+ flush_imap_cmds(imap); |
|
send_server (imap->sock, "UID FETCH %d BODY.PEEK[]", uid); |
|
|
|
for (;;) |
|
@@ -1160,7 +1337,9 @@ |
|
(buf[0] != 0) ? " " : "", Flags[i]); |
|
} |
|
|
|
- return imap_exec (imap, "UID STORE %d +FLAGS.SILENT (%s)", uid, buf); |
|
+ if (issue_imap_cmd(imap, "UID STORE %d +FLAGS.SILENT (%s)", uid, buf)) |
|
+ return 0; |
|
+ return -1; |
|
} |
|
|
|
int |
|
@@ -1249,6 +1428,7 @@ |
|
strcat (flagstr,") "); |
|
} |
|
|
|
+ flush_imap_cmds(imap); |
|
send_server (imap->sock, "APPEND %s%s %s{%d}", |
|
imap->prefix, imap->box->box, flagstr, len + extra); |
|
|
|
@@ -1341,6 +1521,7 @@ |
|
} |
|
|
|
/* didn't receive an APPENDUID */ |
|
+ flush_imap_cmds(imap); |
|
send_server (imap->sock, |
|
"UID SEARCH HEADER X-TUID %08lx%05lx%04x", |
|
tv.tv_sec, tv.tv_usec, pid);
|
|
|