Bunch 'o patches from Oswald Buddenhagen:

i implemented some cool stuff (tm).
first, the long missing "create server-side missing mailboxes". -C now
creates both local and remote boxes; -L and -R create only local/remote.
second, i implemented a 1:1 remote:local folder mapping (-1) with an
optional INBOX exception (inbox/-I). the remote folder is specified with
the folder keyword (or -F switch) and takes precedence over the
namespace setting. the local directory with the mailboxes can now be
specified on the command line, too (-M).

another patch:
- made the -1 switch settable permanently (OneToOne). after all, you
  usually define your mailbox layout once forever. removed -A, as it is
  semantically -a modified by -1.
- cleaned up message output a bit. still, the quiet variable should be
  used throughout the program. at best, create some generic output
  function, which obeys a global verbosity level variable.
- optimized + cleaned up configuration parser slightly
- minor cleanups

add an (almost) unique id to every uploaded message and search for it
right after. i thought about using the message-id, but a) it is not
guaranteed to be unique in a mailbox (imagine you edit a mail and store
the dupe in the same box) and b) some mails (e.g., postponed) don't even
have one. a downside of the current implementation is, that this
id-header remains in the mailbox, but given that it wastes only 27 bytes
per mail and removing it would mean several roundtrips more, this seems
acceptable.
i changed the line-counting loop to use a mmapped file instead of
reading it in chunks, as it makes things simpler and is probably even
faster for big mails.
the amount of goto statements in my code may be scary, but c is simply
lacking a multi-level break statement. :)

this is the "shut up" patch. :) it makes the -q option consequent, so to
say.
additionally it adds an -l option which gathers all defined/found
mailboxes and just outputs the list. don't ask what i need it for. ;)
This commit is contained in:
Michael Elkins 2002-10-30 02:23:05 +00:00
parent 0bd3709a9d
commit 7cd74a1179
8 changed files with 780 additions and 664 deletions

12
.cvsignore Normal file
View File

@ -0,0 +1,12 @@
.deps
Makefile
Makefile.in
autom4te.cache
aclocal.m4
build-stamp
config.cache
config.log
config.status
configure
configure-stamp
isync

203
config.c
View File

@ -91,13 +91,23 @@ expand_strdup (const char *s)
return strdup (s);
}
static int
is_true (const char *val)
{
return
!strcasecmp (val, "yes") ||
!strcasecmp (val, "true") ||
!strcasecmp (val, "on") ||
!strcmp (val, "1");
}
void
load_config (const char *where)
load_config (const char *where, int *o2o)
{
char path[_POSIX_PATH_MAX];
char buf[1024];
struct passwd *pw;
config_t **cur = &boxes;
config_t **cur = &boxes, *cfg;
int line = 0;
FILE *fp;
char *p, *cmd, *val;
@ -109,7 +119,7 @@ load_config (const char *where)
where = path;
}
printf ("Reading %s\n", where);
info ("Reading configuration file %s\n", where);
fp = fopen (where, "r");
if (!fp)
@ -119,6 +129,7 @@ load_config (const char *where)
return;
}
buf[sizeof buf - 1] = 0;
cfg = &global;
while ((fgets (buf, sizeof (buf) - 1, fp)))
{
p = buf;
@ -129,174 +140,116 @@ load_config (const char *where)
continue;
if (!strcasecmp ("mailbox", cmd))
{
if (*cur)
if (*o2o)
break;
cur = &(*cur)->next;
*cur = calloc (1, sizeof (config_t));
config_defaults (*cur);
cfg = *cur = malloc (sizeof (config_t));
config_defaults (cfg);
/* not expanded at this point */
(*cur)->path = strdup (val);
cfg->path = strdup (val);
}
else if (!strcasecmp ("OneToOne", cmd))
{
if (*cur) {
forbid:
fprintf (stderr,
"%s:%d: keyword '%s' allowed only in global section\n",
path, line, cmd);
continue;
}
*o2o = is_true (val);
}
else if (!strcasecmp ("maildir", cmd))
{
if (*cur)
goto forbid;
/* this only affects the global setting */
free (global.maildir);
global.maildir = expand_strdup (val);
}
else if (!strcasecmp ("folder", cmd))
{
if (*cur)
goto forbid;
/* this only affects the global setting */
global.folder = strdup (val);
}
else if (!strcasecmp ("inbox", cmd))
{
if (*cur)
goto forbid;
/* this only affects the global setting */
global.inbox = strdup (val);
}
else if (!strcasecmp ("host", cmd))
{
#if HAVE_LIBSSL
if (!strncasecmp ("imaps:", val, 6))
{
val += 6;
if (*cur)
{
(*cur)->use_imaps = 1;
(*cur)->port = 993;
(*cur)->use_sslv2 = 1;
(*cur)->use_sslv3 = 1;
}
else
{
global.use_imaps = 1;
global.port = 993;
global.use_sslv2 = 1;
global.use_sslv3 = 1;
}
cfg->use_imaps = 1;
cfg->port = 993;
cfg->use_sslv2 = 1;
cfg->use_sslv3 = 1;
}
#endif
if (*cur)
(*cur)->host = strdup (val);
else
global.host = strdup (val);
cfg->host = strdup (val);
}
else if (!strcasecmp ("user", cmd))
{
if (*cur)
(*cur)->user = strdup (val);
else
else {
free (global.user);
global.user = strdup (val);
}
}
else if (!strcasecmp ("pass", cmd))
{
if (*cur)
(*cur)->pass = strdup (val);
else
global.pass = strdup (val);
}
cfg->pass = strdup (val);
else if (!strcasecmp ("port", cmd))
{
if (*cur)
(*cur)->port = atoi (val);
else
global.port = atoi (val);
}
cfg->port = atoi (val);
else if (!strcasecmp ("box", cmd))
{
if (*cur)
(*cur)->box = strdup (val);
else
global.box = strdup (val);
}
cfg->box = strdup (val);
else if (!strcasecmp ("alias", cmd))
{
if (*cur)
(*cur)->alias = strdup (val);
if (!*cur) {
fprintf (stderr,
"%s:%d: keyword 'alias' allowed only in mailbox specification\n",
path, line);
continue;
}
cfg->alias = strdup (val);
}
else if (!strcasecmp ("maxsize", cmd))
{
if (*cur)
(*cur)->max_size = atol (val);
else
global.max_size = atol (val);
}
cfg->max_size = atol (val);
else if (!strcasecmp ("MaxMessages", cmd))
{
if (*cur)
(*cur)->max_messages = atol (val);
else
global.max_messages = atol (val);
}
cfg->max_messages = atol (val);
else if (!strcasecmp ("UseNamespace", cmd))
{
if (*cur)
(*cur)->use_namespace = (strcasecmp (val, "yes") == 0);
else
global.use_namespace = (strcasecmp (val, "yes") == 0);
}
cfg->use_namespace = is_true (val);
else if (!strcasecmp ("CopyDeletedTo", cmd))
{
if (*cur)
(*cur)->copy_deleted_to = strdup (val);
else
global.copy_deleted_to = strdup (val);
}
cfg->copy_deleted_to = strdup (val);
else if (!strcasecmp ("Tunnel", cmd))
{
if (*cur)
(*cur)->tunnel = strdup (val);
else
global.tunnel = strdup (val);
}
cfg->tunnel = strdup (val);
else if (!strcasecmp ("Expunge", cmd))
{
if (*cur)
(*cur)->expunge = (strcasecmp (val, "yes") == 0);
else
global.expunge = (strcasecmp (val, "yes") == 0);
}
cfg->expunge = is_true (val);
else if (!strcasecmp ("Delete", cmd))
{
if (*cur)
(*cur)->delete = (strcasecmp (val, "yes") == 0);
else
global.delete = (strcasecmp (val, "yes") == 0);
}
cfg->delete = is_true (val);
#if HAVE_LIBSSL
else if (!strcasecmp ("CertificateFile", cmd))
{
if (*cur)
(*cur)->cert_file = expand_strdup (val);
else
global.cert_file = expand_strdup (val);
}
cfg->cert_file = expand_strdup (val);
else if (!strcasecmp ("RequireSSL", cmd))
{
if (*cur)
(*cur)->require_ssl = (strcasecmp (val, "yes") == 0);
else
global.require_ssl = (strcasecmp (val, "yes") == 0);
}
cfg->require_ssl = is_true (val);
else if (!strcasecmp ("UseSSLv2", cmd))
{
if (*cur)
(*cur)->use_sslv2 = (strcasecmp (val, "yes") == 0);
else
global.use_sslv2 = (strcasecmp (val, "yes") == 0);
}
cfg->use_sslv2 = is_true (val);
else if (!strcasecmp ("UseSSLv3", cmd))
{
if (*cur)
(*cur)->use_sslv3 = (strcasecmp (val, "yes") == 0);
else
global.use_sslv3 = (strcasecmp (val, "yes") == 0);
}
cfg->use_sslv3 = is_true (val);
else if (!strcasecmp ("UseTLSv1", cmd))
{
if (*cur)
(*cur)->use_tlsv1 = (strcasecmp (val, "yes") == 0);
else
global.use_tlsv1 = (strcasecmp (val, "yes") == 0);
}
cfg->use_tlsv1 = is_true (val);
else if (!strcasecmp ("RequireCRAM", cmd))
{
if (*cur)
(*cur)->require_cram = (strcasecmp (val, "yes") == 0);
else
global.require_cram = (strcasecmp (val, "yes") == 0);
}
cfg->require_cram = is_true (val);
#endif
else if (buf[0])
printf ("%s:%d:unknown keyword:%s\n", path, line, cmd);
fprintf (stderr, "%s:%d: unknown keyword '%s'\n", path, line, cmd);
}
fclose (fp);
}

604
imap.c

File diff suppressed because it is too large Load Diff

314
isync.1
View File

@ -1,6 +1,6 @@
.ig
\" isync - IMAP4 to maildir mailbox synchronizer
\" Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org>
\" Copyright (C) 2000-2002 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
@ -16,66 +16,70 @@
\" 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 "2002 Jun 17"
.TH isync 1 "2002 Oct 14"
..
.SH NAME
isync - synchronize IMAP4 and maildir mailboxes
..
.SH SYNOPSIS
.B isync
[
.I options...
]
.I mailbox
[
.I mailbox ...
]
\fBisync\fR [\fIoptions...\fR] \fImailbox\fR [\fImailbox ...\fR]
.br
\fBisync\fR [\fIoptions...\fR] \fI-a\fR
.br
\fBisync\fR [\fIoptions...\fR] \fI-l\fR
..
.SH DESCRIPTION
.B isync
is a command line application which synchronizes a local maildir-style
mailbox with a remote IMAP4 mailbox, suitable for use in IMAP-disconnected
mode. Multiple copies of the remote IMAP4 mailbox can be maintained, and
all flags are synchronized.
\fBisync\fR is a command line application which synchronizes a local
maildir-style mailbox with a remote IMAP4 mailbox, suitable for use in
IMAP-disconnected mode. Multiple copies of the remote IMAP4 mailbox can
be maintained, and all flags are synchronized.
..
.SH OPTIONS
.TP
\fB-a\fR, \fB--all\fR
Synchronize all mailboxes specified in the user's ~/.isyncrc.
\fB-c\fR, \fB--config\fR \fIfile\fR
Read configuration from \fIfile\fR.
By default, the configuration is read from ~/.isyncrc if it exists.
.TP
\fB-C\fR, \fB--create\fR
\fB-1\fR, \fB--one-to-one\fR
Instead of using the mailbox specifications in ~/.isyncrc, isync will pick up
all mailboxes from the local directory and remote folder and map them 1:1
onto each other according to their names.
.TP
\fB-I\fR, \fB--inbox\fR \fImailbox\fR
Exception to the 1:1 mapping created by -1: the special IMAP mailbox \fIINBOX\fR
is mapped to the local \fImailbox\fR (relative to the maildir).
.TP
\fB-a\fR, \fB--all\fR
Synchronize all mailboxes (either specified in ~/.isyncrc or determined by the
1:1 mapping).
.TP
\fB-l\fR, \fB--list\fR
Don't synchronize anything, but list all mailboxes and exit.
.TP
\fB-L\fR, \fB--create-local\fR
Automatically create the local maildir-style mailbox if it doesn't already
exist.
.TP
\fB-c\fR, \fB--config\fR \fIfile\fR
Read configuration from
.I file
By default, configuration is read from ~/.isyncrc if it exists.
\fB-R\fR, \fB--create-remote\fR
Automatically create the remote IMAP mailbox if it doesn't already exist.
.TP
.B -d, --delete
Causes
.B isync
to delete messages from the local maildir mailbox which do not exist on the
IMAP server. By default,
.I dead
messages are
.B not
deleted.
\fB-C\fR, \fB--create\fR
Automatically create any mailboxes if they doesn't already exist.
.TP
\fB-d\fR, \fB--delete\fR
Causes \fBisync\fR to delete messages from the local maildir mailbox
which do not exist on the IMAP server. By default, \fIdead\fR messages
are \fBnot\fR deleted.
.TP
\fB-e\fR, \fB--expunge\fR
Causes
.B isync
to permanently remove all messages marked for deletion in both the local
maildir mailbox and the remote IMAP mailbox. By default, messages are
.B not
expunged.
Causes \fBisync\fR to permanently remove all messages marked for deletion
in both the local maildir mailbox and the remote IMAP mailbox. By default,
messages are \fBnot\fR expunged.
.TP
\fB-f\fR, \fB--fast\fR
Causes
.B isync
to skip the step of synchronzing message flags between the local maildir
mailbox and the IMAP mailbox. Only new messages existing on the server will
be fetched into the local mailbox.
Causes \fBisync\fR to skip the step of synchronzing message flags between the
local maildir mailbox and the IMAP mailbox. Only new messages existing on the
server will be fetched into the local mailbox.
.TP
\fB-h\fR, \fB--help\fR
Displays a summary of command line options
@ -96,44 +100,34 @@ Specifies the hostname of the IMAP server
\fB-u\fR, \fB--user\fR \fIuser\fR
Specifies the login name to access the IMAP server (default: $USER)
.TP
.B -v, --version
Displays
.B isync
version information
\fB-M\fR, \fB--maildir\fR \fIdir\fR
Specifies the location for your local mailboxes.
.TP
.B -V, --verbose
Enables
.I verbose
mode, which displays the IMAP4 network traffic.
\fB-F\fR, \fB--folder\fR \fIfolder\fR/
Specifies the location for your remote mailboxes.
.TP
\fB-v\fR, \fB--version\fR
Displays \fBisync\fR version information.
.TP
\fB-V\fR, \fB--verbose\fR
Enables \fIverbose\fR mode, which displays the IMAP4 network traffic.
..
.SH CONFIGURATION
.B isync
reads
.I ~/.isyncrc
to load default configuration data. Each line of the configuration file
consists of a command. The following commands are understood:
\fBisync\fR reads \fI~/.isyncrc\fR to load default configuration data.
Each line of the configuration file consists of a command.
The following commands are understood:
.TP
\fBMailbox\fR \fIpath\fR
Defines a local maildir mailbox. All configuration commands following this
line, up until the next
.I Mailbox
command, apply to this mailbox only.
line, up until the next \fIMailbox\fR command, apply to this mailbox only.
..
.TP
\fBHost\fR \fB[\fRimaps:\fB]\fR\fIname\fR
Defines the DNS name or IP address of the IMAP server. If the hostname is
prefixed with
.I imaps:
the connection is assumed to be a SSL connection to port 993 (though you can
change this by placing a
.B Port
command
.B after
the
.B Host
command. Note that some servers support SSL on the default port 143.
.B isync
will always attempt to use SSL if available.
prefixed with \fIimaps:\fR the connection is assumed to be a SSL connection
to port 993 (though you can change this by placing a \fBPort\fR command
\fBafter\fR the \fBHost\fR command. Note that some servers support SSL on
the default port 143. \fBisync\fR will always attempt to use SSL if available.
..
.TP
\fBPort\fR \fIport\fR
@ -150,12 +144,9 @@ Defines the login name on the IMAP server (Default: current user)
..
.TP
\fBPass\fR \fIpassword\fR
Defines the password for
.I username
on the IMAP server. Note that this option is
.B NOT
required. If no password is specified in the configuration file,
.B isync
Defines the password for \fIusername\fR on the IMAP server.
Note that this option is \fBNOT\fR required.
If no password is specified in the configuration file, \fBisync\fR
will prompt you for it.
..
.TP
@ -164,64 +155,58 @@ Defines an alias for the mailbox which can be used as a shortcut on the
command line.
..
.TP
\fBCopyDeletedTo\fR \fIstring\fR
\fBCopyDeletedTo\fR \fImailbox\fR
Specifies the remote IMAP mailbox to copy deleted messages prior to
expunging (Default: none).
..
.TP
\fBDelete\fR \fIyes|no\fR
\fBDelete\fR \fIyes\fR|\fIno\fR
Specifies whether messages in the local copy of the mailbox which don't
exist on the server are automatically deleted. (Default: no).
\fBNOTE:\fR The \fI-d\fR command line option overrides this setting when
set to \fIno\fR.
..
.TP
\fBExpunge\fR \fIyes|no\fR
\fBExpunge\fR \fIyes\fR|\fIno\fR
Specifies whether deleted messages are expunged by default (Default: no).
\fBNOTE:\fR The
.I -e
command line option overrides this setting when set to
\fIno\fR.
\fBNOTE:\fR The \fI-e\fR command line option overrides this setting when
set to \fIno\fR.
..
.TP
\fBMailDir\fR \fIstring\fR
Specifies the location for your mailboxes if a relative path is
specified in a
.I Mailbox
command (Default: \fI~\fR).
.B NOTE:
This directive is only meaningful the in
.I global
\fBMailDir\fR \fIdirectory\fR
Specifies the location of your local mailboxes if a relative path is
specified in a \fIMailbox\fR command (Default: \fI~\fR).
\fBNOTE:\fR This directive is only meaningful in the \fIglobal\fR
section (see below).
..
.TP
\fBFolder\fR \fIdirectory\fR/
Specifies the location of your IMAP mailboxes
specified in \fIBox\fR commands (Default: \fI""\fR).
\fBNOTE:\fR You \fBmust\fR append the hierarchy delimiter (usually
a slash) to this specification.
\fBNOTE 2:\fR This directive is only meaningful in the \fIglobal\fR
section (see below).
..
.TP
\fBMaxMessages\fR \fIcount\fR
Sets the number of messages
.B isync
should keep in a mailbox.
Sets the number of messages \fBisync\fR should keep in a mailbox.
This is useful for mailboxes where you keep a complete archive on the
server, but want to mirror only the last messages (for instance, for mailing
lists.)
The messages that were the first to arrive in the mailbox (independent of the
actual date of the message) will automatically be deleted if you tell
pass
.B isync
the delete (-d, --delete) flag.
actual date of the message) will automatically be deleted if you
pass \fBisync\fR the delete (-d, --delete) flag.
Messages that are flagged (marked as important) will not be automatically
deleted.
If
.I count
is 0, the maximum number of messages is
.B unlimited
If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR
(Default: 0).
..
.TP
\fBMaxSize\fR \fIbytes\fR
Sets a threshold for the maximum message size (in bytes) for which
.B isync
Sets a threshold for the maximum message size (in bytes) for which \fBisync\fR
should fetch from the server. This is useful for weeding out messages with
large attachments. If
.I bytes
is 0, the maximum file size is
.B unlimited.
large attachments. If \fIbytes\fR is 0, the maximum file size is \fBunlimited\fR.
..
.TP
\fBTunnel\fR \fIcommand\fR
@ -229,79 +214,64 @@ Specify a command to run to establish a connection rather than opening a TCP
socket. This allows you to run an IMAP session over an SSH tunnel, for
example.
.TP
\fBUseNamespace\fR \fIyes|no\fR
Selects whether
.B isync
should select mailboxes using the namespace given by the NAMESPACE command.
This is useful with broken IMAP servers. (Default:
.I yes
)
\fBUseNamespace\fR \fIyes\fR|\fIno\fR
Selects whether \fBisync\fR should select mailboxes using the namespace given
by the NAMESPACE command. This is useful with broken IMAP servers. (Default:
\fIyes\fR)
..
.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.
\fBRequireCRAM\fR \fIyes\fR|\fIno\fR
If set to \fIyes\fR, \fBisync\fR 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
server can not be established. (Default:
.I yes
)
\fBRequireSSL\fR \fIyes\fR|\fIno\fR
\fBisync\fR will abort the connection if a TLS/SSL session to the IMAP
server can not be established. (Default: \fIyes\fR)
..
.TP
\fBCertificateFile\fR \fIpath\fR
File containing X.509 CA certificates used to verify server identities.
..
.TP
\fBUseSSLv2\fR \fIyes|no\fR
Should
.B isync
use SSLv2 for communication with the IMAP server over SSL? (Default:
.I yes
if the imaps port is used, otherwise
.I no
)
\fBUseSSLv2\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use SSLv2 for communication with the IMAP server over SSL?
(Default: \fIyes\fR if the imaps port is used, otherwise \fIno\fR)
..
.TP
\fBUseSSLv3\fR \fIyes|no\fR
Should
.B isync
use SSLv3 for communication with the IMAP server over SSL? (Default:
.I yes
if the imaps port is used, otherwise
.I no
)
\fBUseSSLv3\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use SSLv3 for communication with the IMAP server over SSL?
(Default: \fIyes\fR if the imaps port is used, otherwise \fIno\fR)
..
.TP
\fBUseTLSv1\fR \fIyes|no\fR
Should
.B isync
use TLSv1 for communication with the IMAP server over SSL? (Default:
.I yes
)
\fBUseTLSv1\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use TLSv1 for communication with the IMAP server over SSL?
(Default: \fIyes\fR)
..
.TP
\fBOneToOne\fR
\fBisync\fR will ignore any \fIMailbox\fR specifications and instead pick up
all mailboxes from the local \fIMailDir\fR and remote \fIFolder\fR and map
them 1:1 onto each other according to their names.
\fBNOTE:\fR This directive is only meaningful in the \fIglobal\fR
section (see below).
..
.TP
\fBInbox\fR \fImailbox\fR
Exception to the OneToOne mapping: the special IMAP mailbox \fIINBOX\fR
is mapped to the local \fImailbox\fR (relative to the \fIMailDir\fR).
\fBNOTE:\fR This directive is only meaningful in the \fIglobal\fR
section (see below).
..
.P
Configuration commands that appear prior to the first
.B Mailbox
command are considered to be
.I global
Configuration commands that appear prior to the first \fBMailbox\fR
command are considered to be \fIglobal\fR
options which are used as defaults when those specific options are not
specifically set for a defined Mailbox. For example, if you use the same
login name for several IMAP servers, you can put a
.B User
command before the first
.B Mailbox
command, and then leave out the
.B User
command in the sections for each mailbox.
.B isync
will then use the global value by default.
login name for several IMAP servers, you can put a \fBUser\fR command before
the first \fBMailbox\fR command, and then leave out the \fBUser\fR command
in the sections for each mailbox.
\fBisync\fR will then use the global value by default.
..
.SH FILES
.TP
@ -309,20 +279,16 @@ will then use the global value by default.
Default configuration file
..
.SH BUGS
.B isync
does not use NFS-safe locking. It will correctly prevent concurrent
synchronization of a mailbox on the same host, but not across NFS.
\fBisync\fR does not use NFS-safe locking. It will correctly prevent
concurrent synchronization of a mailbox on the same host, but not across NFS.
.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.
.P
If new mail arrives in the IMAP mailbox after
.B isync
If new mail arrives in the IMAP mailbox after \fBisync\fR
has retrieved the initial message list, the new mail will not be fetched
until the next time
.B isync
is invoked.
until the next time \fBisync\fR is invoked.
.P
It is currently impossible to unset the \\Flagged attribute of a message
once it is set. It has to be manually unset everywhere since isync
@ -331,12 +297,12 @@ message.
.P
The ndbm database created for each mailbox is not portable across different
architectures. It currently stores the UID in host byte order.
.P
The configuration file takes precedence over command line options.
.SH SEE ALSO
mutt(1), maildir(5)
.P
Up to date information on
.B isync
can be found at
Up to date information on \fBisync\fR can be found at
http://www.cs.hmc.edu/~me/isync/.
..
.SH AUTHOR

13
isync.h
View File

@ -62,7 +62,9 @@ struct config
int port;
char *user;
char *pass;
char *folder;
char *box;
char *inbox;
char *alias;
char *copy_deleted_to;
char *tunnel;
@ -151,6 +153,7 @@ typedef struct
list_t *ns_personal;
list_t *ns_other;
list_t *ns_shared;
unsigned int have_uidplus:1;
unsigned int have_namespace:1;
#if HAVE_LIBSSL
unsigned int have_cram:1;
@ -163,7 +166,6 @@ imap_t;
/* flags for sync_mailbox */
#define SYNC_DELETE (1<<0) /* delete local that don't exist on server */
#define SYNC_EXPUNGE (1<<1) /* don't fetch deleted messages */
#define SYNC_QUIET (1<<2) /* only display critical errors */
/* flags for maildir_open */
#define OPEN_FAST (1<<0) /* fast open - don't parse */
@ -173,7 +175,10 @@ extern config_t global;
extern config_t *boxes;
extern unsigned int Tag;
extern char Hostname[256];
extern int Verbose;
extern int Verbose, Quiet;
extern void info (const char *, ...);
extern void infoc (char);
#if HAVE_LIBSSL
extern SSL_CTX *SSLContext;
@ -185,7 +190,7 @@ char *next_arg (char **);
int sync_mailbox (mailbox_t *, imap_t *, int, unsigned int, unsigned int);
void load_config (const char *);
void load_config (const char *, int *);
char * expand_strdup (const char *s);
config_t *find_box (const char *);
void free_config (void);
@ -195,8 +200,10 @@ int imap_copy_message (imap_t * imap, unsigned int uid, const char *mailbox);
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_connect (config_t *);
imap_t *imap_open (config_t *, unsigned int, imap_t *, int);
int imap_append_message (imap_t *, int, message_t *);
int imap_list (imap_t *);
mailbox_t *maildir_open (const char *, int flags);
int maildir_expunge (mailbox_t *, int);

View File

@ -242,7 +242,7 @@ maildir_open (const char *path, int flags)
if (p->uid > m->maxuid)
m->maxuid = p->uid;
}
else
else /* XXX remove. every locally generated message triggers this */
puts ("Warning, no UID for message");
if (s)
@ -341,7 +341,7 @@ maildir_clean_tmp (const char *mbox)
char path[_POSIX_PATH_MAX];
DIR *dirp;
struct dirent *entry;
struct stat info;
struct stat st;
time_t now;
snprintf (path, sizeof (path), "%s/tmp", mbox);
@ -359,10 +359,10 @@ maildir_clean_tmp (const char *mbox)
while ((entry = readdir (dirp)))
{
snprintf (path, sizeof (path), "%s/tmp/%s", mbox, entry->d_name);
if (stat (path, &info))
if (stat (path, &st))
fprintf (stderr, "maildir_clean_tmp: stat: %s: %s (errno %d)\n",
path, strerror (errno), errno);
else if (S_ISREG (info.st_mode) && now - info.st_ctime >= _24_HOURS)
else if (S_ISREG (st.st_mode) && now - st.st_ctime >= _24_HOURS)
{
/* this should happen infrequently enough that it won't be
* bothersome to the user to display when it occurs.

218
main.c
View File

@ -18,29 +18,61 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#include "isync.h"
#if HAVE_GETOPT_LONG
#define _GNU_SOURCE
#include <getopt.h>
int Quiet;
void
info (const char *msg, ...)
{
va_list va;
if (!Quiet)
{
va_start (va, msg);
vprintf (msg, va);
va_end (va);
}
}
void
infoc (char c)
{
if (!Quiet)
putchar (c);
}
struct option Opts[] = {
{"all", 0, NULL, 'a'},
{"list", 0, NULL, 'l'},
{"config", 1, NULL, 'c'},
{"create", 0, NULL, 'C'},
{"create-local", 0, NULL, 'L'},
{"create-remote", 0, NULL, 'R'},
{"delete", 0, NULL, 'd'},
{"expunge", 0, NULL, 'e'},
{"fast", 0, NULL, 'f'},
{"help", 0, NULL, 'h'},
{"remote", 1, NULL, 'r'},
{"folder", 1, NULL, 'F'},
{"maildir", 1, NULL, 'M'},
{"one-to-one", 0, NULL, '1'},
{"inbox", 1, NULL, 'I'},
{"host", 1, NULL, 's'},
{"port", 1, NULL, 'p'},
{"quiet", 0, NULL, 'q'},
@ -59,36 +91,49 @@ int Verbose = 0;
static void
version (void)
{
printf ("%s %s\n", PACKAGE, VERSION);
puts (PACKAGE " " VERSION);
exit (0);
}
static void
usage (void)
usage (int code)
{
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");
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:");
fputs (
PACKAGE " " VERSION " IMAP4 to maildir synchronizer\n"
"Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org>\n"
"usage:\n"
" " PACKAGE " [ flags ] mailbox [mailbox ...]\n"
" " PACKAGE " [ flags ] -a\n"
" " PACKAGE " [ flags ] -l\n"
" -a, --all synchronize all defined mailboxes\n"
" -l, --list list all defined mailboxes and exit\n"
" -L, --create-local create local maildir mailbox if nonexistent\n"
" -R, --create-remote create remote imap mailbox if nonexistent\n"
" -C, --create create both local and remote mailboxes if nonexistent\n"
" -d, --delete delete local msgs that don't exist on the server\n"
" -e, --expunge expunge deleted messages from the server\n"
" -f, --fast only fetch new messages\n"
" -r, --remote BOX remote mailbox\n"
" -F, --folder DIR remote IMAP folder containing mailboxes\n"
" -M, --maildir DIR local directory containing mailboxes\n"
" -1, --one-to-one map every IMAP <folder>/box to <maildir>/box\n"
" -I, --inbox BOX map IMAP INBOX to <maildir>/BOX (exception to -1)\n"
" -s, --host HOST IMAP server address\n"
" -p, --port PORT server IMAP port\n"
" -u, --user USER IMAP user name\n"
" -c, --config CONFIG read an alternate config file (default: ~/.isyncrc)\n"
" -V, --verbose verbose mode (display network traffic)\n"
" -q, --quiet don't display progress info\n"
" -v, --version display version\n"
" -h, --help display this help message\n"
"Compile time options:\n"
#if HAVE_LIBSSL
puts (" +HAVE_LIBSSL");
" +HAVE_LIBSSL\n"
#else
puts (" -HAVE_LIBSSL");
" -HAVE_LIBSSL\n"
#endif
exit (0);
, code ? stderr : stdout);
exit (code);
}
char *
@ -141,32 +186,33 @@ main (int argc, char **argv)
int delete = 0;
char *config = 0;
struct passwd *pw;
int quiet = 0;
int all = 0;
int create = 0;
int list = 0;
int o2o = 0;
int mbox_open_mode = 0;
int imap_create = 0;
pw = getpwuid (getuid ());
/* defaults */
memset (&global, 0, sizeof (global));
/* XXX the precedence is borked:
it's defaults < cmdline < file instead of defaults < file < cmdline */
global.port = 143;
global.box = "INBOX";
global.folder = "";
global.user = strdup (pw->pw_name);
global.maildir = strdup (pw->pw_dir);
global.max_size = 0;
global.max_messages = 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 = 0;
global.use_sslv3 = 0;
global.use_tlsv1 = 1;
#endif
#define FLAGS "aCc:defhp:qu:r:s:vV"
#define FLAGS "alCLRc:defhp:qu:r:F:M:1I:s:vV"
#if HAVE_GETOPT_LONG
while ((i = getopt_long (argc, argv, FLAGS, Opts, NULL)) != -1)
@ -176,11 +222,24 @@ main (int argc, char **argv)
{
switch (i)
{
case 'l':
list = 1;
/* plopp */
case 'a':
all = 1;
break;
case '1':
o2o = 1;
break;
case 'C':
create = 1;
mbox_open_mode |= OPEN_CREATE;
imap_create = 1;
break;
case 'L':
mbox_open_mode |= OPEN_CREATE;
break;
case 'R':
imap_create = 1;
break;
case 'c':
config = optarg;
@ -192,18 +251,29 @@ main (int argc, char **argv)
expunge = 1;
break;
case 'f':
mbox_open_mode |= OPEN_FAST;
fast = 1;
break;
case 'p':
global.port = atoi (optarg);
break;
case 'q':
quiet = 1;
Quiet = 1;
Verbose = 0;
break;
case 'r':
global.box = optarg;
break;
case 'F':
global.folder = optarg;
break;
case 'M':
free (global.maildir);
global.maildir = strdup (optarg);
break;
case 'I':
global.inbox = optarg;
break;
case 's':
#if HAVE_LIBSSL
if (!strncasecmp ("imaps:", optarg, 6))
@ -223,26 +293,70 @@ main (int argc, char **argv)
break;
case 'v':
version ();
case 'h':
usage (0);
default:
usage ();
usage (1);
}
}
if (!argv[optind] && !all)
{
puts ("No mailbox specified");
usage ();
fprintf (stderr, "No mailbox specified");
usage (1);
}
gethostname (Hostname, sizeof (Hostname));
load_config (config);
load_config (config, &o2o);
if (all && o2o)
{
DIR *dir;
struct dirent *de;
if (global.inbox) {
boxes = malloc (sizeof (config_t));
memcpy (boxes, &global, sizeof (config_t));
boxes->box = "INBOX";
boxes->path = global.inbox;
}
if (!(dir = opendir (global.maildir))) {
fprintf (stderr, "%s: %s\n", global.maildir, strerror(errno));
return 1;
}
while ((de = readdir (dir))) {
if (*de->d_name == '.')
continue;
if (global.inbox && !strcmp (global.inbox, de->d_name))
continue;
box = malloc (sizeof (config_t));
memcpy (box, &global, sizeof (config_t));
box->path = strdup (de->d_name);
box->box = box->path;
box->next = boxes;
boxes = box;
}
closedir (dir);
imap = imap_connect (&global);
if (!imap)
goto bork;
if (imap_list (imap))
goto bork;
}
if (list)
{
for (box = boxes; box; box = box->next)
puts (box->path);
exit (0);
}
for (box = boxes; (all && box) || (!all && argv[optind]); optind++)
{
if (!all)
{
if (NULL == (box = find_box (argv[optind])))
if (o2o || NULL == (box = find_box (argv[optind])))
{
/* if enough info is given on the command line, don't worry if
* the mailbox isn't defined.
@ -257,25 +371,23 @@ main (int argc, char **argv)
}
global.path = argv[optind];
box = &global;
if (o2o)
global.box =
(global.inbox && !strcmp (global.path, global.inbox)) ?
"INBOX" : global.path;
}
}
do {
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);
info ("Mailbox %s\n", box->path);
mail = maildir_open (box->path, mbox_open_mode);
if (!mail)
{
fprintf (stderr, "%s: unable to open mailbox\n", box->path);
break;
}
imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap, 0);
imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap, imap_create);
if (!imap)
{
fprintf (stderr, "%s: skipping mailbox due to IMAP error\n",
@ -283,12 +395,8 @@ main (int argc, char **argv)
break;
}
if (!quiet)
puts ("Synchronizing");
i = 0;
if (quiet)
i |= SYNC_QUIET;
i |= (delete || box->delete) ? SYNC_DELETE : 0;
info ("Synchronizing\n");
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))
{
@ -305,17 +413,14 @@ main (int argc, char **argv)
(imap->deleted || mail->deleted))
{
/* remove messages marked for deletion */
if (!quiet)
printf ("Expunging %d messages from server\n",
imap->deleted);
info ("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",
info ("Expunging %d messages from local mailbox\n",
mail->deleted);
if (maildir_expunge (mail, 0))
break;
@ -344,6 +449,7 @@ main (int argc, char **argv)
/* gracefully close connection to the IMAP server */
imap_close (imap);
bork:
free_config ();
#if DEBUG

42
sync.c
View File

@ -119,14 +119,11 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
continue;
}
if ((flags & SYNC_QUIET) == 0)
{
if (!upload)
fputs ("Uploading messages", stdout);
fputc ('.', stdout);
info ("Uploading messages");
infoc ('.');
fflush (stdout);
upload++;
}
/* upload the message if its not too big */
snprintf (path, sizeof (path), "%s/%s/%s", mbox->path,
@ -139,16 +136,14 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
if (imap->box->max_size > 0
&& sb.st_size > imap->box->max_size)
{
if ((flags & SYNC_QUIET) == 0)
printf
("Warning, local message is too large (%lu), skipping...\n",
info ("Warning, local message is too large (%lu), skipping...\n",
(unsigned long) sb.st_size);
continue;
}
fd = open (path, O_RDONLY);
if (fd == -1)
{
printf ("Error, unable to open %s: %s (errno %d)\n",
fprintf (stderr, "Error, unable to open %s: %s (errno %d)\n",
path, strerror (errno), errno);
continue;
}
@ -156,8 +151,11 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
cur->size = sb.st_size;
cur->uid = imap_append_message (imap, fd, cur);
/* if the server gave us back a uid, update the db */
if (cur->uid != (unsigned int) -1)
if (cur->uid != (unsigned int) -1) {
set_uid (mbox->db, cur->file, cur->uid);
if (!cur->uid)
printf("warning: no uid for new messge %s\n", cur->file);
}
close (fd);
}
@ -174,9 +172,8 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
/* if the user doesn't want local msgs deleted when they don't
* exist on the server, warn that such messages exist.
*/
else if ((flags & SYNC_QUIET) == 0)
printf ("Warning, uid %u doesn't exist on server\n",
cur->uid);
else
info ("Warning, uid %u doesn't exist on server\n", cur->uid);
continue;
}
tmp->processed = 1;
@ -255,13 +252,10 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
}
if (upload)
fprintf (stdout, " %d messages.\n", upload);
info (" %d messages.\n", upload);
if ((flags & SYNC_QUIET) == 0)
{
fputs ("Fetching new messages", stdout);
info ("Fetching new messages");
fflush (stdout);
}
if (max_msgs == 0)
max_msgs = UINT_MAX;
@ -308,9 +302,7 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
if (max_size && cur->size > max_size)
{
if ((flags & SYNC_QUIET) == 0)
printf
("Warning, message skipped because it is too big (%u)\n",
info ("Warning, message skipped because it is too big (%u)\n",
cur->size);
continue;
}
@ -348,12 +340,9 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
if (fd < 0)
continue;
if ((flags & SYNC_QUIET) == 0)
{
/* give some visual feedback that something is happening */
fputs (".", stdout);
infoc ('.');
fflush (stdout);
}
fetched++;
ret = imap_fetch_message (imap, cur->uid, fd);
@ -389,8 +378,7 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
}
}
if ((flags & SYNC_QUIET) == 0)
printf (" %d messages\n", fetched);
info (" %d messages\n", fetched);
return 0;
}