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 */