isync/main.c

372 lines
8.0 KiB
C
Raw Normal View History

/* $Id$
*
* isync - IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org>
2000-12-20 21:41:21 +00:00
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include "isync.h"
#if HAVE_GETOPT_LONG
#define _GNU_SOURCE
#include <getopt.h>
struct option Opts[] = {
{"all", 0, NULL, 'a'},
2000-12-20 21:41:21 +00:00
{"config", 1, NULL, 'c'},
{"create", 0, NULL, 'C'},
2000-12-20 21:41:21 +00:00
{"delete", 0, NULL, 'd'},
{"expunge", 0, NULL, 'e'},
{"fast", 0, NULL, 'f'},
{"help", 0, NULL, 'h'},
{"remote", 1, NULL, 'r'},
{"host", 1, NULL, 's'},
{"port", 1, NULL, 'p'},
{"quiet", 0, NULL, 'q'},
2000-12-20 21:41:21 +00:00
{"user", 1, NULL, 'u'},
{"version", 0, NULL, 'v'},
{"verbose", 0, NULL, 'V'},
{0, 0, 0, 0}
};
#endif
config_t global;
unsigned int Tag = 0;
char Hostname[256];
int Verbose = 0;
static void
version (void)
{
printf ("%s %s\n", PACKAGE, VERSION);
exit (0);
}
static void
usage (void)
{
printf ("%s %s IMAP4 to maildir synchronizer\n", PACKAGE, VERSION);
puts ("Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org>");
printf ("usage: %s [ flags ] mailbox [mailbox ...]\n", PACKAGE);
puts (" -a, --all Synchronize all defined mailboxes");
puts (" -c, --config CONFIG read an alternate config file (default: ~/.isyncrc)");
puts (" -C, --create create local maildir mailbox if nonexistent");
puts (" -d, --delete delete local msgs that don't exist on the server");
puts (" -e, --expunge expunge deleted messages from the server");
2000-12-20 21:41:21 +00:00
puts (" -f, --fast only fetch new messages");
puts (" -h, --help display this help message");
puts (" -p, --port PORT server IMAP port");
puts (" -r, --remote BOX remote mailbox");
puts (" -s, --host HOST IMAP server address");
puts (" -u, --user USER IMAP user name");
puts (" -v, --version display version");
puts (" -V, --verbose verbose mode (display network traffic)");
puts ("Compile time options:");
#if HAVE_LIBSSL
puts (" +HAVE_LIBSSL");
#else
puts (" -HAVE_LIBSSL");
#endif
2000-12-20 21:41:21 +00:00
exit (0);
}
char *
next_arg (char **s)
{
char *ret;
if (!s)
return 0;
if (!*s)
return 0;
while (isspace ((unsigned char) **s))
2000-12-20 21:41:21 +00:00
(*s)++;
if (!**s)
{
*s = 0;
return 0;
}
if (**s == '"')
{
++*s;
ret = *s;
*s = strchr (*s, '"');
}
else
{
ret = *s;
while (**s && !isspace ((unsigned char) **s))
(*s)++;
}
if (*s)
{
if (**s)
*(*s)++ = 0;
if (!**s)
*s = 0;
}
2000-12-20 21:41:21 +00:00
return ret;
}
int
main (int argc, char **argv)
{
int i;
config_t *box = 0;
mailbox_t *mail = 0;
imap_t *imap = 0;
2000-12-20 21:41:21 +00:00
int expunge = 0; /* by default, don't delete anything */
int fast = 0;
int delete = 0;
char *config = 0;
struct passwd *pw;
int quiet = 0;
int all = 0;
int create = 0;
2000-12-20 21:41:21 +00:00
pw = getpwuid (getuid ());
/* defaults */
memset (&global, 0, sizeof (global));
global.port = 143;
global.box = "INBOX";
global.user = strdup (pw->pw_name);
global.maildir = strdup (pw->pw_dir);
global.max_size = 0;
global.max_messages = 0;
global.use_namespace = 1;
2000-12-21 06:27:05 +00:00
#if HAVE_LIBSSL
/* this will probably annoy people, but its the best default just in
* case people forget to turn it on
*/
global.require_ssl = 1;
patch from Daniel Resare <noa@metamatrix.se>: 1 giving a path to a nonexistant rc-file with the -c argument dumps core The patch adds a check to ensure that the given rc-file is accessible 2 the error messages given from failed openssl calls are bogus The handles the error from SSL_connect () correctly. The bug is understndable since the error handling in openssl is quite obfuscated. Good news is that the documentation manapges has been greatly updated in the latest version (0.9.6). See in particular err(3), ERR_get_error(3) and SSL_get_error(3). Please note that possible SSL_ERROR_SSL type errors from SSL_read() and SSL_write() is not handled. This should also be fixed. 3 connecting using the STARTTLS command with an imap server that is configured only to accept the TLSv1 protocol gives an error because isync sends an SSLv2 Hello message for backwards compability. (This is the case with the uw-imap 2000 that ships with redhat-7.0) I've read RFC2595 several times to see if it says something about compability SSL2/SSL3 hello messages but can't find anything. IMHO the correct thing to do is change the default to not use SSL2/3 compability hello when using the STARTTLS command but use it if the imaps port is used. The patch implements this change 4 repeated calls to SSL_CTX_set_options overwrites the old settings (the values needs to be ORed together) fixed in the patch patch from me@mutt.org: \Recent messages were put in the cur/ directory instead of new/ give error message when the LOGIN command fails
2001-02-14 20:46:41 +00:00
global.use_sslv2 = 0;
global.use_sslv3 = 0;
global.use_tlsv1 = 1;
2000-12-21 06:27:05 +00:00
#endif
2000-12-20 21:41:21 +00:00
#define FLAGS "aCc:defhp:qu:r:s:vV"
2000-12-20 21:41:21 +00:00
#if HAVE_GETOPT_LONG
while ((i = getopt_long (argc, argv, FLAGS, Opts, NULL)) != -1)
2000-12-20 21:41:21 +00:00
#else
while ((i = getopt (argc, argv, FLAGS)) != -1)
2000-12-20 21:41:21 +00:00
#endif
{
switch (i)
{
case 'a':
all = 1;
break;
case 'C':
create = 1;
break;
case 'c':
config = optarg;
break;
case 'd':
delete = 1;
break;
case 'e':
expunge = 1;
break;
case 'f':
fast = 1;
break;
case 'p':
global.port = atoi (optarg);
break;
case 'q':
quiet = 1;
Verbose = 0;
break;
case 'r':
global.box = optarg;
break;
case 's':
#if HAVE_LIBSSL
if (!strncasecmp ("imaps:", optarg, 6))
{
global.use_imaps = 1;
optarg += 6;
}
#endif
global.host = optarg;
break;
case 'u':
free (global.user);
global.user = optarg;
break;
case 'V':
Verbose = 1;
break;
case 'v':
version ();
default:
usage ();
2000-12-20 21:41:21 +00:00
}
}
if (!argv[optind] && !all)
2000-12-20 21:41:21 +00:00
{
puts ("No mailbox specified");
2000-12-20 21:41:21 +00:00
usage ();
}
gethostname (Hostname, sizeof (Hostname));
load_config (config);
for (box = boxes; (all && box) || (!all && argv[optind]); optind++)
2000-12-20 21:41:21 +00:00
{
if (!all)
2000-12-20 21:41:21 +00:00
{
if (NULL == (box = find_box (argv[optind])))
{
/* if enough info is given on the command line, don't worry if
* the mailbox isn't defined.
*/
if (!global.host)
{
fprintf (stderr, "%s: no such mailbox\n", argv[optind]);
/* continue is ok here because we are not handling the
* `all' case.
*/
continue;
}
global.path = argv[optind];
box = &global;
}
2000-12-20 21:41:21 +00:00
}
do {
if (!box->pass)
{
/* if we don't have a global password set, prompt the user for
* it now.
*/
if (!global.pass)
{
global.pass = getpass ("Password:");
if (!global.pass)
{
fprintf (stderr, "Skipping %s, no password", box->path);
break;
}
}
box->pass = strdup (global.pass);
}
if (!quiet)
printf ("Reading %s\n", box->path);
i = 0;
if (fast)
i |= OPEN_FAST;
if (create)
i |= OPEN_CREATE;
mail = maildir_open (box->path, i);
if (!mail)
{
fprintf (stderr, "%s: unable to open mailbox\n", box->path);
break;
}
2000-12-20 21:41:21 +00:00
imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap, 0);
if (!imap)
{
fprintf (stderr, "%s: skipping mailbox due to IMAP error\n",
box->path);
break;
}
2000-12-20 21:41:21 +00:00
if (!quiet)
puts ("Synchronizing");
i = 0;
if (quiet)
i |= SYNC_QUIET;
i |= (delete || box->delete) ? SYNC_DELETE : 0;
i |= (expunge || box->expunge) ? SYNC_EXPUNGE : 0;
if (sync_mailbox (mail, imap, i, box->max_size, box->max_messages))
{
imap_close (imap); /* Just to be safe. Don't really know
* what the problem was.
*/
imap = NULL; /* context no longer valid */
break;
}
2000-12-20 21:41:21 +00:00
if (!fast)
{
if ((expunge || box->expunge) &&
(imap->deleted || mail->deleted))
{
/* remove messages marked for deletion */
if (!quiet)
printf ("Expunging %d messages from server\n",
imap->deleted);
if (imap_expunge (imap))
{
imap_close (imap);
imap = NULL;
break;
}
if (!quiet)
printf ("Expunging %d messages from local mailbox\n",
mail->deleted);
if (maildir_expunge (mail, 0))
break;
}
/* 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);
}
} while (0);
/* we never sync the same mailbox twice, so close it now */
if (mail)
maildir_close (mail);
/* the imap connection is not closed so we can keep the connection
* open, and there is no IMAP command for un-SELECT-ing a mailbox.
*/
if (all)
box = box->next;
2000-12-20 21:41:21 +00:00
}
/* gracefully close connection to the IMAP server */
imap_close (imap);
free_config ();
#if DEBUG
debug_cleanup ();
#endif
2000-12-20 21:41:21 +00:00
exit (0);
}