From acd674f93e94598420efe133e6da45349d6ee4e6 Mon Sep 17 00:00:00 2001 From: Michael Elkins Date: Wed, 27 Dec 2000 21:14:22 +0000 Subject: [PATCH] allow leading whitespace in config files now possible to sync multiple mailboxes by specifying multiple aliases on the command line. IMAP connections are reused if possible. don't initialize ssl unless we are going to use it. --- imap.c | 303 ++++++++++++++++++++++++++++++++++-------------------- isync.1 | 20 +++- isync.h | 5 +- maildir.c | 11 +- main.c | 185 +++++++++++++++++---------------- 5 files changed, 319 insertions(+), 205 deletions(-) diff --git a/imap.c b/imap.c index e74f698..f48747c 100644 --- a/imap.c +++ b/imap.c @@ -49,6 +49,21 @@ const char *Flags[] = { SSL_CTX *SSLContext = 0; +void +free_message (message_t * msg) +{ + message_t *tmp; + + while (msg) + { + tmp = msg; + msg = msg->next; + if (tmp->file) + free (tmp->file); + free (tmp); + } +} + /* this gets called when a certificate is to be verified */ static int verify_cert (SSL * ssl) @@ -100,7 +115,6 @@ verify_cert (SSL * ssl) puts ("\n*** Fine, but don't say I didn't warn you!\n"); } return ret; - } static int @@ -304,15 +318,6 @@ parse_fetch (imap_t * imap, list_t * list) } } -#if 0 - if (uid == 221) - { - int loop = 1; - - while (loop); - } -#endif - cur = calloc (1, sizeof (message_t)); cur->next = imap->msgs; imap->msgs = cur; @@ -485,161 +490,227 @@ imap_exec (imap_t * imap, const char *fmt, ...) * mailbox. */ imap_t * -imap_open (config_t * box, unsigned int minuid) +imap_open (config_t * box, unsigned int minuid, imap_t * imap) { int ret; - imap_t *imap; int s; struct sockaddr_in sin; struct hostent *he; char *ns_prefix = ""; + int reuse = 0; #if HAVE_LIBSSL int use_ssl = 0; #endif + if (imap) + { + /* determine whether or not we can reuse the existing session */ + if (strcmp (box->host, imap->box->host) || + strcmp (box->user, imap->box->user) || + box->port != imap->box->port #if HAVE_LIBSSL - /* initialize SSL */ - if (init_ssl (box)) - return 0; + /* ensure that security requirements are met */ + || (box->require_ssl ^ imap->box->require_ssl) + || (box->require_cram ^ imap->box->require_cram) #endif - - /* open connection to IMAP server */ - - memset (&sin, 0, sizeof (sin)); - sin.sin_port = htons (box->port); - sin.sin_family = AF_INET; - - printf ("Resolving %s... ", box->host); - fflush (stdout); - he = gethostbyname (box->host); - if (!he) - { - perror ("gethostbyname"); - return 0; + ) + { + /* can't reuse */ + imap_close (imap); + imap = 0; + } + else + { + reuse = 1; + /* reset mailbox-specific state info */ + imap->recent = 0; + imap->deleted = 0; + imap->count = 0; + imap->maxuid = 0; + free_message (imap->msgs); + imap->msgs = 0; + } } - puts ("ok"); - sin.sin_addr.s_addr = *((int *) he->h_addr_list[0]); - - s = socket (PF_INET, SOCK_STREAM, 0); - - printf ("Connecting to %s:%hu... ", inet_ntoa (sin.sin_addr), - ntohs (sin.sin_port)); - fflush (stdout); - if (connect (s, (struct sockaddr *) &sin, sizeof (sin))) + if (!imap) { - perror ("connect"); - exit (1); + imap = calloc (1, sizeof (imap_t)); + imap->sock = calloc (1, sizeof (Socket_t)); + imap->buf = calloc (1, sizeof (buffer_t)); + imap->buf->sock = imap->sock; } - puts ("ok"); - imap = calloc (1, sizeof (imap_t)); - imap->sock = calloc (1, sizeof (Socket_t)); - imap->sock->fd = s; - imap->buf = calloc (1, sizeof (buffer_t)); - imap->buf->sock = imap->sock; imap->box = box; imap->minuid = minuid; -#if HAVE_LIBSSL - if (!box->use_imaps) + if (!reuse) { - /* let's see what this puppy can do... */ - ret = imap_exec (imap, "CAPABILITY"); + /* open connection to IMAP server */ - /* always try to select SSL support if available */ - if (imap->have_starttls && !imap_exec (imap, "STARTTLS")) - use_ssl = 1; + memset (&sin, 0, sizeof (sin)); + sin.sin_port = htons (box->port); + sin.sin_family = AF_INET; - if (!use_ssl) + printf ("Resolving %s... ", box->host); + fflush (stdout); + he = gethostbyname (box->host); + if (!he) { - if (box->require_ssl) + perror ("gethostbyname"); + return 0; + } + puts ("ok"); + + sin.sin_addr.s_addr = *((int *) he->h_addr_list[0]); + + s = socket (PF_INET, SOCK_STREAM, 0); + + printf ("Connecting to %s:%hu... ", inet_ntoa (sin.sin_addr), + ntohs (sin.sin_port)); + fflush (stdout); + if (connect (s, (struct sockaddr *) &sin, sizeof (sin))) + { + perror ("connect"); + exit (1); + } + puts ("ok"); + + imap->sock->fd = s; + } + + do + { + /* if we are reusing the existing connection, we can skip the + * authentication steps. + */ + if (!reuse) + { +#if HAVE_LIBSSL + if (!box->use_imaps) { - puts ("Error, SSL support not available"); - return 0; + /* let's see what this puppy can do... */ + if ((ret = imap_exec (imap, "CAPABILITY"))) + break; + + /* always try to select SSL support if available */ + if (imap->have_starttls) + { + if ((ret = imap_exec (imap, "STARTTLS"))) + break; + use_ssl = 1; + } + + if (!use_ssl) + { + if (box->require_ssl) + { + puts ("Error, SSL support not available"); + ret = -1; + break; + } + else + puts ("Warning, SSL support not available"); + } } else - puts ("Warning, SSL support not available"); - } - } - else - use_ssl = 1; + use_ssl = 1; - if (use_ssl) - { - imap->sock->ssl = SSL_new (SSLContext); - SSL_set_fd (imap->sock->ssl, imap->sock->fd); - ret = SSL_connect (imap->sock->ssl); - if (ret <= 0) - { - ret = SSL_get_error (imap->sock->ssl, ret); - printf ("Error, SSL_connect: %s\n", ERR_error_string (ret, 0)); - return 0; - } + if (use_ssl) + { + /* initialize SSL */ + if (init_ssl (box)) + return 0; - /* verify the server certificate */ - if (verify_cert (imap->sock->ssl)) - return 0; + imap->sock->ssl = SSL_new (SSLContext); + SSL_set_fd (imap->sock->ssl, imap->sock->fd); + ret = SSL_connect (imap->sock->ssl); + if (ret <= 0) + { + ret = SSL_get_error (imap->sock->ssl, ret); + printf ("Error, SSL_connect: %s\n", + ERR_error_string (ret, 0)); + ret = -1; + break; + } - imap->sock->use_ssl = 1; - puts ("SSL support enabled"); + /* verify the server certificate */ + if ((ret = verify_cert (imap->sock->ssl))) + break; - if (box->use_imaps) - ret = imap_exec (imap, "CAPABILITY"); - } + imap->sock->use_ssl = 1; + puts ("SSL support enabled"); + + if (box->use_imaps) + if ((ret = imap_exec (imap, "CAPABILITY"))) + break; + } #else - ret = imap_exec (imap, "CAPABILITY"); + if ((ret = imap_exec (imap, "CAPABILITY"))) + break; #endif - puts ("Logging in..."); + puts ("Logging in..."); #if HAVE_LIBSSL - if (imap->have_cram) - { - puts ("Authenticating with CRAM-MD5"); - imap->cram = 1; - ret = imap_exec (imap, "AUTHENTICATE CRAM-MD5"); - } - else + if (imap->have_cram) + { + puts ("Authenticating with CRAM-MD5"); + imap->cram = 1; + if ((ret = imap_exec (imap, "AUTHENTICATE CRAM-MD5"))) + break; + } + else if (imap->box->require_cram) + { + puts + ("Error, CRAM-MD5 authentication is not supported by server"); + ret = -1; + break; + } + else #endif - { + { #if HAVE_LIBSSL - if (!use_ssl) + if (!use_ssl) #endif - puts ("*** Warning *** Password is being sent in the clear"); - ret = imap_exec (imap, "LOGIN \"%s\" \"%s\"", box->user, box->pass); - } + puts + ("*** Warning *** Password is being sent in the clear"); + if ( + (ret = + imap_exec (imap, "LOGIN \"%s\" \"%s\"", box->user, + box->pass))) + break; + } + + /* get NAMESPACE info */ + if (box->use_namespace && imap->have_namespace) + { + if ((ret = imap_exec (imap, "NAMESPACE"))) + break; + } + } /* !reuse */ - /* get NAMESPACE info */ - if (!ret && box->use_namespace && imap->have_namespace && - !imap_exec (imap, "NAMESPACE")) - { /* XXX for now assume personal namespace */ - if (is_list (imap->ns_personal) && - is_list (imap->ns_personal->child) && - is_atom (imap->ns_personal->child->child)) + if (imap->box->use_namespace && is_list (imap->ns_personal) && + is_list (imap->ns_personal->child) && + is_atom (imap->ns_personal->child->child)) { ns_prefix = imap->ns_personal->child->child->val; } - } - if (!ret) - { fputs ("Selecting mailbox... ", stdout); fflush (stdout); - ret = imap_exec (imap, "SELECT %s%s", ns_prefix, box->box); - if (!ret) - printf ("%d messages, %d recent\n", imap->count, imap->recent); - } + if ((ret = imap_exec (imap, "SELECT %s%s", ns_prefix, box->box))) + break; + printf ("%d messages, %d recent\n", imap->count, imap->recent); - if (!ret) - { puts ("Reading IMAP mailbox index"); if (imap->count > 0) { - ret = imap_exec (imap, "UID FETCH %d:* (FLAGS RFC822.SIZE)", - imap->minuid); + if ((ret = imap_exec (imap, "UID FETCH %d:* (FLAGS RFC822.SIZE)", + imap->minuid))) + break; } } + while (0); if (ret) { @@ -658,6 +729,12 @@ imap_close (imap_t * imap) { puts ("Closing IMAP connection"); imap_exec (imap, "LOGOUT"); + close (imap->sock->fd); + free (imap->sock); + free (imap->buf); + free_message (imap->msgs); + memset (imap, 0xff, sizeof (imap_t)); + free (imap); } /* write a buffer stripping all \r bytes */ diff --git a/isync.1 b/isync.1 index ac8ea30..fc92f30 100644 --- a/isync.1 +++ b/isync.1 @@ -16,7 +16,7 @@ \" along with this program; if not, write to the Free Software \" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA .. -.TH isync 1 "2000 Dec 21" +.TH isync 1 "2000 Dec 27" .. .SH NAME isync - synchronize IMAP4 and maildir mailboxes @@ -26,7 +26,10 @@ isync - synchronize IMAP4 and maildir mailboxes [ .I options... ] -.I file +.I mailbox +[ +.I mailbox ... +] .. .SH DESCRIPTION .B isync @@ -171,6 +174,15 @@ This is useful with broken IMAP servers. (Default: ) .. .TP +\fBRequireCRAM\fR \fIyes|no\fR +If set to +.I yes +, +.B isync +will require that the server accept CRAM-MD5 intead of PLAIN to authenticate +the user. +.. +.TP \fBRequireSSL\fR \fIyes|no\fR .B isync will abort the connection if a TLS/SSL session to the IMAP @@ -236,6 +248,10 @@ relies on using the message UIDs that info must be inserted into the filename in a way which will be interoperable with existing readers. So the UID is placed in the filename of the messages in the local maildir mailbox rather than the :info field. +.P +When synchronizing multiple mailboxes on the same IMAP server, it is not +possible to select different SSL options for each mailbox. Only the options +from the first mailbox are applied since the SSL session is reused. .SH SEE ALSO mutt(1), maildir(5) .P diff --git a/isync.h b/isync.h index 3a459fc..03561f4 100644 --- a/isync.h +++ b/isync.h @@ -63,6 +63,7 @@ struct config unsigned int use_sslv2:1; unsigned int use_sslv3:1; unsigned int use_tlsv1:1; + unsigned int require_cram:1; #endif unsigned int use_namespace:1; }; @@ -162,14 +163,16 @@ void imap_close (imap_t *); int imap_fetch_message (imap_t *, unsigned int, int); int imap_set_flags (imap_t *, unsigned int, unsigned int); int imap_expunge (imap_t *); -imap_t *imap_open (config_t *, unsigned int); +imap_t *imap_open (config_t *, unsigned int, imap_t *); mailbox_t *maildir_open (const char *, int fast); int maildir_expunge (mailbox_t *, int); int maildir_sync (mailbox_t *); int maildir_set_uidvalidity (mailbox_t *, unsigned int uidvalidity); +void maildir_close (mailbox_t *); message_t * find_msg (message_t * list, unsigned int uid); +void free_message (message_t *); /* parse an IMAP list construct */ list_t * parse_list (char *s, char **end); diff --git a/maildir.c b/maildir.c index 558d4d6..85c43bd 100644 --- a/maildir.c +++ b/maildir.c @@ -98,7 +98,7 @@ read_uid (const char *path, const char *file) perror ("open"); return -1; } - return 0; /* doesn't exist */ + return 0; /* doesn't exist */ } ret = do_lock (fd, F_RDLCK); if (!ret) @@ -410,3 +410,12 @@ maildir_set_uidvalidity (mailbox_t * mbox, unsigned int uidvalidity) return (ret); } + +void +maildir_close (mailbox_t * mbox) +{ + free (mbox->path); + free_message (mbox->msgs); + memset (mbox, 0xff, sizeof (mailbox_t)); + free (mbox); +} diff --git a/main.c b/main.c index c81817f..7888821 100644 --- a/main.c +++ b/main.c @@ -66,7 +66,7 @@ usage (void) { printf ("%s %s IMAP4 to maildir synchronizer\n", PACKAGE, VERSION); puts ("Copyright (C) 2000 Michael R. Elkins "); - printf ("usage: %s [ flags ] mailbox\n", PACKAGE); + printf ("usage: %s [ flags ] mailbox [mailbox ...]\n", PACKAGE); puts (" -c, --config CONFIG read an alternate config file (default: ~/.isyncrc)"); puts @@ -113,7 +113,7 @@ load_config (char *where) char buf[1024]; struct passwd *pw; config_t **cur = &box; - char *p; + char *p, *q; int line = 0; FILE *fp; @@ -138,15 +138,12 @@ load_config (char *where) { if (buf[0]) buf[strlen (buf) - 1] = 0; - line++; - if (buf[0] == '#') - continue; p = buf; - while (*p && !isspace ((unsigned char) *p)) - p++; - while (isspace ((unsigned char) *p)) - p++; - if (!strncasecmp ("mailbox", buf, 7)) + q = next_arg (&p); + line++; + if (!q || *q == '#') + continue; + if (!strncasecmp ("mailbox", q, 7)) { if (*cur) cur = &(*cur)->next; @@ -154,7 +151,7 @@ load_config (char *where) config_defaults (*cur); (*cur)->path = strdup (p); } - else if (!strncasecmp ("host", buf, 4)) + else if (!strncasecmp ("host", q, 4)) { #if HAVE_LIBSSL if (!strncasecmp ("imaps:", p, 6)) @@ -177,47 +174,47 @@ load_config (char *where) else global.host = strdup (p); } - else if (!strncasecmp ("user", buf, 4)) + else if (!strncasecmp ("user", q, 4)) { if (*cur) (*cur)->user = strdup (p); else global.user = strdup (p); } - else if (!strncasecmp ("pass", buf, 4)) + else if (!strncasecmp ("pass", q, 4)) { if (*cur) (*cur)->pass = strdup (p); else global.pass = strdup (p); } - else if (!strncasecmp ("port", buf, 4)) + else if (!strncasecmp ("port", q, 4)) { if (*cur) (*cur)->port = atoi (p); else global.port = atoi (p); } - else if (!strncasecmp ("box", buf, 3)) + else if (!strncasecmp ("box", q, 3)) { if (*cur) (*cur)->box = strdup (p); else global.box = strdup (p); } - else if (!strncasecmp ("alias", buf, 5)) + else if (!strncasecmp ("alias", q, 5)) { if (*cur) (*cur)->alias = strdup (p); } - else if (!strncasecmp ("maxsize", buf, 7)) + else if (!strncasecmp ("maxsize", q, 7)) { if (*cur) (*cur)->max_size = atol (p); else global.max_size = atol (p); } - else if (!strncasecmp ("UseNamespace", buf, 12)) + else if (!strncasecmp ("UseNamespace", q, 12)) { if (*cur) (*cur)->use_namespace = (strcasecmp (p, "yes") == 0); @@ -225,44 +222,51 @@ load_config (char *where) global.use_namespace = (strcasecmp (p, "yes") == 0); } #if HAVE_LIBSSL - else if (!strncasecmp ("CertificateFile", buf, 15)) + else if (!strncasecmp ("CertificateFile", q, 15)) { if (*cur) (*cur)->cert_file = strdup (p); else global.cert_file = strdup (p); } - else if (!strncasecmp ("RequireSSL", buf, 10)) + else if (!strncasecmp ("RequireSSL", q, 10)) { if (*cur) (*cur)->require_ssl = (strcasecmp (p, "yes") == 0); else global.require_ssl = (strcasecmp (p, "yes") == 0); } - else if (!strncasecmp ("UseSSLv2", buf, 8)) + else if (!strncasecmp ("UseSSLv2", q, 8)) { if (*cur) (*cur)->use_sslv2 = (strcasecmp (p, "yes") == 0); else global.use_sslv2 = (strcasecmp (p, "yes") == 0); } - else if (!strncasecmp ("UseSSLv3", buf, 8)) + else if (!strncasecmp ("UseSSLv3", q, 8)) { if (*cur) (*cur)->use_sslv3 = (strcasecmp (p, "yes") == 0); else global.use_sslv3 = (strcasecmp (p, "yes") == 0); } - else if (!strncasecmp ("UseTLSv1", buf, 8)) + else if (!strncasecmp ("UseTLSv1", q, 8)) { if (*cur) (*cur)->use_tlsv1 = (strcasecmp (p, "yes") == 0); else global.use_tlsv1 = (strcasecmp (p, "yes") == 0); } + else if (!strncasecmp ("RequireCRAM", q, 11)) + { + if (*cur) + (*cur)->require_cram = (strcasecmp (p, "yes") == 0); + else + global.require_cram = (strcasecmp (p, "yes") == 0); + } #endif else if (buf[0]) - printf ("%s:%d:unknown command:%s", path, line, buf); + printf ("%s:%d:unknown command:%s", path, line, q); } fclose (fp); } @@ -310,7 +314,7 @@ main (int argc, char **argv) int i; config_t *box; mailbox_t *mail; - imap_t *imap; + imap_t *imap = 0; int expunge = 0; /* by default, don't delete anything */ int fast = 0; int delete = 0; @@ -389,75 +393,80 @@ main (int argc, char **argv) load_config (config); - box = find_box (argv[optind]); - if (!box) + for (; argv[optind]; optind++) { - /* if enough info is given on the command line, don't worry if - * the mailbox isn't defined. - */ - if (!global.host) + box = find_box (argv[optind]); + if (!box) { - puts ("No such mailbox"); - exit (1); - } - global.path = argv[optind]; - box = &global; - } - - if (!box->pass) - { - char *pass = getpass ("Password:"); - - if (!pass) - { - puts ("Aborting, no password"); - exit (1); - } - box->pass = strdup (pass); - } - - printf ("Reading %s\n", box->path); - mail = maildir_open (box->path, fast); - if (!mail) - { - puts ("Unable to load mailbox"); - exit (1); - } - - imap = imap_open (box, fast ? mail->maxuid + 1 : 1); - if (!imap) - exit (1); - - puts ("Synchronizing"); - i = delete ? SYNC_DELETE : 0; - i |= expunge ? SYNC_EXPUNGE : 0; - if (sync_mailbox (mail, imap, i, box->max_size)) - exit (1); - - if (!fast) - { - if (expunge && (imap->deleted || mail->deleted)) - { - /* remove messages marked for deletion */ - printf ("Expunging %d messages from server\n", imap->deleted); - if (imap_expunge (imap)) + /* if enough info is given on the command line, don't worry if + * the mailbox isn't defined. + */ + if (!global.host) + { + puts ("No such mailbox"); exit (1); - printf ("Expunging %d messages from local mailbox\n", - mail->deleted); - if (maildir_expunge (mail, 0)) + } + global.path = argv[optind]; + box = &global; + } + + if (!box->pass) + { + char *pass = getpass ("Password:"); + + if (!pass) + { + puts ("Aborting, no password"); + exit (1); + } + box->pass = strdup (pass); + } + + printf ("Reading %s\n", box->path); + mail = maildir_open (box->path, fast); + if (!mail) + { + puts ("Unable to load mailbox"); + exit (1); + } + + imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap); + if (!imap) + exit (1); + + puts ("Synchronizing"); + i = delete ? SYNC_DELETE : 0; + i |= expunge ? SYNC_EXPUNGE : 0; + if (sync_mailbox (mail, imap, i, box->max_size)) + exit (1); + + if (!fast) + { + if (expunge && (imap->deleted || mail->deleted)) + { + /* remove messages marked for deletion */ + printf ("Expunging %d messages from server\n", imap->deleted); + if (imap_expunge (imap)) + exit (1); + printf ("Expunging %d messages from local mailbox\n", + mail->deleted); + if (maildir_expunge (mail, 0)) + exit (1); + } + /* remove messages deleted from server. this can safely be an + * `else' clause since dead messages are marked as deleted by + * sync_mailbox. + */ + else if (delete) + maildir_expunge (mail, 1); + + /* write changed flags back to the mailbox */ + printf ("Committing changes to %s\n", mail->path); + if (maildir_sync (mail)) exit (1); } - /* remove messages deleted from server. this can safely be an - * `else' clause since dead messages are marked as deleted by - * sync_mailbox. - */ - else if (delete) - maildir_expunge (mail, 1); - /* write changed flags back to the mailbox */ - printf ("Committing changes to %s\n", mail->path); - if (maildir_sync (mail)) - exit (1); + maildir_close (mail); } /* gracefully close connection to the IMAP server */