isync/main.c
Michael Elkins acd674f93e 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.
2000-12-27 21:14:22 +00:00

477 lines
10 KiB
C

/* $Id$
*
* isync - IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
*
* 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[] = {
{"config", 1, NULL, 'c'},
{"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'},
{"user", 1, NULL, 'u'},
{"version", 0, NULL, 'v'},
{"verbose", 0, NULL, 'V'},
{0, 0, 0, 0}
};
#endif
config_t global;
unsigned int Tag = 0;
static config_t *box = 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 Michael R. Elkins <me@mutt.org>");
printf ("usage: %s [ flags ] mailbox [mailbox ...]\n", PACKAGE);
puts
(" -c, --config CONFIG read an alternate config file (default: ~/.isyncrc)");
puts
(" -d, --delete delete local msgs that don't exist on the server");
puts
(" -e, --expunge expunge deleted messages from the server");
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)");
exit (0);
}
/* set defaults from the global configuration section */
static void
config_defaults (config_t * conf)
{
conf->user = global.user;
conf->pass = global.pass;
conf->port = global.port;
conf->box = global.box;
conf->host = global.host;
conf->max_size = global.max_size;
conf->use_namespace = global.use_namespace;
#if HAVE_LIBSSL
conf->require_ssl = global.require_ssl;
conf->use_imaps = global.use_imaps;
conf->cert_file = global.cert_file;
conf->use_sslv2 = global.use_sslv2;
conf->use_sslv3 = global.use_sslv3;
conf->use_tlsv1 = global.use_tlsv1;
#endif
}
static void
load_config (char *where)
{
char path[_POSIX_PATH_MAX];
char buf[1024];
struct passwd *pw;
config_t **cur = &box;
char *p, *q;
int line = 0;
FILE *fp;
if (!where)
{
pw = getpwuid (getuid ());
snprintf (path, sizeof (path), "%s/.isyncrc", pw->pw_dir);
where = path;
}
printf ("Reading %s\n", where);
fp = fopen (where, "r");
if (!fp)
{
if (errno != ENOENT)
{
perror ("fopen");
return;
}
}
while ((fgets (buf, sizeof (buf) - 1, fp)))
{
if (buf[0])
buf[strlen (buf) - 1] = 0;
p = buf;
q = next_arg (&p);
line++;
if (!q || *q == '#')
continue;
if (!strncasecmp ("mailbox", q, 7))
{
if (*cur)
cur = &(*cur)->next;
*cur = calloc (1, sizeof (config_t));
config_defaults (*cur);
(*cur)->path = strdup (p);
}
else if (!strncasecmp ("host", q, 4))
{
#if HAVE_LIBSSL
if (!strncasecmp ("imaps:", p, 6))
{
p += 6;
if (*cur)
{
(*cur)->use_imaps = 1;
(*cur)->port = 993;
}
else
{
global.use_imaps = 1;
global.port = 993;
}
}
#endif
if (*cur)
(*cur)->host = strdup (p);
else
global.host = strdup (p);
}
else if (!strncasecmp ("user", q, 4))
{
if (*cur)
(*cur)->user = strdup (p);
else
global.user = strdup (p);
}
else if (!strncasecmp ("pass", q, 4))
{
if (*cur)
(*cur)->pass = strdup (p);
else
global.pass = strdup (p);
}
else if (!strncasecmp ("port", q, 4))
{
if (*cur)
(*cur)->port = atoi (p);
else
global.port = atoi (p);
}
else if (!strncasecmp ("box", q, 3))
{
if (*cur)
(*cur)->box = strdup (p);
else
global.box = strdup (p);
}
else if (!strncasecmp ("alias", q, 5))
{
if (*cur)
(*cur)->alias = strdup (p);
}
else if (!strncasecmp ("maxsize", q, 7))
{
if (*cur)
(*cur)->max_size = atol (p);
else
global.max_size = atol (p);
}
else if (!strncasecmp ("UseNamespace", q, 12))
{
if (*cur)
(*cur)->use_namespace = (strcasecmp (p, "yes") == 0);
else
global.use_namespace = (strcasecmp (p, "yes") == 0);
}
#if HAVE_LIBSSL
else if (!strncasecmp ("CertificateFile", q, 15))
{
if (*cur)
(*cur)->cert_file = strdup (p);
else
global.cert_file = strdup (p);
}
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", q, 8))
{
if (*cur)
(*cur)->use_sslv2 = (strcasecmp (p, "yes") == 0);
else
global.use_sslv2 = (strcasecmp (p, "yes") == 0);
}
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", 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, q);
}
fclose (fp);
}
static config_t *
find_box (const char *s)
{
config_t *p = box;
for (; p; p = p->next)
if (!strcmp (s, p->path) || (p->alias && !strcmp (s, p->alias)))
return p;
return 0;
}
char *
next_arg (char **s)
{
char *ret;
if (!s)
return 0;
if (!*s)
return 0;
while (isspace ((unsigned char) **s))
(*s)++;
if (!**s)
{
*s = 0;
return 0;
}
ret = *s;
while (**s && !isspace ((unsigned char) **s))
(*s)++;
if (**s)
*(*s)++ = 0;
if (!**s)
*s = 0;
return ret;
}
int
main (int argc, char **argv)
{
int i;
config_t *box;
mailbox_t *mail;
imap_t *imap = 0;
int expunge = 0; /* by default, don't delete anything */
int fast = 0;
int delete = 0;
char *config = 0;
struct passwd *pw;
pw = getpwuid (getuid ());
/* defaults */
memset (&global, 0, sizeof (global));
global.port = 143;
global.box = "INBOX";
global.user = strdup (pw->pw_name);
global.max_size = 0;
global.use_namespace = 1;
#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;
global.use_sslv2 = 1;
global.use_sslv3 = 1;
global.use_tlsv1 = 1;
#endif
#if HAVE_GETOPT_LONG
while ((i = getopt_long (argc, argv, "defhp:u:r:s:vV", Opts, NULL)) != -1)
#else
while ((i = getopt (argc, argv, "defhp:u:r:s:vV")) != -1)
#endif
{
switch (i)
{
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 'r':
global.box = optarg;
break;
case 's':
global.host = optarg;
break;
case 'u':
free (global.user);
global.user = optarg;
break;
case 'V':
Verbose = 1;
break;
case 'v':
version ();
default:
usage ();
}
}
if (!argv[optind])
{
puts ("No box specified");
usage ();
}
gethostname (Hostname, sizeof (Hostname));
load_config (config);
for (; argv[optind]; optind++)
{
box = find_box (argv[optind]);
if (!box)
{
/* 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);
}
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);
}
maildir_close (mail);
}
/* gracefully close connection to the IMAP server */
imap_close (imap);
exit (0);
}