isync

mailbox synchronization program
git clone https://git.code.sf.net/p/isync/isync
Log | Files | Refs | README | LICENSE

commit 75bffd094763439a610d9d5eb70e8e8deab1aa37
parent 4086e9d4fb4021b6436ed5842703c83755651991
Author: Theodore Ts'o <tytso@users.sf.net>
Date:   Thu, 15 Jan 2004 03:53:15 +0000

Initial asynchronous flags setting patch.

Diffstat:
Adebian/patches/30-async-imap.dpatch | 445+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 445 insertions(+), 0 deletions(-)

diff --git a/debian/patches/30-async-imap.dpatch b/debian/patches/30-async-imap.dpatch @@ -0,0 +1,445 @@ +#!/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);