compile and run!
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.

446 lines
9.6 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-14 18:31:49.000000000 -0500
+++ isync/src/imap.c 2004-01-14 18:39:18.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,114 @@
}
}
-static int
-imap_exec (imap_t * imap, const char *fmt, ...)
+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);
+ if (strncmp(tmp, "LOGIN", 5))
+ printf (">>> %s", buf);
+ else
+ printf (">>> LOGIN USERNAME PASSWORD\n");
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 +574,7 @@
if (!arg)
{
fprintf (stderr, "IMAP error: unable to parse untagged response\n");
- return -1;
+ return NULL;
}
if (!strcmp ("NAMESPACE", arg))
@@ -528,23 +646,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 +664,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 +677,88 @@
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)) {
+ fprintf(stderr, "tag %u returned error: %s\n",
+ tag, arg);
+ 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 +1202,7 @@
size_t n;
char buf[1024];
+ flush_imap_cmds(imap);
send_server (imap->sock, "UID FETCH %d BODY.PEEK[]", uid);
for (;;)
@@ -1160,7 +1324,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 +1415,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 +1508,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);