Browse Source

The Big Rewrite. too many change to list them all.

as opposed to earlier threats, BerkDB was not entirely dropped; i
suppose the isync 0.7 -> 0.8 change had a reason, so i added an
alternative UID storage scheme.
note that BDB 4.0 is not sufficient, as the db->open function changed in
an incompatible way ...

i updated the debian packaging except for a changelog entry.
note that i removed the upgrade blurb, as upstream now has a smooth
upgrade path down to at least isync 0.4.
wip/ssl-fprint
Oswald Buddenhagen 21 years ago
parent
commit
130664b622
  1. 4
      .cvsignore
  2. 7
      Makefile.am
  3. 24
      NEWS
  4. 69
      README
  5. 62
      TODO
  6. 43
      configure.in
  7. 16
      debian/NEWS
  8. 22
      debian/README.Debian
  9. 8
      debian/config
  10. 27
      debian/control
  11. 1
      debian/patches/00list
  12. 146
      debian/patches/20-cleanup.dpatch
  13. 458
      debian/patches/30-async-imap.dpatch
  14. 1
      debian/po/POTFILES.in
  15. 74
      debian/po/fr.po
  16. 60
      debian/po/templates.pot
  17. 19
      debian/preinst
  18. 3
      debian/rules
  19. 21
      debian/templates
  20. 17
      isync.spec.in
  21. 3
      src/.cvsignore
  22. 21
      src/Makefile.am
  23. 4
      src/compat/.cvsignore
  24. 8
      src/compat/Makefile.am
  25. 443
      src/compat/config.c
  26. 259
      src/compat/convert.c
  27. 192
      src/compat/isync.1
  28. 100
      src/compat/isync.h
  29. 0
      src/compat/isyncrc.sample
  30. 431
      src/compat/main.c
  31. 156
      src/compat/util.c
  32. 566
      src/config.c
  33. 89
      src/cram.c
  34. 102
      src/dotlock.c
  35. 25
      src/dotlock.h
  36. 1817
      src/drv_imap.c
  37. 1153
      src/drv_maildir.c
  38. 1413
      src/imap.c
  39. 399
      src/isync.h
  40. 179
      src/list.c
  41. 459
      src/maildir.c
  42. 863
      src/main.c
  43. 471
      src/mbsync.1
  44. 82
      src/mbsyncrc.sample
  45. 51
      src/mdconvert.1
  46. 256
      src/mdconvert.c
  47. 1238
      src/sync.c
  48. 389
      src/util.c

4
.cvsignore

@ -7,12 +7,16 @@ build-stamp
config.h
config.h.in
config.cache
config.guess
config.log
config.status
config.sub
configure
configure.lineno
configure-stamp
isync.spec
isync-*.tar.gz
patch-stamp
stamp-h
stamp-h.in
stamp-h1

7
Makefile.am

@ -1,6 +1,6 @@
SUBDIRS = src
man_MANS = isync.1
EXTRA_DIST = debian isyncrc.sample isync.spec $(man_MANS)
bin_SCRIPTS = get-cert
EXTRA_DIST = debian isync.spec $(bin_SCRIPTS)
log:
@perl -p -e "s/^(\\S+)\\s+(\\S.+\\S)\\s+(\\S+)\\s*\$$/\$$1:'\$$2 <\$$3>'\\n/" < ../CVSROOT/accounts > .usermap
@ -16,6 +16,9 @@ deb-clean:
distdir distclean: deb-clean
dist-hook:
find $(distdir)/debian \( -name CVS -o -name .cvsignore -o -name .#\*# -o -type l \) -print0 | xargs -0r rm -rf
rpm:
make dist
cp $(PACKAGE)-$(VERSION).tar.gz /usr/src/rpm/SOURCES

24
NEWS

@ -1,3 +1,27 @@
[1.0.0]
Essentially a rewrite. Synchronization state storage concept, configuration
and command line changed entirely.
But you needn't to worry about the upgrade, as a fully automated migration
path is provided, even for users of isync 0.7 and below.
Still, you should re-read the manual to be able to take full advantage of the
new features:
The supported mailbox types can be freely paired.
A possible application of this is using a local IMAP server to access
mailboxes that are not natively supported yet.
Message deletions (expunges) are now propagated both ways, so there is no need
for using mutt with maildir_trash any more.
Additional trash options added.
`OneToOne' replaced by something more flexible.
Partial support for IMAP pipelining (streaming, parallelization) added.
Makes flag change propagation much faster - this affects every message that
becomes Seen/Read.
[0.9]
Added Tunnel directive to allow the user to specify a shell command to run

69
README

@ -4,52 +4,69 @@
| \__ \ |_| | | | | (__
|_|___/\__, |_| |_|\___|
|___/
isync - IMAP4 to maildir mailbox synchronization program
isync/mbsync - free (GPL) mailbox synchronization program
http://isync.sf.net/
Author: Michael Elkins <me@mutt.org>
Current maintainer: Oswald Buddenhagen <ossi@users.sf.net>
See AUTHORS for contact information.
``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.
``mbsync'' is a command line application which synchronizes mailboxes;
currently Maildir and IMAP4 mailboxes are supported. New messages, message
deletions and flag changes can be propagated both ways.
``mbsync'' is suitable for use in IMAP-disconnected mode.
* Features:
Synchronization is based on unique message identifiers (UIDs), so no
identification conflicts can occur (as opposed to some other mail
synchronizers).
Synchronization state is kept in one local text file per mailbox pair;
multiple replicas of a mailbox can be maintained.
* Fast mode for fetching new mail only
* Supports imaps: (port 993) TLS/SSL connections
* Supports STARTTLS (RFC2595) for confidentiality
* Supports NAMESPACE (RFC2342)
* Supports CRAM-MD5 (RFC2095) for authentication
isync is the project name, while mbsync is the current executable name; this
change was necessary because of massive changes in the user interface. An
isync executable still exists; it is a compatibility wrapper around mbsync.
* Features
* Fine-grained selection of synchronization operations to perform
* Synchronizes single mailboxes or entire mailbox collections
* Partial mirrors possible: keep only the latest messages locally
* Trash functionality: backup messages before removing them
* IMAP features:
* Supports TLS/SSL via imaps: (port 993) and STARTTLS (RFC2595)
* Supports CRAM-MD5 (RFC2195) for authentication
* Supports NAMESPACE (RFC2342) for simplified configuration
* Pipelining for maximum speed (currently only partially implemented)
* Compatibility
``isync'' has been tested with the following IMAP servers:
isync should work fairly well with any IMAP4 compliant server;
particularily efficient with those that support the UIDPLUS and LITERAL+
extensions.
* Microsoft Exchange 2000 IMAP4rev1 server version 6.0.4417.0
* Courier-IMAP 1.2.3
* WU-IMAP 2000
* Domino IMAP4 Server Release 5.0.8
Courier 1.4.3 is known to be buggy, version 1.7.3 works fine.
* Platforms
c-client (UW-IMAP, Pine) is mostly fine, but tends to change UIDVALIDITY
pretty often when used with unix/mbox mailboxes, making isync refuse
synchronization.
The "cure" is to simply copy the new UIDVALIDITY from the affected
mailbox to mbsync's state file. This is a Bad Hack (TM), but it works -
use at your own risk (if the UIDVALIDITY change was genuine, this will
delete all messages in the affected mailbox - not that this ever
happened to me).
``isync'' has successfully been compiled on:
* Platforms
* Linux
* Solaris 2.7
* OpenBSD 2.8
* FreeBSD 4.3
At some point, ``isync'' has successfully run on:
Linux, Solaris 2.7, OpenBSD 2.8, FreeBSD 4.3, Cygwin
* Requirements
OpenSSL for TLS/SSL support (optional)
* INSTALLING
* Installation
./configure
make install
* HELP
* Help
Please see the man page for complete documentation.

62
TODO

@ -1,39 +1,51 @@
change of UIDVALIDITY shouldn't be considered fatal for the imap connection.
maybe the error handling needs to be cleaned up in general.
make SSL certificate validation more automatic.
don't require maildir_trash. currently MaxMessages gets into the way of
simply removing it; that is fixable with some shuffling, though.
add asynchronous operation to remote mailbox drivers. this is actually
what prevents us from simply using c-client and thus becoming mailsync.
refactor mailbox support. create proper mailbox drivers; handle imap and
maildir (and anything else) symmetrically; decouple UID->message mapping
from sync database - should use the same UID storing schemes as c-client
(pine, uw-imap) does, at least optionally, i think.
handle custom flags (keywords).
add asynchrounous operation to remote mailbox drivers. this is actually
what prevents us from simply using c-client for the previous point and
thus simply becoming mailsync.
fix maildir_{open_store,list} to handle partial names (last char not slash).
store message flags in sync database, so _un_setting them will be properly
synced as well.
add a way to automatically create and sync subfolders.
handle custom imap flags. currently, isync just fails horribly if it
encounters some.
could store TUID even when UIDPLUS is supported. would avoid duplicated
messages after abort before new UID arrives.
add options for fine-grained control of syncing operations (--new, --delete &
--flags) and direction (--push & --pull).
decouple TUID search from append. that's a prerequisite for usable
MULTIAPPEND, and is generally good for async. should be way faster, too,
as it saves repeated mailbox rescans with single-file formats.
add support for syncing with other: and shared: via NAMESPACE
use MULTIAPPEND and FETCH with multiple messages.
isync ignores asynchronous notifications (untagged responses), so mail
arriving during a fetch will not be fetched in the current run any more.
create dummies describing MIME structure of messages bigger than MaxSize.
flagging the dummy would fetch the real message. possibly remove --renew.
add a way to automatically create and sync IMAP subfolders.
don't SELECT boxes unless really needed; in particular not for appending,
and in write-only mode not before changes are made.
make the command line take precedence over the config file.
possibly request message attributes on a per-message basis from the drivers.
considerations:
- record non-existing UID ranges in the sync database, so IMAP FETCHes needn't
to exclude anyway non-existing messages explicitly.
- when detect unborn pairs and orphaned messages being gone? implied by expunge:
with trashing, by local driver, or of messages we deleted in this run. the
remaining cases could be handled by automatic periodical cleanup passes, an
explicit --cleanup action, or be implied by one of the other actions.
- the benefit of this is questionable, as fine-grained requests will result
in sending huge amounts of data, and upstream is often way slower than
downstream.
possibly timestamp mails with remote arrival date.
maildir: possibly timestamp mails with remote arrival date.
possibly recover from UIDVALIDITY change by resyncing according to message
IDs - this is a pretty common condition with uw-imap.
maybe throw out the ctx->recent stuff - it's used only for one info message.
possibly use ^[[1m to highlight error messages.
consider alternative trash implementation: trash only messages we delete,
and trash before marking them deleted in the mailbox. downside: all other
programs have to do the same. and what if the deleted flag is unset?
items out of scope of purely UID based approach:
- detect message moves between folders
- recovering from UIDVALIDITY change (uw-imap does this a lot)

43
configure.in

@ -1,22 +1,23 @@
AC_INIT(src/isync.h)
AM_CONFIG_HEADER(config.h)
AM_INIT_AUTOMAKE(isync, 0.9.2)
AM_INIT_AUTOMAKE(isync, 1.0.0alpha)
AM_MAINTAINER_MODE
AM_PROG_CC_STDC
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -pipe -W -Wall -Wshadow -Wmissing-prototypes"
CFLAGS="$CFLAGS -pipe -W -Wall -Wshadow -Wstrict-prototypes"
fi
AC_CHECK_FUNCS(getopt_long)
AC_CHECK_FUNCS(vasprintf)
AC_CHECK_LIB(socket, socket)
AC_CHECK_LIB(nsl, inet_ntoa)
AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
AC_CHECK_LIB(nsl, inet_ntoa, [SOCK_LIBS="$SOCK_LIBS -lnsl"])
AC_SUBST(SOCK_LIBS)
ssl=false
AC_ARG_WITH(ssl,
[ --with-ssl=DIR yes/no/OpenSSL installation root [detect]],
AS_HELP_STRING([--with-ssl=DIR], [yes/no/OpenSSL installation root [detect]]),
[ob_cv_with_ssl=$withval])
if test "x$ob_cv_with_ssl" != xno; then
if test -d "$ob_cv_with_ssl/lib"; then
@ -25,7 +26,7 @@ if test "x$ob_cv_with_ssl" != xno; then
fi
AC_CHECK_LIB(crypto, ERR_error_string, [cryptolib=" -lcrypto"])
AC_CHECK_LIB(ssl, SSL_library_init, [
LIBS="-lssl$cryptolib $LIBS"
SSL_LIBS="-lssl$cryptolib"
AC_DEFINE(HAVE_LIBSSL, 1, [Define if you want SSL support])
ssl=true
],[
@ -34,19 +35,29 @@ if test "x$ob_cv_with_ssl" != xno; then
fi
])
fi
AC_SUBST(SSL_LIBS)
AC_CACHE_CHECK(for db_create in -ldb, ac_cv_db_db_create,
[ac_cv_db_dbcreate=no
AC_CACHE_CHECK([for Berkley DB 4.2], ac_cv_berkdb4,
[ac_cv_berkdb4=no
AC_TRY_LINK([#include <db.h>],
[db_create();],[ac_cv_db_db_create=yes])])
if test $ac_cv_db_db_create=yes; then
LIBS="$LIBS -ldb"
else
AC_MSG_ERROR([Berkley DB not found.
You must install libdb including the respective development files/headers.])
[DB *db;
db->truncate(db, 0, 0, 0);
db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 0)],
[ac_cv_berkdb4=yes])])
if test "x$ac_cv_berkdb4" = xno; then
AC_MSG_ERROR([Berkley DB 4.2 not found.
You must install libdb4.2 including the respective development files/headers.])
fi
AC_ARG_ENABLE(compat,
AS_HELP_STRING([--disable-compat], [don't include isync compatibility wrapper [no]]),
[ob_cv_enable_compat=$enableval])
if test "x$ob_cv_enable_compat" != xno; then
AC_CHECK_FUNCS(getopt_long)
fi
AM_CONDITIONAL(with_compat, test "x$ob_cv_enable_compat" != xno)
AC_OUTPUT(Makefile src/Makefile isync.spec)
AC_OUTPUT(Makefile src/Makefile src/compat/Makefile isync.spec)
if $ssl; then
AC_MSG_RESULT([

16
debian/NEWS vendored

@ -1,16 +0,0 @@
isync (0.8-1) unstable; urgency=low
IMPORTANT upgrade note:
This version includes a change to the way the UID for each message is
stored in the local mailbox. You need to remove all the messages in your
local folder if you were previously using another version of isync or else
you will end up with duplicate messages on your IMAP server.
A suggested upgrade procedure is to use isync version 0.7 to synchronize
any local changes in isync-managed mailboxes with your IMAP server, and
then remove the contents of the local mailboxes, before upgrading to this
version. Then run isync again to pull down the mail again. You must do
this manually, the Debian package will not do this for you.
-- Joey Hess <joeyh@debian.org> Tue, 29 Oct 2002 13:50:40 -0500

22
debian/README.Debian vendored

@ -1,22 +0,0 @@
A note from isync's web site:
To use this command effectively, you need a mail client that sets the T
(trashed) flag when it deletes a message from a maildir mailbox, instead of
just removing it altogether. Currently, only Mutt 1.3.27 supports this. Without
such a client, isync will refetch the locally deleted messages from the server
since they will never get expunged. Be sure to put
set maildir_trash
in your ~/.muttrc when using Mutt.
isync can be integrated into Mutt fairly easily with a few hooks:
folder-hook ~A bind index $ <sync-mailbox>
folder-hook +maildir 'macro index $ "<sync-mailbox>!isync -e maildir\n"'
where maildir is the name of the local mailbox (or its alias). This works well
so long as you are not modifying the IMAP mailbox outside of Mutt. However, if
you are using another mail program simultaneously Mutt will have the wrong idea
of the local mailbox flags and messages will start disappearing from its index
display (don't worry, they are still on disk).

8
debian/config vendored

@ -1,8 +0,0 @@
#!/bin/sh
set -e
. /usr/share/debconf/confmodule
if [ "$1" = "configure" -a ! -z "$2" ] && \
dpkg --compare-versions "$2" lt 0.8; then
db_input critical isync/upgrade_0.8 || true
db_go || true
fi

27
debian/control vendored

@ -4,21 +4,26 @@ Priority: optional
Maintainer: Nicolas Boullis <nboullis@debian.org>
Uploaders: Nicolas Boullis <nboullis@debian.org>, Theodore Y. Ts'o <tytso@mit.edu>
Standards-Version: 3.6.1
Build-Depends: libssl-dev, debhelper (>= 4.1.16), dpkg-dev (>= 1.9.0), libdb4.0-dev, dpatch
Build-Depends: libssl-dev, debhelper (>= 4.1.16), dpkg-dev (>= 1.9.0), libdb4.2-dev, dpatch
Package: isync
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Suggests: mutt
Description: Synchronize a local maildir with a remote IMAP4 mailbox
A command line application which synchronizes a local maildir-style
mailbox with a remote IMAP4 mailbox, suitable for use in disconnected
mode. Multiple copies of the remote IMAP4 mailbox can be maintained,
and all flags and messages are synchronized.
Description: Synchronize Maildir and IMAP4 mailboxes
A command line application which synchronizes mailboxes; currently
Maildir and IMAP4 mailboxes are supported.
New messages, message deletions and flag changes can be propagated both ways.
It is useful for working in disconnected mode, such as on a laptop or with a
non-permanent internet collection (dIMAP).
.
Features:
* Fast mode for fetching new mail only
* Supports imaps: (port 993) TLS/SSL connections
* Supports STARTTLS (RFC2595) for confidentiality
* Supports NAMESPACE (RFC2342)
* Supports CRAM-MD5 (RFC2095) for authentication
* Fine-grained selection of synchronization operations to perform
* Synchronizes single mailboxes or entire mailbox collections
* Partial mirrors possible: keep only the latest messages locally
* Trash functionality: backup messages before removing them
* IMAP features:
* Supports TLS/SSL via imaps: (port 993) and STARTTLS (RFC2595)
* Supports CRAM-MD5 (RFC2195) for authentication
* Supports NAMESPACE (RFC2342) for simplified configuration
* Pipelining for maximum speed (currently only partially implemented)

1
debian/patches/00list vendored

@ -1 +0,0 @@
20-cleanup

146
debian/patches/20-cleanup.dpatch vendored

@ -1,146 +0,0 @@
#! /bin/sh -e
## 20-cleanup.dpatch by Theodore Ts'o <tytso@mit.edu>
##
## DP: Make sure the database store and the imap database is closed
## DP: if isync is aborted.
[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts
patch_opts="${patch_opts:--f --no-backup-if-mismatch}"
if [ $# -ne 1 ]; then
echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
exit 1
fi
case "$1" in
-patch) patch $patch_opts -p1 < $0;;
-unpatch) patch $patch_opts -p1 -R < $0;;
*)
echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
exit 1;;
esac
exit 0
@DPATCH@
Problem description:
>> If isync dies in the middle of synchronization, or the network
>> connection breaks while it is synchronizing a mailbox, new messages
>> which are downloaded from the IMAP server do not have their UID saved
>> to the maildir directory. This is REALLY, REALLY BAD, because it
>> means that on the next isync, the downloaded messages are re-uploaded
>> to the imap server, resulting in duplicate messages in the IMAP store.
>>
>> This takes means the network download takes longer, and if the network
>> connection is unrealible, it means it's more likely the the IMAP
>> connection will break, resulting in more duplicate messages being
>> uploaded to the servers. (The first time, 14 messages were uploaded
>> to the server. The second time I re-isynced, 65 messages were
>> uploaded to the server, resulting in some 79 duplicate messages that I
>> had to manually weed out. Grr, grr, grr, grr.)
Problem solution:
Actually, I managed to figure out the solution a while ago, and got
hung up trying to figure out the right way to submit the patches back
to upstream (there's no mailing list that I can find; so do you just
communicate directly with the developers). Anyway, I got busy and I
never had a chance to send the patches a while ago.
This patch is not the best, but it does seem to work. Perhaps a
better approach would be to use the more advanced API's available with
berkdb, so you can actually force a sync to the db/dbm files after
the mail message has been downloaded. Fundamentally, that's the
problem. The id has been added to the db file, but the changes don't
get forced out to disk, so in the case of an abnormal termination of
the program, the id's never get written to disk.
The patch enclosed below solves the problem by establishing a signal
handler, which cleans up in the case of the user typing ^C (after the
network connection has gone away, say because your GSM phone's GPRS
connection has gotten flakey, for example). However, it doesn't solve
the problem in case of an abrupt system crash. In order to address
that problem, the overall program interfaces would have to be changed
to use the newer berkdb interfaces directly, but that would mean
dropping compatibility with the ancient dbm interface. Personally, I
don't think that to be any great loss, but the changes would be much
more invasive, and would require agreement with the upstream
maintainer that this is the right way to go.
Also, for bonus points, perhaps there should be an inactivity timer so
that isync can automatically figure out when the network connection
has gone away, and can do a clean shutdown and exit automatically,
instead of requiring the user to type ^C.
- Ted
Patched files: src/main.c
===================================================================
RCS file: isync-0.9.2/src/RCS/main.c,v
retrieving revision 1.3
diff -u -r1.3 isync-0.9.2/src/main.c
--- isync-0.9.2/src/main.c 2004/01/10 01:13:38 1.3
+++ isync-0.9.2/src/main.c 2004/01/10 01:14:34
@@ -35,6 +35,7 @@
#include <string.h>
#include <ctype.h>
#include <dirent.h>
+#include <signal.h>
int Quiet;
@@ -92,6 +93,22 @@
unsigned int Tag = 0;
char Hostname[256];
int Verbose = 0;
+mailbox_t *CleanupMail = 0;
+imap_t *CleanupImap = 0;
+int CleanupValid = 0;
+
+static void signal_exit(int sig)
+{
+ info("Abort received\n");
+ if (CleanupValid) {
+ info("Aborting, cleaning up\n");
+ if (CleanupMail)
+ maildir_close (CleanupMail);
+ if (CleanupImap)
+ imap_close (CleanupImap);
+ }
+ exit (1);
+}
static void
version (void)
@@ -319,6 +336,10 @@
usage (1);
}
+ signal(SIGTERM, signal_exit);
+ signal(SIGHUP, signal_exit);
+ signal(SIGINT, signal_exit);
+
gethostname (Hostname, sizeof (Hostname));
load_config (config, &o2o);
@@ -410,6 +431,9 @@
ret = 1;
break;
}
+ CleanupValid = 1;
+ CleanupMail = mail;
+ CleanupImap = imap;
info ("Synchronizing\n");
i = (delete || box->delete) ? SYNC_DELETE : 0;
@@ -460,6 +484,8 @@
} while (0);
+ CleanupValid = 0;
+
/* we never sync the same mailbox twice, so close it now */
if (mail)
maildir_close (mail);

458
debian/patches/30-async-imap.dpatch vendored

@ -1,458 +0,0 @@
#!/bin/sh -e
## 30-aysnc-imap.dpatch by Theodore Y. Ts'o <tytso@mit.edu>
##
## DP: Add the beginnings of asynchronous IMAP support. So far, we only
## DP: support asynchronous flag setting, since that's the easist.
## DP: Eventually we need to support asynchronous message fetches and
## DP: uploads.
if [ $# -ne 1 ]; then
echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
exit 1
fi
[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts
patch_opts="${patch_opts:--f --no-backup-if-mismatch}"
case "$1" in
-patch) patch $patch_opts -p1 < $0;;
-unpatch) patch $patch_opts -p1 -R < $0;;
*)
echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
exit 1;;
esac
exit 0
@DPATCH@
diff -urNad /usr/projects/isync/SF-cvs/isync/src/imap.c isync/src/imap.c
--- /usr/projects/isync/SF-cvs/isync/src/imap.c 2004-01-15 14:24:40.000000000 -0500
+++ isync/src/imap.c 2004-01-15 20:36:15.000000000 -0500
@@ -3,6 +3,7 @@
* isync - IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2003 Oswald Buddenhagen <ossi@users.sf.net>
+ * Copyright (C) 2004 Theodore Ts'o <tytso@alum.mit.edu>
*
* 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
@@ -35,13 +36,33 @@
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
+#include <sys/ioctl.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#if HAVE_LIBSSL
# include <openssl/err.h>
#endif
+struct imap_cmd {
+ unsigned int tag;
+ char *cmd;
+ int flags;
+ int response;
+ struct imap_cmd *next;
+ int (*complete_fn) (imap_t *imap, struct imap_cmd * cmd);
+};
+
+#define IMAP_FLAG_DONE 0x0001
+
+static struct imap_cmd *in_progress = NULL;
+static int num_in_progress = 0;
+int max_in_progress_high = 50;
+int max_in_progress_low = 10;
+
+static struct imap_cmd *get_cmd_result(imap_t *imap);
+
const char *Flags[] = {
"\\Seen",
"\\Answered",
@@ -199,6 +220,22 @@
return write (sock->fd, buf, len);
}
+static int
+socket_pending(Socket_t *sock)
+{
+ int num = -1;
+
+ if (ioctl(sock->fd, FIONREAD, &num) < 0)
+ return -1;
+ if (num > 0)
+ return num;
+#if HAVE_LIBSSL
+ if (sock->use_ssl)
+ return SSL_pending (sock->ssl);
+#endif
+ return 0;
+}
+
static void
socket_perror (const char *func, Socket_t *sock, int ret)
{
@@ -301,16 +338,20 @@
}
static int
-parse_fetch (imap_t * imap, list_t * list)
+parse_fetch (imap_t * imap, char *cmd)
{
- list_t *tmp;
+ list_t *tmp, *list;
unsigned int uid = 0;
unsigned int mask = 0;
unsigned int size = 0;
message_t *cur;
- if (!is_list (list))
+ list = parse_list (cmd, 0);
+
+ if (!is_list (list)) {
+ free_list(list);
return -1;
+ }
for (tmp = list->child; tmp; tmp = tmp->next)
{
@@ -325,6 +366,7 @@
if (uid < imap->minuid)
{
/* already saw this message */
+ free_list(list);
return 0;
}
else if (uid > imap->maxuid)
@@ -387,6 +429,7 @@
cur->flags = mask;
cur->size = size;
+ free_list(list);
return 0;
}
@@ -415,39 +458,121 @@
}
}
-static int
-imap_exec (imap_t * imap, const char *fmt, ...)
+static void print_imap_command(const char *cmd, FILE *f)
+{
+ if (strncmp(cmd, "LOGIN", 5))
+ fputs(cmd, f);
+ else
+ fputs("LOGIN USERNAME PASSWORD", f);
+}
+
+static struct imap_cmd *issue_imap_cmd(imap_t *imap,
+ const char *fmt, ...)
{
va_list ap;
- char tmp[256];
- char buf[256];
- char *cmd;
- char *arg;
- char *arg1;
- config_t *box;
+ char tmp[1024];
+ char buf[1024];
+ struct imap_cmd *cmd;
int n;
+ cmd = malloc(sizeof(struct imap_cmd));
+ if (!cmd)
+ return NULL;
+
+ cmd->tag = ++Tag;
+ cmd->flags = 0;
+ cmd->response = 0;
+ cmd->complete_fn = 0;
+
va_start (ap, fmt);
vsnprintf (tmp, sizeof (tmp), fmt, ap);
va_end (ap);
- snprintf (buf, sizeof (buf), "%d %s\r\n", ++Tag, tmp);
+ cmd->cmd = malloc(strlen(tmp)+1);
+ if (cmd->cmd)
+ strcpy(cmd->cmd, tmp);
+
+ snprintf (buf, sizeof (buf), "%d %s\r\n", cmd->tag, tmp);
if (Verbose) {
- printf (">>> %s", buf);
+ if (num_in_progress)
+ printf("(%d in progress) ", num_in_progress);
+ printf(">>> %d ", cmd->tag);
+ print_imap_command(tmp, stdout);
+ fputc('\n', stdout);
fflush (stdout);
}
n = socket_write (imap->sock, buf, strlen (buf));
if (n <= 0)
{
socket_perror ("write", imap->sock, n);
- return -1;
+ free(cmd);
+ return NULL;
}
+ cmd->next = in_progress;
+ in_progress = cmd;
+ num_in_progress++;
+ if ((num_in_progress > max_in_progress_high) ||
+ socket_pending(imap->sock)) {
+ while ((num_in_progress > max_in_progress_low) ||
+ socket_pending(imap->sock)) {
+ if (Verbose && socket_pending(imap->sock))
+ printf("(Socket input pending)\n");
+ get_cmd_result(imap);
+ }
+ }
+ return cmd;
+}
+
+static struct imap_cmd *find_imap_cmd(unsigned int tag)
+{
+ struct imap_cmd *cmd, *prev;
+
+ for (prev=NULL, cmd=in_progress; cmd; cmd = cmd->next) {
+ if (tag == cmd->tag) {
+ return cmd;
+ }
+ prev = cmd;
+ }
+ return NULL;
+}
+
+static void dequeue_imap_cmd(unsigned int tag)
+{
+ struct imap_cmd *cmd, *prev;
+
+ for (prev=NULL, cmd=in_progress; cmd; cmd = cmd->next) {
+ if (tag != cmd->tag) {
+ prev = cmd;
+ continue;
+ }
+ if (prev)
+ prev->next = cmd->next;
+ else
+ in_progress = cmd->next;
+ cmd->next = 0;
+ if (cmd->cmd)
+ free(cmd->cmd);
+ cmd->cmd = 0;
+ free(cmd);
+ break;
+ }
+}
+
+static struct imap_cmd *get_cmd_result(imap_t *imap)
+{
+ char *cmd;
+ char *arg;
+ char *arg1;
+ config_t *box;
+ int n;
+ unsigned int tag;
+ struct imap_cmd *cmdp;
for (;;)
{
next:
if (buffer_gets (imap->buf, &cmd))
- return -1;
+ return NULL;
arg = next_arg (&cmd);
if (*arg == '*')
@@ -456,7 +581,7 @@
if (!arg)
{
fprintf (stderr, "IMAP error: unable to parse untagged response\n");
- return -1;
+ return NULL;
}
if (!strcmp ("NAMESPACE", arg))
@@ -528,23 +653,14 @@
imap->recent = atoi (arg);
else if (!strcmp ("FETCH", arg1))
{
- list_t *list;
-
- list = parse_list (cmd, 0);
-
- if (parse_fetch (imap, list))
- {
- free_list (list);
- return -1;
- }
-
- free_list (list);
+ if (parse_fetch (imap, cmd))
+ return NULL;
}
}
else
{
fprintf (stderr, "IMAP error: unable to parse untagged response\n");
- return -1;
+ return NULL;
}
}
#if HAVE_LIBSSL
@@ -555,7 +671,7 @@
if (!imap->cram)
{
fprintf (stderr, "IMAP error, not doing CRAM-MD5 authentication\n");
- return -1;
+ return NULL;
}
resp = cram (cmd, imap->box->user, imap->box->pass);
@@ -568,34 +684,94 @@
if (n <= 0)
{
socket_perror ("write", imap->sock, n);
- return -1;
+ return NULL;
}
n = socket_write (imap->sock, "\r\n", 2);
if (n <= 0)
{
socket_perror ("write", imap->sock, n);
- return -1;
+ return NULL;
}
imap->cram = 0;
}
#endif
- else if ((size_t) atol (arg) != Tag)
- {
- fprintf (stderr, "IMAP error: wrong tag\n");
- return -1;
- }
- else
- {
- arg = next_arg (&cmd);
- parse_response_code (imap, cmd);
- if (!strcmp ("OK", arg))
- return 0;
- return -1;
+ else {
+ tag = (unsigned int) atol (arg);
+ cmdp = find_imap_cmd(tag);
+ if (!cmdp) {
+ fprintf(stderr, "IMAP error: sent unknown tag: %u\n",
+ tag);
+ return NULL;
+ }
+ arg = next_arg (&cmd);
+ if (strncmp("OK", arg, 2)) {
+ if (cmdp->cmd) {
+ fputc('\'', stderr);
+ print_imap_command(cmdp->cmd, stderr);
+ fputc('\'', stderr);
+ } else
+ fprintf(stderr, "tag %u", tag);
+ fprintf(stderr, " returned an error (%s): %s\n",
+ arg, cmd ? cmd : "");
+ cmdp->response = -1;
+ }
+ parse_response_code (imap, cmd);
+ num_in_progress--;
+ cmdp->flags |= IMAP_FLAG_DONE;
+ if (Verbose)
+ printf("Tag %u completed with response %d\n",
+ cmdp->tag, cmdp->response);
+ return cmdp;
}
}
/* not reached */
}
+static void flush_imap_cmds(imap_t *imap)
+{
+ struct imap_cmd *cmdp;
+
+ while (num_in_progress) {
+ if (in_progress && in_progress->flags & IMAP_FLAG_DONE) {
+ dequeue_imap_cmd(in_progress->tag);
+ continue;
+ }
+ cmdp = get_cmd_result(imap);
+ if (!cmdp)
+ printf("Error trying to flush pending imap cmds\n");
+ }
+}
+
+static int
+imap_exec (imap_t * imap, const char *fmt, ...)
+{
+ va_list ap;
+ char tmp[1024];
+ struct imap_cmd *cmdp, *waitp;
+ int result;
+
+ va_start (ap, fmt);
+ vsnprintf (tmp, sizeof (tmp), fmt, ap);
+ va_end (ap);
+
+ cmdp = issue_imap_cmd(imap, "%s", tmp);
+ if (!cmdp)
+ return -1;
+
+ if (cmdp->flags & IMAP_FLAG_DONE)
+ return cmdp->response;
+
+ do {
+ waitp = get_cmd_result(imap);
+ } while (waitp->tag != cmdp->tag);
+
+ result = cmdp->response;
+ dequeue_imap_cmd(cmdp->tag);
+
+ return cmdp->response;
+
+}
+
#ifdef HAVE_LIBSSL
static int
start_tls (imap_t *imap, config_t * cfg)
@@ -1039,6 +1215,7 @@
size_t n;
char buf[1024];
+ flush_imap_cmds(imap);
send_server (imap->sock, "UID FETCH %d BODY.PEEK[]", uid);
for (;;)
@@ -1160,7 +1337,9 @@
(buf[0] != 0) ? " " : "", Flags[i]);
}
- return imap_exec (imap, "UID STORE %d +FLAGS.SILENT (%s)", uid, buf);
+ if (issue_imap_cmd(imap, "UID STORE %d +FLAGS.SILENT (%s)", uid, buf))
+ return 0;
+ return -1;
}
int
@@ -1249,6 +1428,7 @@
strcat (flagstr,") ");
}
+ flush_imap_cmds(imap);
send_server (imap->sock, "APPEND %s%s %s{%d}",
imap->prefix, imap->box->box, flagstr, len + extra);
@@ -1341,6 +1521,7 @@
}
/* didn't receive an APPENDUID */
+ flush_imap_cmds(imap);
send_server (imap->sock,
"UID SEARCH HEADER X-TUID %08lx%05lx%04x",
tv.tv_sec, tv.tv_usec, pid);

1
debian/po/POTFILES.in vendored

@ -1 +0,0 @@
[type: gettext/rfc822deb] templates

74
debian/po/fr.po vendored

@ -1,74 +0,0 @@
#
# Translators, if you are not familiar with the PO format, gettext
# documentation is worth reading, especially sections dedicated to
# this format, e.g. by running:
# info -n '(gettext)PO Files'
# info -n '(gettext)Header Entry'
#
# Some information specific to po-debconf are available at
# /usr/share/doc/po-debconf/README-trans
# or http://www.debian.org/intl/l10n/po-debconf/README-trans
#
# Developers do not need to manually edit POT or PO files.
#
msgid ""
msgstr ""
"Project-Id-Version: isync 0.9.1-1\n"
"POT-Creation-Date: 2003-10-14 21:55+0200\n"
"PO-Revision-Date: 2003-10-27 11:52+0100\n"
"Last-Translator: Christian Perrier <bubulle@debian.org>\n"
"Language-Team: French <debian-l10n-french@lists.debian.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
#. Description
#: ../templates:4
msgid "Abort isync upgrade"
msgstr "Interrompre la mise Ă  jour d'isync"
#. Description
#: ../templates:4
msgid ""
"You are upgrading from an older version of isync that stored the UID of each "
"message in a way that is not compatable with the new version. You need to "
"remove all the messages in local folders downloaded with the old version of "
"isync. Otherwise isync will get confused and upload duplicate messages to "
"the IMAP server."
msgstr ""
"Vous mettez isync à jour à partir d'une version qui utilise une méthode de "
"stockage des identifiants des messages incompatible avec la nouvelle "
"version. Il faut supprimer des répertoires locaux tous les messages "
"téléchargés avec l'ancienne version ; sinon, isync fonctionnera "
"incorrectement et enverra au serveur IMAP des doublons des messages."
#. Description
#: ../templates:4
msgid ""
"A suggested upgrade procedure is to use the isync version 0.7 to synchronize "
"any local changes in isync-managed mailboxes with your IMAP server (if there "
"are any local changes to synchronise), and then remove the contents of the "
"local mailboxes, before upgrading to version 0.8 or above. Then run isync "
"again to pull down the mail again. You must do this manually; the Debian "
"package will not do this for you."
msgstr ""
"La méthode suggérée pour la mise à jour est la suivante : en utilisant "
"la version 0.7 d'isync, synchronisez avec le serveur IMAP les Ă©ventuelles modifications locales "
"des boîtes aux lettres gérées par isync ; puis "
"supprimez le contenu des boîtes aux lettres locales. Ensuite, effectuez la "
"mise à jour vers une version supérieure ou égale à 0.8. Enfin, utilisez à "
"nouveau isync pour récupérer les courriels. Vous devez effectuer cette "
"opération vous-même : le paquet Debian ne la fera pas automatiquement."
#. Description
#: ../templates:4
msgid ""
"If you want, the upgrade of isync can be aborted to let you deal with this "
"issue. Or you can just suspend the upgrade or switch to a different virtual "
"console to take care of it. Do not continue past this point before manually "
"resolving this issue!"
msgstr ""
"Si vous le souhaitez, la mise Ă  jour d'isync peut ĂŞtre interrompue pour vous "
"permettre d'effectuer cette opération. Vous pouvez également basculer vers "
"une autre console virtuelle pour vous en occuper, puis reprendre la mise Ă  jour. Ne continuez pas "
"sans faire cette correction."

60
debian/po/templates.pot vendored

@ -1,60 +0,0 @@
#
# Translators, if you are not familiar with the PO format, gettext
# documentation is worth reading, especially sections dedicated to
# this format, e.g. by running:
# info -n '(gettext)PO Files'
# info -n '(gettext)Header Entry'
#
# Some information specific to po-debconf are available at
# /usr/share/doc/po-debconf/README-trans
# or http://www.debian.org/intl/l10n/po-debconf/README-trans
#
# Developers do not need to manually edit POT or PO files.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2003-10-14 21:55+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#. Description
#: ../templates:4
msgid "Abort isync upgrade"
msgstr ""
#. Description
#: ../templates:4
msgid ""
"You are upgrading from an older version of isync that stored the UID of each "
"message in a way that is not compatable with the new version. You need to "
"remove all the messages in local folders downloaded with the old version of "
"isync. Otherwise isync will get confused and upload duplicate messages to "
"the IMAP server."
msgstr ""
#. Description
#: ../templates:4
msgid ""
"A suggested upgrade procedure is to use the isync version 0.7 to synchronize "
"any local changes in isync-managed mailboxes with your IMAP server (if there "
"are any local changes to synchronise), and then remove the contents of the "
"local mailboxes, before upgrading to version 0.8 or above. Then run isync "
"again to pull down the mail again. You must do this manually; the Debian "
"package will not do this for you."
msgstr ""
#. Description
#: ../templates:4
msgid ""
"If you want, the upgrade of isync can be aborted to let you deal with this "
"issue. Or you can just suspend the upgrade or switch to a different virtual "
"console to take care of it. Do not continue past this point before manually "
"resolving this issue!"
msgstr ""

19
debian/preinst vendored

@ -1,19 +0,0 @@
#!/bin/sh
set -e
if [ "$1" = "upgrade" ] && dpkg --compare-versions "$2" lt "0.8"; then
# Do not do debconf stuff if debconf is not there.
# I don't want to have to pre-depend on debconf.
if [ -e /usr/share/debconf/confmodule ]; then
. /usr/share/debconf/confmodule
db_get isync/upgrade_0.8
if [ "$RET" = true ]; then
echo "Aborting isync upgrade at your request so you can manually resolve upgrade issue." >&2
exit 1
fi
else
echo "WARNING: Read NEWS.Debian file about manual upgrade issues from isync 0.7." >&2
fi
fi
#DEBHELPER#

3
debian/rules vendored

@ -41,9 +41,8 @@ binary-arch: build install
dh_testroot
dh_installchangelogs ChangeLog
dh_installdocs AUTHORS NEWS README TODO
dh_installexamples isyncrc.sample
dh_installexamples src/mbsyncrc.sample src/compat/isyncrc.sample
dh_installman
dh_installdebconf
dh_strip
dh_compress
dh_fixperms

21
debian/templates vendored

@ -1,21 +0,0 @@
Template: isync/upgrade_0.8
Type: boolean
Default: false
_Description: Abort isync upgrade
You are upgrading from an older version of isync that stored the UID of
each message in a way that is not compatable with the new version. You
need to remove all the messages in local folders downloaded with the old
version of isync. Otherwise isync will get confused and upload duplicate
messages to the IMAP server.
.
A suggested upgrade procedure is to use the isync version 0.7 to
synchronize any local changes in isync-managed mailboxes with your IMAP
server (if there are any local changes to synchronise), and then remove
the contents of the local mailboxes, before upgrading to version 0.8 or
above. Then run isync again to pull down the mail again. You must do this
manually; the Debian package will not do this for you.
.
If you want, the upgrade of isync can be aborted to let you deal with this
issue. Or you can just suspend the upgrade or switch to a different
virtual console to take care of it. Do not continue past this point before
manually resolving this issue!

17
isync.spec.in

@ -10,16 +10,16 @@ Packager: Oswald Buddenhagen <ossi@users.sf.net>
BuildRoot: /var/tmp/%{name}-buildroot
%description
isync is a command line utility for synchronizing a remote IMAP mailbox with a
local maildir-style mailbox. This is useful for working in disconnected mode,
such as on a laptop. Modifications made locally and remotely are synchronized
so that no message status flags are lost.
isync is a command line utility which synchronizes mailboxes; currently
Maildir and IMAP4 mailboxes are supported.
New messages, message deletions and flag changes can be propagated both ways.
It is useful for working in disconnected mode, such as on a laptop or with a
non-permanent internet collection (dIMAP).
%prep
%setup
%build
./configure --prefix=/usr
make RPM_OPT_FLAGS="$RPM_OPT_FLAGS"
%install
make DESTDIR=$RPM_BUILD_ROOT install
@ -28,6 +28,11 @@ make DESTDIR=$RPM_BUILD_ROOT install
rm -rf $RPM_BUILD_ROOT
%files
%doc AUTHORS COPYING README TODO ChangeLog isyncrc.sample
%doc AUTHORS COPYING NEWS README TODO ChangeLog src/mbsyncrc.sample src/compat/isyncrc.sample
/usr/bin/isync
/usr/bin/mbsync
/usr/bin/mdconvert
/usr/bin/get-cert
/usr/man/man1/isync.1.gz
/usr/man/man1/mbsync.1.gz
/usr/man/man1/mdconvert.1.gz

3
src/.cvsignore

@ -1,4 +1,5 @@
.deps
Makefile
Makefile.in
isync
mbsync
mdconvert

21
src/Makefile.am

@ -1,5 +1,16 @@
bin_PROGRAMS = isync
isync_SOURCES = main.c imap.c sync.c maildir.c list.c cram.c config.c dotlock.c
noinst_HEADERS = isync.h dotlock.h
INCLUDES=$(RPM_OPT_FLAGS)
DISTCLEANFILES = *~
if with_compat
compat_dir = compat
endif
SUBDIRS = $(compat_dir)
bin_PROGRAMS = mbsync mdconvert
mbsync_SOURCES = main.c sync.c config.c util.c drv_imap.c drv_maildir.c
mbsync_LDADD = -ldb $(SSL_LIBS) $(SOCK_LIBS)
noinst_HEADERS = isync.h
mdconvert_SOURCES = mdconvert.c
mdconvert_LDADD = -ldb
man_MANS = mbsync.1 mdconvert.1
EXTRA_DIST = mbsyncrc.sample $(man_MANS)

4
src/compat/.cvsignore

@ -0,0 +1,4 @@
.deps
Makefile
Makefile.in
isync

8
src/compat/Makefile.am

@ -0,0 +1,8 @@
bin_PROGRAMS = isync
isync_SOURCES = main.c config.c convert.c util.c
isync_LDADD = -ldb
noinst_HEADERS = isync.h
man_MANS = isync.1
EXTRA_DIST = isyncrc.sample $(man_MANS)

443
src/compat/config.c

@ -0,0 +1,443 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* 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 "isync.h"
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <pwd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
static char *
my_strndup( const char *s, size_t nchars )
{
char *r = nfmalloc( sizeof(char) * (nchars + 1) );
memcpy( r, s, nchars );
r[nchars] = 0;
return r;
}
char *
expand_strdup( const char *s )
{
struct passwd *pw;
const char *p, *q;
char *r;
if (*s == '~') {
s++;
if (!*s) {
p = 0;
q = Home;
} else if (*s == '/') {
p = s + 1;
q = Home;
} else {
if ((p = strchr( s, '/' ))) {
r = my_strndup( s, (int)(p - s) );
pw = getpwnam( r );
free( r );
p++;
} else
pw = getpwnam( s );
if (!pw)
return 0;
q = pw->pw_dir;
}
nfasprintf( &r, "%s/%s", q, p ? p : "" );
return r;
} else if (*s != '/' && xmaildir) {
nfasprintf( &r, "%s/%s", xmaildir, s );
return r;
} else
return nfstrdup( 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 *path, config_t ***stor )
{
config_t **sstor, *cfg;
FILE *fp;
char *p, *cmd, *val;
int line = 0;
char buf[1024];
if (!(fp = fopen( path, "r" ))) {
if (errno != ENOENT)
perror( "fopen" );
return;
}
if (!Quiet && !Debug && !Verbose)
printf( "Reading configuration file %s\n", path );
buf[sizeof(buf) - 1] = 0;
cfg = &global;
while (fgets( buf, sizeof(buf) - 1, fp )) {
p = buf;
cmd = next_arg( &p );
val = next_arg( &p );
line++;
if (!cmd || *cmd == '#')
continue;
if (!val) {
fprintf( stderr, "%s:%d: parameter missing\n", path, line );
continue;
}
if (!strcasecmp( "Mailbox", cmd )) {
if (o2o)
break;
cfg = **stor = nfmalloc( sizeof(config_t) );
*stor = &cfg->next;
memcpy( cfg, &global, sizeof(config_t) );
/* not expanded at this point */
cfg->path = nfstrdup( val );
} else if (!strcasecmp( "OneToOne", cmd )) {
if (boxes) {
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 (boxes)
goto forbid;
maildir = nfstrdup( val );
xmaildir = expand_strdup( val );
} else if (!strcasecmp( "Folder", cmd )) {
if (boxes)
goto forbid;
folder = nfstrdup( val );
} else if (!strcasecmp( "Inbox", cmd )) {
if (boxes)
goto forbid;
inbox = nfstrdup( val );
} else if (!strcasecmp( "Host", cmd )) {
if (!memcmp( "imaps:", val, 6 )) {
val += 6;
cfg->use_imaps = 1;
cfg->port = 993;
cfg->use_sslv2 = 1;
cfg->use_sslv3 = 1;
}
cfg->host = nfstrdup( val );
} else if (!strcasecmp( "User", cmd ))
cfg->user = nfstrdup( val );
else if (!strcasecmp( "Pass", cmd ))
cfg->pass = nfstrdup( val );
else if (!strcasecmp ( "Port", cmd ))
cfg->port = atoi( val );
else if (!strcasecmp ( "Box", cmd ))
cfg->box = nfstrdup( val );
else if (!strcasecmp ( "Alias", cmd )) {
if (!boxes) {
fprintf( stderr,
"%s:%d: keyword 'Alias' allowed only in mailbox specification\n",
path, line );
continue;
}
cfg->alias = nfstrdup( val );
} else if (!strcasecmp( "MaxSize", cmd ))
cfg->max_size = atol( val );
else if (!strcasecmp ( "MaxMessages", cmd ))
cfg->max_messages = atol( val );
else if (!strcasecmp ( "UseNamespace", cmd ))
cfg->use_namespace = is_true( val );
else if (!strcasecmp ( "CopyDeletedTo", cmd ))
cfg->copy_deleted_to = nfstrdup( val );
else if (!strcasecmp ( "Tunnel", cmd ))
cfg->tunnel = nfstrdup( val );
else if (!strcasecmp ( "Expunge", cmd ))
cfg->expunge = is_true( val );
else if (!strcasecmp( "Delete", cmd ))
cfg->delete = is_true( val );
else if (!strcasecmp( "CertificateFile", cmd ))
cfg->cert_file = expand_strdup( val );
else if (!strcasecmp( "RequireSSL", cmd ))
cfg->require_ssl = is_true( val );
else if (!strcasecmp( "UseSSLv2", cmd ))
cfg->use_sslv2 = is_true( val );
else if (!strcasecmp( "UseSSLv3", cmd ))
cfg->use_sslv3 = is_true( val );
else if (!strcasecmp( "UseTLSv1", cmd ))
cfg->use_tlsv1 = is_true( val );
else if (!strcasecmp( "RequireCRAM", cmd ))
cfg->require_cram = is_true( val );
else if (buf[0])
fprintf( stderr, "%s:%d: unknown keyword '%s'\n", path, line, cmd );
}
fclose( fp );
if (o2o) {
if (!global.host && !global.tunnel) {
fprintf( stderr, "Neither Host nor Tunnel given to OneToOne. Aborting.\n" );
exit( 1 );
}
} else
for (sstor = &boxes; (cfg = *sstor); ) {
if (!cfg->host && !cfg->tunnel) {
fprintf( stderr, "Mailbox '%s' has neither Host nor Tunnel. Skipping.\n",
cfg->alias ? cfg->alias : cfg->path );
if (&cfg->next == *stor)
*stor = sstor;
*sstor = cfg->next;
continue;
}
sstor = &cfg->next;
}
}
static const char *
tb( int on )
{
return on ? "yes" : "no";
}
static void
write_imap_server( FILE *fp, config_t *cfg )
{
config_t *pbox;
char *p, *p2;
int hl, a1, a2, a3, a4;
char buf[128];
static int tunnels;
if (cfg->tunnel) {
nfasprintf( (char **)&cfg->server_name, "tunnel%d", ++tunnels );
fprintf( fp, "IMAPAccount %s\nTunnel \"%s\"\n",
cfg->server_name, cfg->tunnel );
} else {
if (sscanf( cfg->host, "%d.%d.%d.%d", &a1, &a2, &a3, &a4 ) == 4)
cfg->server_name = nfstrdup( cfg->host );
else {
p = strrchr( cfg->host, '.' );
if (!p)
hl = nfsnprintf( buf, sizeof(buf), "%s", cfg->host );
else {
hl = nfsnprintf( buf, sizeof(buf), "%.*s", p - cfg->host, cfg->host );
p2 = strrchr( buf, '.' );
if (p2)
hl = sprintf( buf, "%s", p2 + 1 );
}
if (boxes) /* !o2o */
for (pbox = boxes; pbox != cfg; pbox = pbox->next)
if (!memcmp( pbox->server_name, buf, hl + 1 )) {
nfasprintf( (char **)&cfg->server_name, "%s-%d", buf, ++pbox->servers );
goto gotsrv;
}
cfg->server_name = nfstrdup( buf );
cfg->servers = 1;
gotsrv: ;
}
fprintf( fp, "IMAPAccount %s\n", cfg->server_name );
if (cfg->use_imaps)
fprintf( fp, "Host imaps:%s\n", cfg->host );
else
fprintf( fp, "Host %s\n", cfg->host );
fprintf( fp, "Port %d\n", cfg->port );
}
if (cfg->user)
fprintf( fp, "User %s\n", cfg->user );
if (cfg->pass)
fprintf( fp, "Pass \"%s\"\n", cfg->pass );
fprintf( fp, "RequireCRAM %s\nRequireSSL %s\n"
"UseSSLv2 %s\nUseSSLv3 %s\nUseTLSv1 %s\n",
tb(cfg->require_cram), tb(cfg->require_ssl),
tb(cfg->use_sslv2), tb(cfg->use_sslv3), tb(cfg->use_tlsv1) );
if ((cfg->use_imaps || cfg->use_sslv2 || cfg->use_sslv3 || cfg->use_tlsv1) &&
cfg->cert_file)
fprintf( fp, "CertificateFile %s\n", cfg->cert_file );
fputc( '\n', fp );
}
static void
write_imap_store( FILE *fp, config_t *cfg )
{
if (cfg->stores > 1)
nfasprintf( (char **)&cfg->store_name, "%s-%d", cfg->server_name, cfg->stores );
else
cfg->store_name = cfg->server_name;
fprintf( fp, "IMAPStore %s\nAccount %s\n",
cfg->store_name, cfg->server_name );
if (*folder)
fprintf( fp, "Path \"%s\"\n", folder );
else
fprintf( fp, "UseNamespace %s\n", tb(cfg->use_namespace) );
if (inbox)
fprintf( fp, "MapInbox \"%s\"\n", inbox );
if (cfg->copy_deleted_to)
fprintf( fp, "Trash \"%s\"\n", cfg->copy_deleted_to );
fputc( '\n', fp );
}
static void
write_channel_parm( FILE *fp, config_t *cfg )
{
if (cfg->max_size)
fprintf( fp, "MaxSize %d\n", cfg->max_size );
if (cfg->max_messages)
fprintf( fp, "MaxMessages %d\n", cfg->max_messages );
if (!cfg->delete)
fputs( "Sync New ReNew Flags\n", fp );
if (cfg->expunge)
fputs( "Expunge Both\n", fp );
fputc( '\n', fp );
}
static int
mstrcmp( const char *s1, const char *s2 )
{
if (s1 == s2)
return 0;
if (!s1 || !s2)
return 1;
return strcmp( s1, s2 );
}
void
write_config( int fd )
{
FILE *fp;
const char *cn, *scn;
config_t *box, *sbox, *pbox;
if (!(fp = fdopen( fd, "w" ))) {
perror( "fdopen" );
return;
}
fprintf( fp, "SyncState *\n\nMaildirStore local\nPath \"%s/\"\nAltMap %s\n\n", maildir, tb( altmap > 0 ) );
if (o2o) {
write_imap_server( fp, &global );
write_imap_store( fp, &global );
fprintf( fp, "Channel o2o\nMaster :%s:\nSlave :local:\nPattern %%\n", global.store_name );
write_channel_parm( fp, &global );
} else {
for (box = boxes; box; box = box->next) {
for (pbox = boxes; pbox != box; pbox = pbox->next) {
if (box->tunnel) {
if (mstrcmp( pbox->tunnel, box->tunnel ))
continue;
} else {
if (mstrcmp( pbox->host, box->host ) ||
pbox->use_imaps != box->use_imaps ||
pbox->port != box->port)
continue;
}
if (mstrcmp( pbox->user, box->user ) ||
mstrcmp( pbox->pass, box->pass )) /* nonsense */
continue;
if ((box->use_imaps || box->use_sslv2 ||
box->use_sslv3 || box->use_tlsv1) &&
mstrcmp( pbox->cert_file, box->cert_file )) /* nonsense */
continue;
if (pbox->use_imaps != box->use_imaps ||
pbox->use_sslv2 != box->use_sslv2 ||
pbox->use_sslv3 != box->use_sslv3 ||
pbox->use_tlsv1 != box->use_tlsv1)
continue;
box->server_name = pbox->server_name;
for (sbox = boxes; sbox != box; sbox = sbox->next) {
if (sbox->server_name != box->server_name ||
mstrcmp( sbox->copy_deleted_to, box->copy_deleted_to ) ||
(!*folder && sbox->use_namespace != box->use_namespace))
continue;
box->store_name = sbox->store_name;
goto gotall;
}
box->stores = ++pbox->stores;
goto gotsrv;
}
write_imap_server( fp, box );
box->stores = 1;
gotsrv:
write_imap_store( fp, box );
gotall:
if (box->alias)
cn = box->alias;
else {
cn = strrchr( box->path, '/' );
if (cn)
cn++;
else
cn = box->path;
}
for (sbox = boxes; sbox != box; sbox = sbox->next) {
if (sbox->alias)
scn = sbox->alias;
else {
scn = strrchr( sbox->path, '/' );
if (scn)
scn++;
else
scn = sbox->path;
}
if (mstrcmp( cn, scn ))
continue;
nfasprintf( (char **)&box->channel_name, "%s-%d", cn, ++sbox->channels );
goto gotchan;
}
box->channels = 1;
box->channel_name = cn;
gotchan:
fprintf( fp, "Channel %s\nMaster :%s:%s\nSlave :local:%s\n",
box->channel_name, box->store_name, box->box, box->path );
write_channel_parm( fp, box );
}
}
fclose( fp );
}
config_t *
find_box( const char *s )
{
config_t *p;
char *t;
for (p = boxes; p; p = p->next) {
if (!strcmp( s, p->path ) || (p->alias && !strcmp( s, p->alias )))
return p;
/* check to see if the full pathname was specified on the
* command line.
*/
t = expand_strdup( p->path );
if (!strcmp( s, t )) {
free( t );
return p;
}
free( t );
}
return 0;
}

259
src/compat/convert.c

@ -0,0 +1,259 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* 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 "isync.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>
#include <db.h>
static const char *subdirs[] = { "cur", "new", "tmp" };
static const char Flags[] = { 'D', 'F', 'R', 'S', 'T' };
static int
parse_info( const char *s )
{
unsigned i;
int flags;
flags = 0;
if (s && *(s + 1) == '2' && *(s + 2) == ',')
for (s += 3, i = 0; i < as(Flags); i++)
if (strchr( s, Flags[i] ))
flags |= (1 << i);
return flags;
}
typedef struct {
int uid, flags;
} msg_t;
static int
compare_uids( const void *l, const void *r )
{
return ((msg_t *)l)->uid - ((msg_t *)r)->uid;
}
static DBT key, value;
static struct flock lck;
void
convert( config_t *box )
{
DIR *d;
struct dirent *e;
char *s, *p, *mboxdir;
FILE *fp;
msg_t *msgs;
DB *db;
int i, ret, fd, uidval, maxuid, bl, uid, rmsgs, nmsgs, uv[2];
unsigned u;
struct stat sb;
char buf[_POSIX_PATH_MAX], diumname[_POSIX_PATH_MAX],
uvname[_POSIX_PATH_MAX], sname[_POSIX_PATH_MAX],
iuvname[_POSIX_PATH_MAX], imuname[_POSIX_PATH_MAX],
ilname[_POSIX_PATH_MAX], iumname[_POSIX_PATH_MAX];
mboxdir = expand_strdup( box->path );
nfsnprintf( iuvname, sizeof(iuvname), "%s/isyncuidvalidity", mboxdir );
nfsnprintf( diumname, sizeof(iumname), "%s/.isyncuidmap.db", mboxdir );
nfsnprintf( uvname, sizeof(uvname), "%s/.uidvalidity", mboxdir );
if (stat( iuvname, &sb )) {
if (!stat( diumname, &sb ))
altmap++;
else if (!stat( uvname, &sb ))
altmap--;
err1:
free( mboxdir );
return;
}
for (i = 0; i < 3; i++) {
nfsnprintf( buf, sizeof(buf), "%s/%s", mboxdir, subdirs[i] );
if (stat( buf, &sb )) {
fprintf( stderr, "ERROR: stat %s: %s (errno %d)\n", buf,
strerror(errno), errno );
fprintf( stderr,
"ERROR: %s does not appear to be a valid maildir style mailbox\n",
mboxdir );
goto err1;
}
}
nfsnprintf( iumname, sizeof(iumname), "%s/isyncuidmap.db", mboxdir );
nfsnprintf( imuname, sizeof(imuname), "%s/isyncmaxuid", mboxdir );
nfsnprintf( ilname, sizeof(ilname), "%s/isynclock", mboxdir );
nfsnprintf( sname, sizeof(sname), "%s/.mbsyncstate", mboxdir );
if ((fd = open( ilname, O_WRONLY|O_CREAT, 0600 )) < 0) {
perror( ilname );
goto err1;
}
#if SEEK_SET != 0
lck.l_whence = SEEK_SET;
#endif
#if F_WRLCK != 0
lck.l_type = F_WRLCK;
#endif
if (fcntl( fd, F_SETLKW, &lck )) {
perror( ilname );
err2:
close( fd );
goto err1;
}
if (!(fp = fopen( iuvname, "r" ))) {
perror( iuvname );
goto err2;
}
fscanf( fp, "%d", &uidval );
fclose( fp );
if (!(fp = fopen( imuname, "r" ))) {
perror( imuname );
goto err2;
}
fscanf( fp, "%d", &maxuid );
fclose( fp );
if (!stat( iumname, &sb )) {
if (db_create( &db, 0, 0 )) {
fputs( "dbcreate failed\n", stderr );
goto err2;
}
if (db->open( db, 0, iumname, 0, DB_HASH, 0, 0 )) {
fputs( "cannot open db\n", stderr );
db->close( db, 0 );
goto err2;
}
altmap++;
} else {
db = 0;
altmap--;
}
msgs = 0;
rmsgs = 0;
nmsgs = 0;
for (i = 0; i < 2; i++) {
bl = nfsnprintf( buf, sizeof(buf), "%s/%s/", mboxdir, subdirs[i] );
if (!(d = opendir( buf ))) {
perror( "opendir" );
err4:
if (msgs)
free( msgs );
if (db)
db->close( db, 0 );
goto err2;
}
while ((e = readdir( d ))) {
if (*e->d_name == '.')
continue;
s = strchr( e->d_name, ':' );
if (db) {
key.data = e->d_name;
key.size = s ? (size_t)(s - e->d_name) : strlen( e->d_name );
if ((ret = db->get( db, 0, &key, &value, 0 ))) {
if (ret != DB_NOTFOUND)
db->err( db, ret, "Maildir error: db->get()" );
continue;
}
uid = *(int *)value.data;
} else if ((p = strstr( e->d_name, ",U=" )))
uid = atoi( p + 3 );
else
continue;
if (nmsgs == rmsgs) {
rmsgs = rmsgs * 2 + 100;
msgs = nfrealloc( msgs, rmsgs * sizeof(msg_t) );
}
msgs[nmsgs].uid = uid;
msgs[nmsgs++].flags = parse_info( s );
}
closedir( d );
}
qsort( msgs, nmsgs, sizeof(msg_t), compare_uids );
if (!(fp = fopen( sname, "w" ))) {
perror( sname );
goto err4;
}
if (box->max_messages) {
if (!nmsgs)
i = maxuid;
else {
i = nmsgs - box->max_messages;
if (i < 0)
i = 0;
i = msgs[i].uid - 1;
}
} else
i = 0;
fprintf( fp, "%d:%d %d:%d:%d\n", uidval, maxuid, uidval, i, maxuid );
for (i = 0; i < nmsgs; i++) {
fprintf( fp, "%d %d ", msgs[i].uid, msgs[i].uid );
for (u = 0; u < as(Flags); u++)
if (msgs[i].flags & (1 << u))
fputc( Flags[u], fp );
fputc( '\n', fp );
}
fclose( fp );
if (db) {
key.data = (void *)"UIDVALIDITY";
key.size = 11;
uv[0] = uidval;
uv[1] = maxuid;
value.data = uv;
value.size = sizeof(uv);
if ((ret = db->put( db, 0, &key, &value, 0 ))) {
db->err( db, ret, "Maildir error: db->put()" );
goto err4;
}
db->close( db, 0 );
rename( iumname, diumname );
} else {
if (!(fp = fopen( uvname, "w" ))) {
perror( uvname );
goto err4;
}
fprintf( fp, "%d\n%d\n", uidval, maxuid );
fclose( fp );
}
unlink( iuvname );
unlink( imuname );
close( fd );
unlink( ilname );
if (msgs)
free( msgs );
free( mboxdir );
return;
}

192
isync.1 → src/compat/isync.1

@ -1,6 +1,7 @@
.ig
\" isync - IMAP4 to maildir mailbox synchronizer
\" isync - mbsync wrapper: IMAP4 to Maildir mailbox synchronizer
\" Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
\" Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
\"
\" 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,26 +17,27 @@
\" along with this program; if not, write to the Free Software
\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\"
\" As a special exception, isync may be linked with the OpenSSL library,
\" despite that library's more restrictive license.
..
.TH isync 1 "2002 Dec 22"
.TH isync 1 "2004 Mar 27"
..
.SH NAME
isync - synchronize IMAP4 and maildir mailboxes
isync - synchronize IMAP4 and Maildir mailboxes
..
.SH SYNOPSIS
\fBisync\fR [\fIoptions...\fR] \fImailbox\fR [\fImailbox ...\fR]
.br
\fBisync\fR [\fIoptions...\fR] \fI-a\fR
.br
\fBisync\fR [\fIoptions...\fR] \fI-l\fR
\fBisync\fR [\fIoptions\fR ...] {\fImailbox\fR ...|\fI-a\fR|\fI-l\fR}
..
.SH DESCRIPTION
\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
\fBisync\fR is a command line application which synchronizes local
Maildir mailboxes with remote IMAP4 mailboxes, suitable for use in
IMAP-disconnected mode. Multiple copies of the remote IMAP4 mailboxes can
be maintained, and all flags are synchronized.
.br
\fBisync\fR is only a wrapper binary around \fBmbsync\fR to simplify upgrades.
It will automatically migrate the UID mapping from previous versions of
\fBisync\fR (even before 0.8) to the new format, and transparently call
\fBmbsync\fR. If you were using \fBisync\fR version 0.8 or 0.9.x you might
want to use \fBmdconvert\fR to convert the mailboxes to the more efficient
\fBnative\fR UID storage scheme after migrating them.
..
.SH OPTIONS
.TP
@ -60,7 +62,7 @@ Synchronize all mailboxes (either specified in ~/.isyncrc or determined by the
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
Automatically create the local Maildir mailbox if it doesn't already
exist.
.TP
\fB-R\fR, \fB--create-remote\fR
@ -71,25 +73,23 @@ Automatically create any mailboxes if they don't already exist.
This is simply a combination of -L and -R.
.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.
Causes \fBisync\fR to propagate message deletions.
By default, \fIdead\fR messages are \fBnot\fR deleted.
.TP
\fB-e\fR, \fB--expunge\fR
Causes \fBisync\fR to permanently remove all messages marked for deletion
in both the local maildir mailbox and the remote IMAP mailbox. By default,
\fIdeleted\fR messages are \fBnot\fR expunged.
Causes \fBisync\fR to permanently remove all messages marked for deletion.
By default, \fIdeleted\fR messages are \fBnot\fR expunged.
.TP
\fB-f\fR, \fB--fast\fR
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.
Only fetch new messages existing on the server into the local mailbox.
Message deletions and flag changes will not be propagated.
.TP
\fB-h\fR, \fB--help\fR
Displays a summary of command line options
.TP
\fB-p\fR, \fB--port\fR \fIport\fR
Specifies the port on the IMAP server to connect to (default: 143)
Specifies the port on the IMAP server to connect to (default: 143 for imap,
993 for imaps)
.TP
\fB-q\fR, \fB--quiet\fR
Suppress informational messages.
@ -99,7 +99,7 @@ If specified twice, suppress warning messages as well.
Specifies the name of the remote IMAP mailbox to synchronize with
(Default: INBOX)
.TP
\fB-s\fR, \fB--host\fR \fB[\fRimaps:\fB]\fR\fIhost\fR
\fB-s\fR, \fB--host\fR [\fBimaps:\fR]\fIhost\fR
Specifies the hostname of the IMAP server
.TP
\fB-u\fR, \fB--user\fR \fIuser\fR
@ -116,32 +116,49 @@ Displays \fBisync\fR version information.
.TP
\fB-V\fR, \fB--verbose\fR
Enables \fIverbose\fR mode, which displays the IMAP4 network traffic.
.TP
\fB-D\fR, \fB--debug\fR
Enable printing of \fIdebug\fR messages.
.TP
\fB-w\fR, \fB--write\fR
Don't run \fBmbsync\fR, but instead write a permanent config file for it.
The UID mappings of all configured mailboxes will be migrated.
Note that some command line options that would affect an actual sync operation
will be incorporated into the new config file as well.
The name of the new config file is determined by replacing the last occurrence
of "isync" with "mbsync", or appending ".mbsync" if "isync" was not found.
.TP
\fB-W\fR, \fB--writeto\fR \fIfile\fR
Like \fB-w\fR, but use the specified name for the new config file.
..
.SH CONFIGURATION
\fBisync\fR reads \fI~/.isyncrc\fR to load default configuration data.
Each line of the configuration file consists of a command.
\fBisync\fR by default reads \fI~/.isyncrc\fR to load configuration data.
Each non-empty line of the configuration file that does not start with a
hash mark consists of a command.
The following commands are understood:
.TP
\fBMailbox\fR \fIpath\fR
Defines a local maildir mailbox. All configuration commands following this
Defines a local Maildir mailbox. All configuration commands following this
line, up until the next \fIMailbox\fR command, apply to this mailbox only.
..
.TP
\fBHost\fR \fB[\fRimaps:\fB]\fR\fIname\fR
\fBHost\fR [\fBimaps:\fR]\fIname\fR
Defines the DNS name or IP address of the IMAP server. If the hostname is
prefixed with \fIimaps:\fR the connection is assumed to be a SSL connection
prefixed with \fBimaps:\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.
\fBafter\fR the \fBHost\fR command).
Note that modern servers support SSL on the default port 143.
\fBisync\fR will always attempt to use SSL if available.
..
.TP
\fBPort\fR \fIport\fR
Defines the TCP port number on the IMAP server to use (Default: 143)
Defines the TCP port number of the IMAP server (Default: 143 for imap,
993 for imaps)
..
.TP
\fBBox\fR \fImailbox\fR
Defines the name of the remote IMAP mailbox associated with the local
maildir mailbox (Default: INBOX)
Maildir mailbox (Default: INBOX)
..
.TP
\fBUser\fR \fIusername\fR
@ -161,19 +178,18 @@ command line.
..
.TP
\fBCopyDeletedTo\fR \fImailbox\fR
Specifies the remote IMAP mailbox to copy deleted messages prior to
Specifies the remote IMAP mailbox to copy deleted messages to prior to
expunging (Default: none).
..
.TP
\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).
Specifies whether message deletions are propagated. (Default: no).
\fBNOTE:\fR The \fI-d\fR command line option overrides this setting when
set to \fIno\fR.
..
.TP
\fBExpunge\fR \fIyes\fR|\fIno\fR
Specifies whether deleted messages are expunged by default (Default: no).
Specifies whether deleted messages are expunged. (Default: no).
\fBNOTE:\fR The \fI-e\fR command line option overrides this setting when
set to \fIno\fR.
..
@ -181,7 +197,7 @@ set to \fIno\fR.
\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
\fBNOTE:\fR This directive is allowed only in the \fIglobal\fR
section (see below).
..
.TP
@ -190,28 +206,28 @@ 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
\fBNOTE 2:\fR This directive is allowed only in the \fIglobal\fR
section (see below).
..
.TP
\fBMaxMessages\fR \fIcount\fR
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
pass \fBisync\fR the delete (-d, --delete) flag.
Messages that are flagged (marked as important) will not be automatically
deleted.
If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR
(Default: 0).
Sets the number of messages \fBisync\fR should keep in the local copy of 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 (independently of the
actual date of the message) will be deleted first.
Messages that are flagged (marked as important) and recent messages will not be
automatically deleted.
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 \fBisync\fR
should transfer over the wire. This is useful for weeding out messages with
large attachments. If \fIbytes\fR is 0, the maximum file size is \fBunlimited\fR.
Messages larger than that many bytes will not be transferred over the wire.
This is useful for weeding out messages with large attachments.
If \fIbytes\fR is 0, the maximum file size is \fBunlimited\fR.
(Default: 0)
..
.TP
\fBTunnel\fR \fIcommand\fR
@ -220,19 +236,20 @@ socket. This allows you to run an IMAP session over an SSH tunnel, for
example.
.TP
\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)
Selects whether the server's first "personal" NAMESPACE should be prefixed to
mailbox names. Disabling this makes sense for some broken IMAP servers.
This option is meaningless if a \fIFolder\fR was specified.
(Default: \fIyes\fR)
..
.TP
\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.
If set to \fIyes\fR, \fBisync\fR will abort the connection if no CRAM-MD5
authentication is possible. (Default: \fIno\fR)
..
.TP
\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)
\fBisync\fR will abort the connection if a TLS/SSL session cannot be
established with the IMAP server. (Default: \fIyes\fR)
..
.TP
\fBCertificateFile\fR \fIpath\fR
@ -258,7 +275,7 @@ Should \fBisync\fR use TLSv1 for communication with the IMAP server over SSL?
\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
\fBNOTE:\fR This directive is allowed only in the \fIglobal\fR
section (see below).
..
.TP
@ -278,63 +295,20 @@ 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 MAIL USER AGENT INTERACTION
To use \fBisync\fR effectively, you need a mail client that sets the T
(trashed) flag when it deletes a message from a maildir mailbox, instead of
just removing it altogether. Without such a client, \fBisync\fR will refetch the
locally deleted messages from the server since they will never get expunged.
Mutt (starting with version 1.3.27) is known to support this. Be sure to put
.IP "" 2
set maildir_trash
.PP
in your ~/.muttrc when using Mutt.
.br
\fBisync\fR can be integrated into Mutt fairly easily with a few hooks:
.IP "" 2
.nf
folder-hook ~A bind index $ <sync-mailbox>
.br
folder-hook +\fImdir\fR 'macro index $ "<sync-mailbox>!isync -e \fImdir\fR\\n"'
.fi
.PP
where \fImdir\fR is the name of the local mailbox (or its \fIalias\fR).
This works well so long as you are not modifying the IMAP mailbox outside of
Mutt. However, if you are using another mail program simultaneously Mutt
will have the wrong idea of the local mailbox flags and messages will start
disappearing from its index display (don't worry, they are still on disk).
..
.SH FILES
.TP
.B ~/.isyncrc
Default configuration file
..
.SH BUGS
No support for IMAP subfolders.
.P
\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 \fBisync\fR
has retrieved the initial message list, the new mail will not be fetched
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
doesn't have enough information to know which was the last status of the
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.
.br
Use -c /dev/null to work around.
.P
See the \fBINHERENT PROBLEMS\fR section in the \fBmbsync\fR man page, too.
..
.SH SEE ALSO
mutt(1), maildir(5)
mbsync(1), mdconvert(1), mutt(1), maildir(5)
.P
Up to date information on \fBisync\fR can be found at http://isync.sf.net/
..

100
src/compat/isync.h

@ -0,0 +1,100 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* 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
*/
#define _GNU_SOURCE
#include <config.h>
#include <sys/types.h>
#include <stdarg.h>
#define as(ar) (sizeof(ar)/sizeof(ar[0]))
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_UNUSED __attribute__((unused))
# define ATTR_NORETURN __attribute__((noreturn))
# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
#else
# define ATTR_UNUSED
# define ATTR_NORETURN
# define ATTR_PRINTFLIKE(fmt,var)
#endif
typedef struct config {
struct config *next;
const char *server_name;
int servers;
char *host;
int port;
char *user;
char *pass;
char *tunnel;
unsigned int require_cram:1;
unsigned int require_ssl:1;
unsigned int use_imaps:1;
unsigned int use_sslv2:1;
unsigned int use_sslv3:1;
unsigned int use_tlsv1:1;
char *cert_file;
const char *store_name;
int stores;
char *copy_deleted_to;
unsigned int use_namespace:1;
const char *channel_name;
int channels;
const char *alias;
const char *box;
const char *path; /* path relative to .maildir, or absolute path */
int max_size;
unsigned int max_messages;
unsigned int expunge:1;
unsigned int delete:1;
} config_t;
extern int Quiet, Verbose, Debug;
extern const char *Home;
extern config_t global, *boxes;
extern const char *maildir, *xmaildir, *folder, *inbox;
extern int o2o, altmap;
/* config.c */
void load_config( const char *, config_t *** );
void write_config( int );
char *expand_strdup( const char * );
config_t *find_box( const char * );
/* convert.c */
void convert( config_t * );
/* util.c */
char *next_arg( char ** );
void *nfmalloc( size_t sz );
void *nfrealloc( void *mem, size_t sz );
char *nfstrdup( const char *str );
int nfvasprintf( char **str, const char *fmt, va_list va );
int nfasprintf( char **str, const char *fmt, ... );
int nfsnprintf( char *buf, int blen, const char *fmt, ... );
void ATTR_NORETURN oob( void );

0
isyncrc.sample → src/compat/isyncrc.sample

431
src/compat/main.c

@ -0,0 +1,431 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* 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 "isync.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.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>
#if HAVE_GETOPT_LONG
# define _GNU_SOURCE
# include <getopt.h>
struct option Opts[] = {
{"write", 0, NULL, 'w' },
{"writeto", 0, NULL, 'W' },
{"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'},
{"debug", 0, NULL, 'D'},
{"quiet", 0, NULL, 'q'},
{"user", 1, NULL, 'u'},
{"version", 0, NULL, 'v'},
{"verbose", 0, NULL, 'V'},
{0, 0, 0, 0}
};
#endif
static void
version( void )
{
puts( PACKAGE " " VERSION );
exit( 0 );
}
static void
usage( int code )
{
fputs(
PACKAGE " " VERSION " - mbsync wrapper: IMAP4 to maildir synchronizer\n"
"Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>\n"
"Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>\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\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"
" -D, --debug print debugging messages\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\n"
"Note that this is a wrapper binary only; the \"real\" isync is named \"mbsync\".\n"
"Options to permanently transform your old isync configuration:\n"
" -w, --write write permanent mbsync configuration\n"
" -W, --writeto FILE write permanent mbsync configuration to FILE\n",
code ? stderr : stdout );
exit( code );
}
static const char *
strrstr( const char *h, const char *n )
{
char *p = strstr( h, n );
if (!p)
return 0;
do {
h = p;
p = strstr( h + 1, n );
} while (p);
return h;
}
static void
add_arg( char ***args, const char *arg )
{
int nu = 0;
if (*args)
for (; (*args)[nu]; nu++);
*args = nfrealloc( *args, sizeof(char *) * (nu + 2));
(*args)[nu] = nfstrdup( arg );
(*args)[nu + 1] = 0;
}
#define OP_EXPUNGE (1<<0)
#define OP_DELETE (1<<1)
#define OP_FAST (1<<2)
#define OP_CREATE_REMOTE (1<<3)
#define OP_CREATE_LOCAL (1<<4)
int Quiet, Verbose, Debug;
config_t global, *boxes;
const char *maildir, *xmaildir, *folder, *inbox;
int o2o, altmap;
const char *Home;
int
main( int argc, char **argv )
{
config_t *box, **stor;
char *config = 0, *outconfig = 0, **args;
int i, pl, fd, mod, all, list, ops, writeout;
struct stat st;
char path1[_POSIX_PATH_MAX], path2[_POSIX_PATH_MAX];
if (!(Home = getenv("HOME"))) {
fputs( "Fatal: $HOME not set\n", stderr );
return 1;
}
/* defaults */
/* XXX the precedence is borked:
it's defaults < cmdline < file instead of defaults < file < cmdline */
global.port = 143;
global.box = "INBOX";
global.use_namespace = 1;
global.require_ssl = 1;
global.use_tlsv1 = 1;
folder = "";
maildir = "~";
xmaildir = Home;
#define FLAGS "wW:alCLRc:defhp:qu:r:F:M:1I:s:vVD"
mod = all = list = ops = writeout = Quiet = Verbose = Debug = 0;
#if HAVE_GETOPT_LONG
while ((i = getopt_long( argc, argv, FLAGS, Opts, NULL )) != -1)
#else
while ((i = getopt( argc, argv, FLAGS )) != -1)
#endif
{
switch (i) {
case 'W':
outconfig = optarg;
/* plopp */
case 'w':
writeout = 1;
break;
case 'l':
list = 1;
/* plopp */
case 'a':
all = 1;
break;
case '1':
o2o = 1;
mod = 1;
break;
case 'C':
ops |= OP_CREATE_REMOTE|OP_CREATE_LOCAL;
break;
case 'L':
ops |= OP_CREATE_LOCAL;
break;
case 'R':
ops |= OP_CREATE_REMOTE;
break;
case 'c':
config = optarg;
break;
case 'd':
ops |= OP_DELETE;
break;
case 'e':
ops |= OP_EXPUNGE;
break;
case 'f':
ops |= OP_FAST;
break;
case 'p':
global.port = atoi( optarg );
mod = 1;
break;
case 'r':
global.box = optarg;
mod = 1;
break;
case 'F':
folder = optarg;
mod = 1;
break;
case 'M':
maildir = optarg;
mod = 1;
break;
case 'I':
inbox = optarg;
mod = 1;
break;
case 's':
#if HAVE_LIBSSL
if (!strncasecmp( "imaps:", optarg, 6 )) {
global.use_imaps = 1;
global.port = 993;
global.use_sslv2 = 1;
global.use_sslv3 = 1;
optarg += 6;
}
#endif
global.host = optarg;
mod = 1;
break;
case 'u':
global.user = optarg;
mod = 1;
break;
case 'D':
Debug = 1;
break;
case 'V':
Verbose = 1;
break;
case 'q':
Quiet++;
break;
case 'v':
version();
case 'h':
usage( 0 );
default:
usage( 1 );
}
}
if (config) {
if (*config != '/') {
if (!getcwd( path1, sizeof(path1) )) {
fprintf( stderr, "Can't obtain working directory\n" );
return 1;
}
pl = strlen( path1 );
nfsnprintf( path1 + pl, sizeof(path1) - pl, "/%s", config );
config = path1;
}
} else {
nfsnprintf( path1, sizeof(path1), "%s/.isyncrc", Home );
config = path1;
}
stor = &boxes;
load_config( config, &stor );
if (!all && !o2o)
for (i = optind; argv[i]; i++)
if (!(box = find_box( argv[i] ))) {
box = nfmalloc( sizeof(config_t) );
memcpy( box, &global, sizeof(config_t) );
box->path = argv[i];
*stor = box;
stor = &box->next;
mod = 1;
}
if (writeout) {
all = 1;
if (mod)
fprintf( stderr,
"Warning: command line switches that influence the resulting config file\n"
"have been supplied.\n" );
} else {
if (!argv[optind] && !all) {
fprintf( stderr, "No mailbox specified. Try isync -h\n" );
return 1;
}
}
if (all) {
if (o2o) {
DIR * dir;
struct dirent *de;
if (!(dir = opendir( xmaildir ))) {
fprintf( stderr, "%s: %s\n", xmaildir, strerror(errno) );
return 1;
}
while ((de = readdir( dir ))) {
if (*de->d_name == '.')
continue;
nfsnprintf( path1, sizeof(path1), "%s/%s/cur", xmaildir, de->d_name );
if (stat( path1, &st ) || !S_ISDIR( st.st_mode ))
continue;
global.path = de->d_name;
global.box = (inbox && !strcmp( inbox, global.path )) ?
"INBOX" : global.path;
convert( &global );
}
closedir( dir );
} else
for (box = boxes; box; box = box->next)
convert( box );
} else {
for (i = optind; argv[i]; i++)
if (o2o) {
global.path = argv[i];
global.box =
(inbox && !strcmp( global.path, inbox )) ?
"INBOX" : global.path;
convert( &global );
} else
convert( find_box( argv[i] ) );
}
if (writeout) {
if (!outconfig) {
const char *p = strrchr( config, '/' );
if (!p)
p = config;
p = strrstr( p, "isync" );
if (!p)
nfsnprintf( path2, sizeof(path2), "%s.mbsync", config );
else
nfsnprintf( path2, sizeof(path2), "%.*smb%s", p - config, config, p + 1 );
outconfig = path2;
}
if ((fd = creat( outconfig, 0666 )) < 0) {
fprintf( stderr, "Error: cannot write new config %s: %s\n", outconfig, strerror(errno) );
return 1;
}
} else {
strcpy( path2, "/tmp/mbsyncrcXXXXXX" );
if ((fd = mkstemp( path2 )) < 0) {
fprintf( stderr, "Can't create temp file\n" );
return 1;
}
}
write_config( fd );
if (writeout)
return 0;
args = 0;
add_arg( &args, "mbsync" );
if (Verbose)
add_arg( &args, "-V" );
if (Debug)
add_arg( &args, "-D" );
for (; Quiet; Quiet--)
add_arg( &args, "-q" );
add_arg( &args, "-cT" );
add_arg( &args, path2 );
if (ops & OP_FAST)
add_arg( &args, "-Ln" );
else if (!(ops & OP_DELETE))
add_arg( &args, "-nNf" );
if (ops & OP_CREATE_REMOTE)
add_arg( &args, "-Cm" );
if (ops & OP_CREATE_LOCAL)
add_arg( &args, "-Cs" );
if (ops & OP_EXPUNGE)
add_arg( &args, "-X" );
if (list)
add_arg( &args, "-l" );
if (o2o) {
if (all)
add_arg( &args, "o2o" );
else {
char buf[1024];
strcpy( buf, "o2o:" );
strcat( buf, argv[optind] );
while (argv[++optind]) {
strcat( buf, "," );
strcat( buf, argv[optind] );
}
add_arg( &args, buf );
}
} else {
if (all)
add_arg( &args, "-a" );
else
for (; argv[optind]; optind++)
add_arg( &args, find_box( argv[optind] )->channel_name );
}
execvp( args[0], args );
perror( args[0] );
return 1;
}

156
src/compat/util.c

@ -0,0 +1,156 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* 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 "isync.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pwd.h>
#include <ctype.h>
char *
next_arg( char **s )
{
char *ret;
if (!s || !*s)
return 0;
while (isspace( (unsigned char) **s ))
(*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;
}
return ret;
}
#ifndef HAVE_VASPRINTF
static int
vasprintf( char **strp, const char *fmt, va_list ap )
{
int len;
char tmp[1024];
if ((len = vsnprintf( tmp, sizeof(tmp), fmt, ap) ) < 0)
*strp = 0;
else if ((*strp = malloc( len + 1 ))) {
if (len >= sizeof(tmp))
vsprintf( *strp, fmt, ap );
else
memcpy( *strp, tmp, len + 1 );
}
return len;
}
#endif
void
oob( void )
{
fputs( "Fatal: buffer too small. Please report a bug.\n", stderr );
abort();
}
int
nfsnprintf( char *buf, int blen, const char *fmt, ... )
{
int ret;
va_list va;
va_start( va, fmt );
if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
oob();
va_end( va );
return ret;
}
static void ATTR_NORETURN
oom( void )
{
fputs( "Fatal: Out of memory\n", stderr );
abort();
}
void *
nfmalloc( size_t sz )
{
void *ret;
if (!(ret = malloc( sz )))
oom();
return ret;
}
void *
nfrealloc( void *mem, size_t sz )
{
char *ret;
if (!(ret = realloc( mem, sz )) && sz)
oom();
return ret;
}
char *
nfstrdup( const char *str )
{
char *ret;
if (!(ret = strdup( str )))
oom();
return ret;
}
int
nfvasprintf( char **str, const char *fmt, va_list va )
{
int ret = vasprintf( str, fmt, va );
if (!*str)
oom();
return ret;
}
int
nfasprintf( char **str, const char *fmt, ... )
{
int ret;
va_list va;
va_start( va, fmt );
ret = nfvasprintf( str, fmt, va );
va_end( va );
return ret;
}

566
src/config.c

@ -1,8 +1,7 @@
/* $Id$
*
* isync - IMAP4 to maildir mailbox synchronizer
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2003 Oswald Buddenhagen <ossi@users.sf.net>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* 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
@ -18,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* As a special exception, isync may be linked with the OpenSSL library,
* As a special exception, mbsync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
@ -33,254 +32,411 @@
#include <stdlib.h>
#include <stdio.h>
config_t *boxes = 0;
store_conf_t *stores;
channel_conf_t *channels;
group_conf_t *groups;
int global_mops, global_sops;
char *global_sync_state;
/* set defaults from the global configuration section */
static void
config_defaults (config_t * conf)
int
parse_bool( conffile_t *cfile )
{
memcpy (conf, &global, sizeof (config_t));
if (!strcasecmp( cfile->val, "yes" ) ||
!strcasecmp( cfile->val, "true" ) ||
!strcasecmp( cfile->val, "on" ) ||
!strcmp( cfile->val, "1" ))
return 1;
if (strcasecmp( cfile->val, "no" ) &&
strcasecmp( cfile->val, "false" ) &&
strcasecmp( cfile->val, "off" ) &&
strcmp( cfile->val, "0" ))
fprintf( stderr, "%s:%d: invalid boolean value '%s'\n",
cfile->file, cfile->line, cfile->val );
return 0;
}
static char *
my_strndup (const char *s, size_t nchars)
{
char *r = malloc (sizeof (char) * (nchars + 1));
if (r)
int
parse_int( conffile_t *cfile )
{
memcpy (r, s, nchars);
r[nchars] = 0;
char *p;
int ret;
ret = strtol( cfile->val, &p, 10 );
if (*p) {
fprintf( stderr, "%s:%d: invalid integer value '%s'\n",
cfile->file, cfile->line, cfile->val );
return 0;
}
return r;
return ret;
}
char *
expand_strdup (const char *s)
int
parse_size( conffile_t *cfile )
{
char path[_POSIX_PATH_MAX];
struct passwd *pw;
const char *p;
char *p;
int ret;
if (*s == '~')
{
s++;
if (*s == '/')
{
/* current user */
pw = getpwuid (getuid ());
p = s + 1;
ret = strtol (cfile->val, &p, 10);
if (*p == 'k' || *p == 'K')
ret *= 1024, p++;
else if (*p == 'm' || *p == 'M')
ret *= 1024 * 1024, p++;
if (*p == 'b' || *p == 'B')
p++;
if (*p) {
fprintf (stderr, "%s:%d: invalid size '%s'\n",
cfile->file, cfile->line, cfile->val);
return 0;
}
return ret;
}
else
{
char *user;
p = strchr (s, '/');
if (p)
static int
getopt_helper( conffile_t *cfile, int *cops, int *mops, int *sops, char **sync_state )
{
user = my_strndup (s, (int)(p - s));
p++;
}
char *arg;
if (!strcasecmp( "Sync", cfile->cmd )) {
arg = cfile->val;
do
if (!strcasecmp( "Push", arg ))
*cops |= XOP_PUSH;
else if (!strcasecmp( "Pull", arg ))
*cops |= XOP_PULL;
else if (!strcasecmp( "ReNew", arg ))
*cops |= OP_RENEW;
else if (!strcasecmp( "New", arg ))
*cops |= OP_NEW;
else if (!strcasecmp( "Delete", arg ))
*cops |= OP_DELETE;
else if (!strcasecmp( "Flags", arg ))
*cops |= OP_FLAGS;
else if (!strcasecmp( "PullReNew", arg ))
*sops |= OP_RENEW;
else if (!strcasecmp( "PullNew", arg ))
*sops |= OP_NEW;
else if (!strcasecmp( "PullDelete", arg ))
*sops |= OP_DELETE;
else if (!strcasecmp( "PullFlags", arg ))
*sops |= OP_FLAGS;
else if (!strcasecmp( "PushReNew", arg ))
*mops |= OP_RENEW;
else if (!strcasecmp( "PushNew", arg ))
*mops |= OP_NEW;
else if (!strcasecmp( "PushDelete", arg ))
*mops |= OP_DELETE;
else if (!strcasecmp( "PushFlags", arg ))
*mops |= OP_FLAGS;
else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg ))
*cops |= XOP_PULL|XOP_PUSH;
else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg ))
fprintf( stderr, "%s:%d: invalid Sync arg '%s'\n",
cfile->file, cfile->line, arg );
while ((arg = next_arg( &cfile->rest )));
*mops |= XOP_HAVE_TYPE;
} else if (!strcasecmp( "Expunge", cfile->cmd )) {
arg = cfile->val;
do
if (!strcasecmp( "Both", arg ))
*cops |= OP_EXPUNGE;
else if (!strcasecmp( "Master", arg ))
*mops |= OP_EXPUNGE;
else if (!strcasecmp( "Slave", arg ))
*sops |= OP_EXPUNGE;
else if (strcasecmp( "None", arg ))
fprintf( stderr, "%s:%d: invalid Expunge arg '%s'\n",
cfile->file, cfile->line, arg );
while ((arg = next_arg( &cfile->rest )));
*mops |= XOP_HAVE_EXPUNGE;
} else if (!strcasecmp( "Create", cfile->cmd )) {
arg = cfile->val;
do
if (!strcasecmp( "Both", arg ))
*cops |= OP_CREATE;
else if (!strcasecmp( "Master", arg ))
*mops |= OP_CREATE;
else if (!strcasecmp( "Slave", arg ))
*sops |= OP_CREATE;
else if (strcasecmp( "None", arg ))
fprintf( stderr, "%s:%d: invalid Create arg '%s'\n",
cfile->file, cfile->line, arg );
while ((arg = next_arg( &cfile->rest )));
*mops |= XOP_HAVE_CREATE;
} else if (!strcasecmp( "SyncState", cfile->cmd ))
*sync_state = expand_strdup( cfile->val );
else
user = strdup (s);
pw = getpwnam (user);
free (user);
}
if (!pw)
return 0;
snprintf (path, sizeof (path), "%s/%s", pw->pw_dir, p ? p : "");
s = path;
return 1;
}
else if (*s != '/')
int
getcline( conffile_t *cfile )
{
snprintf (path, sizeof (path), "%s/%s",
global.maildir ? global.maildir : "", s);
s = path;
char *p;
while (fgets( cfile->buf, cfile->bufl, cfile->fp )) {
cfile->line++;
p = cfile->buf;
if (!(cfile->cmd = next_arg( &p )))
return 1;
if (*cfile->cmd == '#')
continue;
if (!(cfile->val = next_arg( &p ))) {
fprintf( stderr, "%s:%d: parameter missing\n",
cfile->file, cfile->line );
continue;
}
return strdup (s);
cfile->rest = p;
return 1;
}
return 0;
}
static int
is_true (const char *val)
/* XXX - this does not detect None conflicts ... */
int
merge_ops( int cops, int *mops, int *sops )
{
return
!strcasecmp (val, "yes") ||
!strcasecmp (val, "true") ||
!strcasecmp (val, "on") ||
!strcmp (val, "1");
int aops;
aops = *mops | *sops;
if (*mops & XOP_HAVE_TYPE) {
if (aops & OP_MASK_TYPE) {
if (aops & cops & OP_MASK_TYPE) {
cfl:
fprintf( stderr, "Conflicting Sync args specified.\n" );
return 1;
}
*mops |= cops & OP_MASK_TYPE;
*sops |= cops & OP_MASK_TYPE;
if (cops & XOP_PULL) {
if (*sops & OP_MASK_TYPE)
goto cfl;
*sops |= OP_MASK_TYPE;
}
if (cops & XOP_PUSH) {
if (*mops & OP_MASK_TYPE)
goto cfl;
*mops |= OP_MASK_TYPE;
}
} else if (cops & (OP_MASK_TYPE|XOP_MASK_DIR)) {
if (!(cops & OP_MASK_TYPE))
cops |= OP_MASK_TYPE;
else if (!(cops & XOP_MASK_DIR))
cops |= XOP_PULL|XOP_PUSH;
if (cops & XOP_PULL)
*sops |= cops & OP_MASK_TYPE;
if (cops & XOP_PUSH)
*mops |= cops & OP_MASK_TYPE;
}
}
if (*mops & XOP_HAVE_EXPUNGE) {
if (aops & cops & OP_EXPUNGE) {
fprintf( stderr, "Conflicting Expunge args specified.\n" );
return 1;
}
*mops |= cops & OP_EXPUNGE;
*sops |= cops & OP_EXPUNGE;
}
if (*mops & XOP_HAVE_CREATE) {
if (aops & cops & OP_CREATE) {
fprintf( stderr, "Conflicting Create args specified.\n" );
return 1;
}
*mops |= cops & OP_CREATE;
*sops |= cops & OP_CREATE;
}
return 0;
}
void
load_config (const char *where, int *o2o)
int
load_config( const char *where, int pseudo )
{
conffile_t cfile;
store_conf_t *store, **storeapp = &stores, **sptarg;
channel_conf_t *channel, **channelapp = &channels;
group_conf_t *group, **groupapp = &groups;
string_list_t *chanlist, **chanlistapp;
char *arg, *p, **ntarg;
int err, len, cops, gcops, max_size;
char path[_POSIX_PATH_MAX];
char buf[1024];
struct passwd *pw;
config_t **stor = &boxes, *cfg;
int line = 0;
FILE *fp;
char *p, *cmd, *val;
if (!where)
{
pw = getpwuid (getuid ());
snprintf (path, sizeof (path), "%s/.isyncrc", pw->pw_dir);
where = path;
}
if (!where) {
nfsnprintf( path, sizeof(path), "%s/." EXE "rc", Home );
cfile.file = path;
} else
cfile.file = where;
info ("Reading configuration file %s\n", where);
if (!pseudo)
info( "Reading configuration file %s\n", cfile.file );
if (!(cfile.fp = fopen( cfile.file, "r" ))) {
perror( "Cannot open config file" );
return 1;
}
buf[sizeof(buf) - 1] = 0;
cfile.buf = buf;
cfile.bufl = sizeof(buf) - 1;
cfile.line = 0;
fp = fopen (where, "r");
if (!fp)
gcops = err = 0;
reloop:
while (getcline( &cfile )) {
if (!cfile.cmd)
continue;
if (imap_driver.parse_store( &cfile, &store, &err ) ||
maildir_driver.parse_store( &cfile, &store, &err ))
{
if (errno != ENOENT)
perror ("fopen");
return;
if (store) {
if (!store->path)
store->path = "";
*storeapp = store;
storeapp = &store->next;
*storeapp = 0;
}
}
buf[sizeof buf - 1] = 0;
cfg = &global;
while ((fgets (buf, sizeof (buf) - 1, fp)))
else if (!strcasecmp( "Channel", cfile.cmd ))
{
p = buf;
cmd = next_arg (&p);
val = next_arg (&p);
line++;
if (!cmd || *cmd == '#')
continue;
if (!val) {
fprintf (stderr, "%s:%d: parameter missing\n", path, line);
channel = nfcalloc( sizeof(*channel) );
channel->name = nfstrdup( cfile.val );
cops = 0;
max_size = -1;
while (getcline( &cfile ) && cfile.cmd) {
if (!strcasecmp( "MaxSize", cfile.cmd ))
max_size = parse_size( &cfile );
else if (!strcasecmp( "MaxMessages", cfile.cmd ))
channel->max_messages = parse_int( &cfile );
else if (!strcasecmp( "Pattern", cfile.cmd ) ||
!strcasecmp( "Patterns", cfile.cmd ))
{
arg = cfile.val;
do
add_string_list( &channel->patterns, arg );
while ((arg = next_arg( &cfile.rest )));
}
else if (!strcasecmp( "Master", cfile.cmd )) {
sptarg = &channel->master;
ntarg = &channel->master_name;
goto linkst;
} else if (!strcasecmp( "Slave", cfile.cmd )) {
sptarg = &channel->slave;
ntarg = &channel->slave_name;
linkst:
if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) {
fprintf( stderr, "%s:%d: malformed mailbox spec\n",
cfile.file, cfile.line );
err = 1;
continue;
}
if (!strcasecmp ("mailbox", cmd))
{
if (*o2o)
break;
cfg = *stor = malloc (sizeof (config_t));
stor = &cfg->next;
config_defaults (cfg);
/* not expanded at this point */
cfg->path = strdup (val);
*p = 0;
for (store = stores; store; store = store->next)
if (!strcmp( store->name, cfile.val + 1 )) {
memset( sptarg, 0, sizeof(*sptarg) );
*sptarg = store;
goto stpcom;
}
else if (!strcasecmp ("OneToOne", cmd))
{
if (boxes) {
forbid:
fprintf (stderr,
"%s:%d: keyword '%s' allowed only in global section\n",
path, line, cmd);
fprintf( stderr, "%s:%d: unknown store '%s'\n",
cfile.file, cfile.line, cfile.val + 1 );
err = 1;
continue;
stpcom:
if (*++p)
*ntarg = nfstrdup( p );
} else if (!getopt_helper( &cfile, &cops, &channel->mops, &channel->sops, &channel->sync_state )) {
fprintf( stderr, "%s:%d: unknown keyword '%s'\n",
cfile.file, cfile.line, cfile.cmd );
err = 1;
}
*o2o = is_true (val);
}
else if (!strcasecmp ("maildir", cmd))
{
if (boxes)
goto forbid;
/* this only affects the global setting */
global.maildir = expand_strdup (val);
if (!channel->master) {
fprintf( stderr, "channel '%s' refers to no master store\n", channel->name );
err = 1;
} else if (!channel->slave) {
fprintf( stderr, "channel '%s' refers to no slave store\n", channel->name );
err = 1;
} else if (merge_ops( cops, &channel->mops, &channel->sops ))
err = 1;
else {
if (max_size >= 0)
channel->master->max_size = channel->slave->max_size = max_size;
*channelapp = channel;
channelapp = &channel->next;
}
else if (!strcasecmp ("folder", cmd))
{
if (boxes)
goto forbid;
/* this only affects the global setting */
global.folder = strdup (val);
}
else if (!strcasecmp ("inbox", cmd))
else if (!strcasecmp( "Group", cfile.cmd ))
{
if (boxes)
goto forbid;
/* this only affects the global setting */
global.inbox = strdup (val);
group = nfmalloc( sizeof(*group) );
group->name = nfstrdup( cfile.val );
*groupapp = group;
groupapp = &group->next;
*groupapp = 0;
chanlistapp = &group->channels;
*chanlistapp = 0;
p = cfile.rest;
while ((arg = next_arg( &p ))) {
addone:
len = strlen( arg );
chanlist = nfmalloc( sizeof(*chanlist) + len );
memcpy( chanlist->string, arg, len + 1 );
*chanlistapp = chanlist;
chanlistapp = &chanlist->next;
*chanlistapp = 0;
}
else if (!strcasecmp ("host", cmd))
{
#if HAVE_LIBSSL
if (!strncasecmp ("imaps:", val, 6))
while (getcline( &cfile )) {
if (!cfile.cmd)
goto reloop;
if (!strcasecmp( "Channel", cfile.cmd ) ||
!strcasecmp( "Channels", cfile.cmd ))
{
val += 6;
cfg->use_imaps = 1;
cfg->port = 993;
cfg->use_sslv2 = 1;
cfg->use_sslv3 = 1;
}
#endif
cfg->host = strdup (val);
}
else if (!strcasecmp ("user", cmd))
cfg->user = strdup (val);
else if (!strcasecmp ("pass", cmd))
cfg->pass = strdup (val);
else if (!strcasecmp ("port", cmd))
cfg->port = atoi (val);
else if (!strcasecmp ("box", cmd))
cfg->box = strdup (val);
else if (!strcasecmp ("alias", cmd))
{
if (!boxes) {
fprintf (stderr,
"%s:%d: keyword 'alias' allowed only in mailbox specification\n",
path, line);
continue;
p = cfile.rest;
arg = cfile.val;
goto addone;
}
cfg->alias = strdup (val);
}
else if (!strcasecmp ("maxsize", cmd))
cfg->max_size = atol (val);
else if (!strcasecmp ("MaxMessages", cmd))
cfg->max_messages = atol (val);
else if (!strcasecmp ("UseNamespace", cmd))
cfg->use_namespace = is_true (val);
else if (!strcasecmp ("CopyDeletedTo", cmd))
cfg->copy_deleted_to = strdup (val);
else if (!strcasecmp ("Tunnel", cmd))
cfg->tunnel = strdup (val);
else if (!strcasecmp ("Expunge", cmd))
cfg->expunge = is_true (val);
else if (!strcasecmp ("Delete", cmd))
cfg->delete = is_true (val);
#if HAVE_LIBSSL
else if (!strcasecmp ("CertificateFile", cmd))
cfg->cert_file = expand_strdup (val);
else if (!strcasecmp ("RequireSSL", cmd))
cfg->require_ssl = is_true (val);
else if (!strcasecmp ("UseSSLv2", cmd))
cfg->use_sslv2 = is_true (val);
else if (!strcasecmp ("UseSSLv3", cmd))
cfg->use_sslv3 = is_true (val);
else if (!strcasecmp ("UseTLSv1", cmd))
cfg->use_tlsv1 = is_true (val);
else if (!strcasecmp ("RequireCRAM", cmd))
cfg->require_cram = is_true (val);
#endif
else if (buf[0])
fprintf (stderr, "%s:%d: unknown keyword '%s'\n", path, line, cmd);
}
fclose (fp);
}
config_t *
find_box (const char *s)
{
config_t *p = boxes;
for (; p; p = p->next)
{
if (!strcmp (s, p->path) || (p->alias && !strcmp (s, p->alias)))
return p;
else
{
/* check to see if the full pathname was specified on the
* command line.
*/
char *t = expand_strdup (p->path);
if (!strcmp (s, t))
fprintf( stderr, "%s:%d: unknown keyword '%s'\n",
cfile.file, cfile.line, cfile.cmd );
err = 1;
}
}
break;
}
else if (!getopt_helper( &cfile, &gcops, &global_mops, &global_sops, &global_sync_state ))
{
free (t);
return p;
fprintf( stderr, "%s:%d: unknown section keyword '%s'\n",
cfile.file, cfile.line, cfile.cmd );
err = 1;
while (getcline( &cfile ))
if (!cfile.cmd)
goto reloop;
break;
}
free (t);
}
fclose (cfile.fp);
err |= merge_ops( gcops, &global_mops, &global_sops );
if (!global_sync_state)
global_sync_state = expand_strdup( "~/." EXE "/" );
if (!err && pseudo)
unlink( where );
return err;
}
void
parse_generic_store( store_conf_t *store, conffile_t *cfg, int *err )
{
if (!strcasecmp( "Trash", cfg->cmd ))
store->trash = nfstrdup( cfg->val );
else if (!strcasecmp( "TrashRemoteNew", cfg->cmd ))
store->trash_remote_new = parse_bool( cfg );
else if (!strcasecmp( "TrashNewOnly", cfg->cmd ))
store->trash_only_new = parse_bool( cfg );
else if (!strcasecmp( "MaxSize", cfg->cmd ))
store->max_size = parse_size( cfg );
else if (!strcasecmp( "MapInbox", cfg->cmd ))
store->map_inbox = nfstrdup( cfg->val );
else {
fprintf( stderr, "%s:%d: unknown keyword '%s'\n",
cfg->file, cfg->line, cfg->cmd );
*err = 1;
}
return 0;
}

89
src/cram.c

@ -1,89 +0,0 @@
/* $Id$
*
* isync - IMAP4 to maildir mailbox synchronizer
* 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
* 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
*
* As a special exception, isync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
#include <config.h>
#if HAVE_LIBSSL
#include "isync.h"
#include <assert.h>
#include <string.h>
#include <openssl/hmac.h>
#define ENCODED_SIZE(n) (4*((n+2)/3))
static char
hexchar (unsigned int b)
{
if (b < 10)
return '0' + b;
return 'a' + (b - 10);
}
char *
cram (const char *challenge, const char *user, const char *pass)
{
HMAC_CTX hmac;
char hash[16];
char hex[33];
int i;
unsigned int hashlen = sizeof (hash);
char buf[256];
int len = strlen (challenge);
char *response = calloc (1, 1 + len);
char *final;
/* response will always be smaller than challenge because we are
* decoding.
*/
len = EVP_DecodeBlock ((unsigned char *) response, (unsigned char *) challenge, strlen (challenge));
HMAC_Init (&hmac, (unsigned char *) pass, strlen (pass), EVP_md5 ());
HMAC_Update (&hmac, (unsigned char *) response, strlen(response));
HMAC_Final (&hmac, (unsigned char *) hash, &hashlen);
assert (hashlen == sizeof (hash));
free (response);
hex[32] = 0;
for (i = 0; i < 16; i++)
{
hex[2 * i] = hexchar ((hash[i] >> 4) & 0xf);
hex[2 * i + 1] = hexchar (hash[i] & 0xf);
}
snprintf (buf, sizeof (buf), "%s %s", user, hex);
len = strlen (buf);
len = ENCODED_SIZE (len) + 1;
final = malloc (len);
final[len - 1] = 0;
assert (EVP_EncodeBlock ((unsigned char *) final, (unsigned char *) buf, strlen (buf)) == len - 1);
return final;
}
#endif

102
src/dotlock.c

@ -1,102 +0,0 @@
/* $Id$
*
* isync - IMAP4 to maildir mailbox synchronizer
* Copyright (C) 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
* 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
*
* As a special exception, isync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
/*
* this file contains routines to establish a mutex using a `dotlock' file
*/
#include "dotlock.h"
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#if TESTING
#include <stdio.h>
#endif
int dotlock_lock (const char *path, int *fd)
{
struct flock lck;
*fd = open (path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (*fd == -1)
return -1;
memset (&lck, 0, sizeof(lck));
#if SEEK_SET != 0
lck.l_whence = SEEK_SET;
#endif
#if F_WRLCK != 0
lck.l_type = F_WRLCK;
#endif
if (fcntl (*fd, F_SETLK, &lck))
{
close (*fd);
*fd = -1;
return -1;
}
return 0;
}
int dotlock_unlock (int *fd)
{
int r = 0;
struct flock lck;
if (*fd != -1)
{
memset (&lck, 0, sizeof(lck));
#if SEEK_SET != 0
lck.l_whence = SEEK_SET;
#endif
#if F_UNLCK != 0
lck.l_type = F_UNLCK;
#endif
if (fcntl (*fd, F_SETLK, &lck))
r = -1;
close (*fd);
*fd = -1;
}
return r;
}
#if TESTING
int main (void)
{
int fd;
if (dotlock_lock ("./lock", &fd))
{
perror ("dotlock_lock");
goto done;
}
puts ("sleeping for 5 seconds");
sleep(5);
if (dotlock_unlock (&fd))
{
perror ("dotlock_unlock");
}
done:
exit (0);
}
#endif

25
src/dotlock.h

@ -1,25 +0,0 @@
/* $Id$
*
* isync - IMAP4 to maildir mailbox synchronizer
* Copyright (C) 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
* 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
*
* As a special exception, isync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
int dotlock_lock (const char *, int *);
int dotlock_unlock (int *);

1817
src/drv_imap.c

File diff suppressed because it is too large Load Diff

1153
src/drv_maildir.c

File diff suppressed because it is too large Load Diff

1413
src/imap.c

File diff suppressed because it is too large Load Diff

399
src/isync.h

@ -1,6 +1,5 @@
/* $Id$
*
* isync - IMAP4 to maildir mailbox synchronizer
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
@ -18,219 +17,233 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* As a special exception, isync may be linked with the OpenSSL library,
* As a special exception, mbsync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
#define _GNU_SOURCE
#include <config.h>
#include <sys/types.h>
#include <db.h>
#if HAVE_LIBSSL
# include <openssl/ssl.h>
#include <stdio.h>
#define as(ar) (sizeof(ar)/sizeof(ar[0]))
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_UNUSED __attribute__((unused))
# define ATTR_NORETURN __attribute__((noreturn))
# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
#else
# define ATTR_UNUSED
# define ATTR_NORETURN
# define ATTR_PRINTFLIKE(fmt,var)
#endif
typedef struct
{
int fd;
#if HAVE_LIBSSL
SSL *ssl;
unsigned int use_ssl:1;
#endif
} Socket_t;
typedef struct
{
Socket_t *sock;
int bytes;
int offset;
char buf[1024];
}
buffer_t;
typedef struct config config_t;
typedef struct mailbox mailbox_t;
typedef struct message message_t;
struct config
{
char *maildir;
char *path; /* path relative to .maildir, or absolute path */
char *host;
int port;
char *user;
char *pass;
char *folder;
char *box;
char *inbox;
char *alias;
char *copy_deleted_to;
char *tunnel;
unsigned int max_messages;
off_t max_size;
config_t *next;
#if HAVE_LIBSSL
char *cert_file;
unsigned int use_imaps:1;
unsigned int require_ssl:1;
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;
unsigned int expunge:1;
unsigned int delete:1;
unsigned int wanted:1;
#define EXE "mbsync"
typedef struct {
const char *file;
FILE *fp;
char *buf;
int bufl;
int line;
char *cmd, *val, *rest;
} conffile_t;
#define OP_NEW (1<<0)
#define OP_RENEW (1<<1)
#define OP_DELETE (1<<2)
#define OP_FLAGS (1<<3)
#define OP_MASK_TYPE (OP_NEW|OP_RENEW|OP_DELETE|OP_FLAGS) /* asserted in the target ops */
#define OP_EXPUNGE (1<<4)
#define OP_CREATE (1<<5)
#define XOP_PUSH (1<<6)
#define XOP_PULL (1<<7)
#define XOP_MASK_DIR (XOP_PUSH|XOP_PULL)
#define XOP_HAVE_TYPE (1<<8)
#define XOP_HAVE_EXPUNGE (1<<9)
#define XOP_HAVE_CREATE (1<<10)
typedef struct driver driver_t;
typedef struct store_conf {
struct store_conf *next;
char *name;
driver_t *driver;
const char *path; /* should this be here? its interpretation is driver-specific */
char *map_inbox;
char *trash;
unsigned max_size; /* off_t is overkill */
unsigned trash_remote_new:1, trash_only_new:1;
} store_conf_t;
typedef struct string_list {
struct string_list *next;
char string[1];
} string_list_t;
typedef struct channel_conf {
struct channel_conf *next;
char *name;
store_conf_t *master, *slave;
char *master_name, *slave_name;
char *sync_state;
string_list_t *patterns;
int mops, sops;
unsigned max_messages; /* for slave only */
} channel_conf_t;
typedef struct group_conf {
struct group_conf *next;
char *name;
string_list_t *channels;
} group_conf_t;
/* For message->flags */
/* Keep the mailbox driver flag definitions in sync! */
/* The order is according to alphabetical maildir flag sort */
#define F_DRAFT (1<<0) /* Draft */
#define F_FLAGGED (1<<1) /* Flagged */
#define F_ANSWERED (1<<2) /* Replied */
#define F_SEEN (1<<3) /* Seen */
#define F_DELETED (1<<4) /* Trashed */
#define NUM_FLAGS 5
/* For message->status */
#define M_RECENT (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
#define M_DEAD (1<<1) /* expunged */
#define M_FLAGS (1<<2) /* flags fetched */
#define M_PROCESSED (1<<3) /* registered in pair */
#define M_NOT_SYNCED (1<<4) /* not in remote mailbox, yet */
#define M_EXPIRED (1<<5) /* kicked out by MaxMessages */
typedef struct message {
struct message *next;
/* string_list_t *keywords; */
size_t size; /* zero implies "not fetched" */
int uid;
unsigned char flags, status;
} message_t;
/* For opts, both in store and driver_t->select() */
#define OPEN_OLD (1<<0)
#define OPEN_NEW (1<<1)
#define OPEN_FLAGS (1<<2)
#define OPEN_SIZE (1<<3)
#define OPEN_CREATE (1<<4)
#define OPEN_EXPUNGE (1<<5)
#define OPEN_SETFLAGS (1<<6)
#define OPEN_APPEND (1<<7)
typedef struct store {
store_conf_t *conf; /* foreign */
/* currently open mailbox */
const char *name; /* foreign! maybe preset? */
char *path; /* own */
message_t *msgs; /* own */
int uidvalidity;
unsigned char opts; /* maybe preset? */
/* note that the following do _not_ reflect stats from msgs, but mailbox totals */
int count; /* # of messages */
int recent; /* # of recent messages - don't trust this beyond the initial read */
} store_t;
typedef struct {
char *data;
int len;
unsigned char flags;
unsigned char crlf:1;
} msg_data_t;
#define DRV_OK 0
#define DRV_MSG_BAD -1
#define DRV_BOX_BAD -2
#define DRV_STORE_BAD -3
struct driver {
int (*parse_store)( conffile_t *cfg, store_conf_t **storep, int *err );
store_t *(*open_store)( store_conf_t *conf, store_t *oldctx );
void (*close_store)( store_t *ctx );
int (*list)( store_t *ctx, string_list_t **boxes );
void (*prepare)( store_t *ctx, int opts );
int (*select)( store_t *ctx, int minuid, int maxuid, int *excs, int nexcs );
int (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data );
int (*store_msg)( store_t *ctx, msg_data_t *data, int *uid ); /* if uid is null, store to trash */
int (*set_flags)( store_t *ctx, message_t *msg, int uid, int add, int del ); /* msg can be null, therefore uid as a fallback */
int (*trash_msg)( store_t *ctx, message_t *msg ); /* This may expunge the original message immediately, but it needn't to */
int (*check)( store_t *ctx ); /* IMAP-style: flush */
int (*close)( store_t *ctx ); /* IMAP-style: expunge inclusive */
};
/* struct representing local mailbox file */
struct mailbox
{
DB *db;
char *path;
message_t *msgs;
int lockfd;
unsigned int deleted; /* # of deleted messages */
unsigned int uidvalidity;
unsigned int maxuid; /* largest uid we know about */
unsigned int uidseen : 1; /* flag indicating whether or not we saw a
valid value for UIDVALIDITY */
};
/* message dispositions */
#define D_SEEN (1<<0)
#define D_ANSWERED (1<<1)
#define D_DELETED (1<<2)
#define D_FLAGGED (1<<3)
#define D_RECENT (1<<4)
#define D_DRAFT (1<<5)
#define D_MAX 6
struct message
{
char *file;
unsigned int uid;
unsigned int flags;
size_t size;
message_t *next;
unsigned int processed:1; /* message has already been evaluated */
unsigned int new:1; /* message is in the new/ subdir */
unsigned int dead:1; /* message doesn't exist on the server */
unsigned int wanted:1; /* when using MaxMessages, keep this message */
};
/* main.c */
/* struct used for parsing IMAP lists */
typedef struct _list list_t;
extern int Pid;
extern char Hostname[256];
extern const char *Home;
#define NIL (void*)0x1
#define LIST (void*)0x2
struct _list
{
char *val;
list_t *next;
list_t *child;
};
/* util.c */
/* imap connection info */
typedef struct
{
Socket_t *sock;
unsigned int count; /* # of msgs */
unsigned int recent; /* # of recent messages */
buffer_t *buf; /* input buffer for reading server output */
message_t *msgs; /* list of messages on the server */
config_t *box; /* mailbox to open */
char *prefix; /* namespace prefix */
unsigned int deleted; /* # of deleted messages */
unsigned int uidvalidity;
unsigned int maxuid;
unsigned int minuid;
/* NAMESPACE info */
list_t *ns_personal;
list_t *ns_other;
list_t *ns_shared;
unsigned int caps;
#if HAVE_LIBSSL
unsigned int cram:1;
#endif
}
imap_t;
/* Keep in sync with cap_list */
enum CAPABILITY {
NOLOGIN,
UIDPLUS,
NAMESPACE,
#if HAVE_LIBSSL
CRAM,
STARTTLS,
#endif
};
extern int Verbose, Quiet, Debug;
/* 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 */
void debug( const char *, ... );
void info( const char *, ... );
void infoc( char );
void warn( const char *, ... );
/* flags for maildir_open */
#define OPEN_FAST (1<<0) /* fast open - don't parse */
#define OPEN_CREATE (1<<1) /* create mailbox if nonexistent */
char *next_arg( char ** );
/* flags for imap_open */
#define IMAP_CREATE (1<<0) /* Create remote mailboxes if necessary */
#define IMAP_GET_SIZE (1<<1) /* Request the RFC 822 SIZE */
void add_string_list( string_list_t **list, const char *str );
void free_string_list( string_list_t *list );
void free_generic_messages( message_t * );
extern config_t global;
extern config_t *boxes;
extern char Hostname[256];
extern int Verbose, Quiet;
void strip_cr( msg_data_t *msgdata );
extern void info (const char *, ...);
extern void infoc (char);
extern void warn (const char *, ...);
void *nfmalloc( size_t sz );
void *nfcalloc( size_t sz );
void *nfrealloc( void *mem, size_t sz );
char *nfstrdup( const char *str );
int nfvasprintf( char **str, const char *fmt, va_list va );
int nfasprintf( char **str, const char *fmt, ... );
int nfsnprintf( char *buf, int blen, const char *fmt, ... );
void ATTR_NORETURN oob( void );
#if HAVE_LIBSSL
extern SSL_CTX *SSLContext;
char *expand_strdup( const char *s );
char *cram (const char *, const char *, const char *);
#endif
void sort_ints( int *arr, int len );
char *next_arg (char **);
void arc4_init( void );
unsigned char arc4_getbyte( void );
int sync_mailbox (mailbox_t *, imap_t *, int, unsigned int, unsigned int);
/* sync.c */
void load_config (const char *, int *);
char * expand_strdup (const char *s);
config_t *find_box (const char *);
void imap_close (imap_t *);
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);
int maildir_set_uidvalidity (mailbox_t *, unsigned int uidvalidity);
void maildir_close (mailbox_t *);
int maildir_update_maxuid (mailbox_t * mbox);
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);
int is_atom (list_t *list);
int is_list (list_t *list);
int is_nil (list_t *list);
void free_list (list_t *list);
#define strfcpy(a,b,c) {strncpy(a,b,c);(a)[c-1]=0;}
#define SYNC_OK 0
#define SYNC_FAIL 1
#define SYNC_MASTER_BAD 2
#define SYNC_SLAVE_BAD 3
int sync_boxes( store_t *, const char *,
store_t *, const char *,
channel_conf_t * );
/* config.c */
extern channel_conf_t *channels;
extern group_conf_t *groups;
extern int global_mops, global_sops;
extern char *global_sync_state;
int parse_bool( conffile_t *cfile );
int parse_int( conffile_t *cfile );
int parse_size( conffile_t *cfile );
int getcline( conffile_t *cfile );
int merge_ops( int cops, int *mops, int *sops );
int load_config( const char *filename, int pseudo );
void parse_generic_store( store_conf_t *store, conffile_t *cfg, int *err );
/* drv_*.c */
extern driver_t maildir_driver, imap_driver;

179
src/list.c

@ -1,179 +0,0 @@
/* $Id$
*
* isync - IMAP4 to maildir mailbox synchronizer
* 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
* 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
*
* As a special exception, isync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
#include "isync.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
static char *
skip_string (char *s)
{
while (*s && *s != '"')
s++;
return s;
}
list_t *
parse_list (char *s, char **end)
{
int level = 1;
list_t *cur;
list_t **list;
char *b;
cur = calloc (1, sizeof (list_t));
while (isspace ((unsigned char) *s))
s++;
if (*s == '(')
{
/* start of list. find the end of the list */
s++;
b = s; /* save beginning */
cur->val = LIST;
while (*s)
{
if (*s == '(')
{
level++;
}
else if (*s == ')')
{
level--;
if (level == 0)
break;
}
else if (*s == '"')
{
s = skip_string (s + 1);
if (!*s)
{
/* parse error */
free (cur);
return NULL;
}
}
s++;
}
if (level != 0)
{
free (cur); /* parse error */
return NULL;
}
*s++ = 0;
list = &cur->child;
while (*b)
{
*list = parse_list (b, &b);
if (*list == NULL)
{
/* parse error */
free (cur);
return NULL;
}
while (*list)
list = &(*list)->next;
}
}
else if (*s == '"')
{
/* quoted string */
s++;
cur->val = s;
s = skip_string (s);
if (!*s)
{
/* parse error */
free (cur);
return NULL;
}
*s++ = 0;
cur->val = strdup (cur->val);
}
else
{
/* atom */
cur->val = s;
while (*s && !isspace ((unsigned char) *s))
s++;
if (*s)
*s++ = 0;
if (strcmp ("NIL", cur->val))
cur->val = strdup (cur->val);
else
cur->val = NIL;
}
if (end)
*end = s;
return cur;
}
int
is_atom (list_t * list)
{
return (list && list->val && list->val != NIL && list->val != LIST);
}
int
is_list (list_t * list)
{
return (list && list->val == LIST);
}
int
is_nil (list_t * list)
{
return (list && list->val == NIL);
}
void
free_list (list_t * list)
{
list_t *tmp;
while (list)
{
tmp = list;
list = list->next;
if (is_list (tmp))
free_list (tmp->child);
else if (is_atom (tmp))
free (tmp->val);
free (tmp);
}
}
#if TEST
int
main (int argc, char **argv)
{
char buf[256];
list_t *list;
strcpy (buf,
"((compound list) atom NIL \"string with a (\" (another list))");
list = parse_list (buf, 0);
}
#endif

459
src/maildir.c

@ -1,459 +0,0 @@
/* $Id$
*
* isync - IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2003 Oswald Buddenhagen <ossi@users.sf.net>
*
* 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
*
* As a special exception, isync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
#include "isync.h"
#include "dotlock.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>
/* 2,<flags> */
static void
parse_info (message_t * m, char *s)
{
if (*s == '2' && *(s + 1) == ',')
{
s += 2;
while (*s)
{
if (*s == 'F')
m->flags |= D_FLAGGED;
else if (*s == 'R')
m->flags |= D_ANSWERED;
else if (*s == 'T')
m->flags |= D_DELETED;
else if (*s == 'S')
m->flags |= D_SEEN;
s++;
}
}
}
/*
* There are three possible results of this function:
* >1 uid was already seen
* 0 uid was not yet seen
* -1 unable to read uid because of some other error
*/
static int
read_uid (const char *path, const char *file, unsigned int *uid /* out */)
{
char full[_POSIX_PATH_MAX];
int fd;
int ret = -1;
int len;
char buf[64], *ptr;
snprintf (full, sizeof (full), "%s/%s", path, file);
fd = open (full, O_RDONLY);
if (fd == -1)
{
if (errno != ENOENT)
{
perror (full);
return -1;
}
return 0; /* doesn't exist */
}
len = read (fd, buf, sizeof (buf) - 1);
if (len == -1)
perror ("read");
else
{
buf[len] = 0;
errno = 0;
*uid = strtoul (buf, &ptr, 10);
if (errno)
perror ("strtoul");
else if (ptr && *ptr == '\n')
ret = 1;
/* else invalid value */
}
close (fd);
return ret;
}
/*
* open a maildir mailbox.
* if OPEN_FAST is set, we just check to make
* sure its a valid mailbox and don't actually parse it. any IMAP messages
* with the \Recent flag set are guaranteed not to be in the mailbox yet,
* so we can save a lot of time when the user just wants to fetch new messages
* without syncing the flags.
* if OPEN_CREATE is set, we create the mailbox if it doesn't already exist.
*/
mailbox_t *
maildir_open (const char *path, int flags)
{
char buf[_POSIX_PATH_MAX];
DIR *d;
struct dirent *e;
message_t **cur;
message_t *p;
mailbox_t *m;
char *s;
int count = 0;
struct stat sb;
const char *subdirs[] = { "cur", "new", "tmp" };
int i, ret;
DBT key, value;
m = calloc (1, sizeof (mailbox_t));
m->lockfd = -1;
/* filename expansion happens here, not in the config parser */
m->path = expand_strdup (path);
if (stat (m->path, &sb))
{
if (errno == ENOENT && (flags & OPEN_CREATE))
{
if (mkdir (m->path, S_IRUSR | S_IWUSR | S_IXUSR))
{
fprintf (stderr, "ERROR: mkdir %s: %s (errno %d)\n",
m->path, strerror (errno), errno);
goto err;
}
for (i = 0; i < 3; i++)
{
snprintf (buf, sizeof (buf), "%s/%s", m->path, subdirs[i]);
if (mkdir (buf, S_IRUSR | S_IWUSR | S_IXUSR))
{
fprintf (stderr, "ERROR: mkdir %s: %s (errno %d)\n",
buf, strerror (errno), errno);
goto err;
}
}
}
else
{
fprintf (stderr, "ERROR: stat %s: %s (errno %d)\n", m->path,
strerror (errno), errno);
goto err;
}
}
else
{
/* check to make sure this looks like a valid maildir box */
for (i = 0; i < 3; i++)
{
snprintf (buf, sizeof (buf), "%s/%s", m->path, subdirs[i]);
if (stat (buf, &sb))
{
fprintf (stderr, "ERROR: stat %s: %s (errno %d)\n", buf,
strerror (errno), errno);
fprintf (stderr,
"ERROR: %s does not appear to be a valid maildir style mailbox\n",
m->path);
goto err;
}
}
}
/*
* we need a mutex on the maildir because of the state files that isync
* uses.
*/
snprintf (buf, sizeof (buf), "%s/isynclock", m->path);
if (dotlock_lock (buf, &m->lockfd))
goto err;
/* check for the uidvalidity value */
i = read_uid (m->path, "isyncuidvalidity", &m->uidvalidity);
if (i == -1)
goto err;
else if (i > 0)
m->uidseen = 1;
/* load the current maxuid */
if (read_uid (m->path, "isyncmaxuid", &m->maxuid) == -1)
goto err;
snprintf (buf, sizeof (buf), "%s/isyncuidmap.db", m->path);
if (db_create (&m->db, 0, 0)) {
fputs ("dbcreate failed\n", stderr);
goto err;
}
if ((ret = m->db->set_pagesize (m->db, 4096)) != 0 ||
(ret = m->db->set_h_ffactor (m->db, 40)) != 0 ||
(ret = m->db->set_h_nelem (m->db, 1)) != 0) {
fputs ("Error configuring database\n", stderr);
goto err;
}
m->db->open (m->db, buf, 0, DB_HASH, DB_CREATE, S_IRUSR | S_IWUSR);
if (m->db == NULL)
{
fputs ("ERROR: unable to open UID db\n", stderr);
goto err;
}
if (flags & OPEN_FAST)
return m;
cur = &m->msgs;
for (; count < 2; count++)
{
/* read the msgs from the new subdir */
snprintf (buf, sizeof (buf), "%s/%s", m->path,
(count == 0) ? "new" : "cur");
d = opendir (buf);
if (!d)
{
perror ("opendir");
goto err;
}
while ((e = readdir (d)))
{
if (*e->d_name == '.')
continue; /* skip dot-files */
*cur = calloc (1, sizeof (message_t));
p = *cur;
p->file = strdup (e->d_name);
p->uid = -1;
p->flags = 0;
p->new = (count == 0);
/* determine the UID for this message. The basename (sans
* flags) is used as the key in the db
*/
memset (&key, 0, sizeof(key));
memset (&value, 0, sizeof(value));
key.data = p->file;
s = strchr (p->file, ':');
key.size = s ? (size_t) (s - p->file) : strlen (p->file);
ret = m->db->get (m->db, 0, &key, &value, 0);
if (ret == DB_NOTFOUND) {
/* Every locally generated message triggers this ... */
/*warn ("Warning: no UID for message %.*s\n",
key.size, p->file);*/
} else if (ret) {
fprintf (stderr, "Unexpected error (%d) from db_get(%.*s)\n",
ret, key.size, p->file);
} else if (ret == 0) {
p->uid = *((int *) value.data);
if (p->uid > m->maxuid)
m->maxuid = p->uid;
}
if (s)
parse_info (p, s + 1);
if (p->flags & D_DELETED)
m->deleted++;
cur = &p->next;
}
closedir (d);
}
return m;
err:
if (m->db)
m->db->close (m->db, 0);
dotlock_unlock (&m->lockfd);
free (m->path);
free (m);
return NULL;
}
/* permanently remove messages from a maildir mailbox. if `dead' is nonzero,
* we only remove the messags marked dead.
*/
int
maildir_expunge (mailbox_t * mbox, int dead)
{
message_t **cur = &mbox->msgs;
message_t *tmp;
char *s;
DBT key;
char path[_POSIX_PATH_MAX];
while (*cur)
{
if ((dead == 0 && (*cur)->flags & D_DELETED) ||
(dead && (*cur)->dead))
{
tmp = *cur;
snprintf (path, sizeof (path), "%s/%s/%s",
mbox->path, tmp->new ? "new" : "cur", tmp->file);
if (unlink (path))
perror (path);
/* remove the message from the UID map */
memset (&key, 0, sizeof(key));
key.data = tmp->file;
s = strchr (tmp->file, ':');
key.size = s ? (size_t) (s - tmp->file) : strlen (key.data);
mbox->db->del (mbox->db, 0, &key, 0);
mbox->db->sync (mbox->db, 0);
*cur = (*cur)->next;
free (tmp->file);
free (tmp);
}
else
cur = &(*cur)->next;
}
return 0;
}
int
maildir_update_maxuid (mailbox_t * mbox)
{
int fd;
char buf[64];
size_t len;
char path[_POSIX_PATH_MAX];
int ret = 0;
snprintf (path, sizeof (path), "%s/isyncmaxuid", mbox->path);
fd = open (path, O_WRONLY | O_CREAT, 0600);
if (fd == -1)
{
perror ("open");
return -1;
}
/* write out the file */
snprintf (buf, sizeof (buf), "%u\n", mbox->maxuid);
len = write (fd, buf, strlen (buf));
if (len == (size_t) - 1)
{
perror ("write");
ret = -1;
}
if (close (fd))
ret = -1;
return ret;
}
#define _24_HOURS (3600 * 24)
static void
maildir_clean_tmp (const char *mbox)
{
char path[_POSIX_PATH_MAX];
DIR *dirp;
struct dirent *entry;
struct stat st;
time_t now;
snprintf (path, sizeof (path), "%s/tmp", mbox);
dirp = opendir (path);
if (dirp == NULL)
{
fprintf (stderr, "maildir_clean_tmp: opendir: %s: %s (errno %d)\n",
path, strerror (errno), errno);
return;
}
/* assuming this scan will take less than a second, we only need to
* check the time once before the following loop.
*/
time (&now);
while ((entry = readdir (dirp)))
{
snprintf (path, sizeof (path), "%s/tmp/%s", mbox, entry->d_name);
if (stat (path, &st))
fprintf (stderr, "maildir_clean_tmp: stat: %s: %s (errno %d)\n",
path, strerror (errno), errno);
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.
*/
info ("Notice: removing stale file %s\n", path);
if (unlink (path))
fprintf (stderr,
"maildir_clean_tmp: unlink: %s: %s (errno %d)\n",
path, strerror (errno), errno);
}
}
closedir(dirp);
}
void
maildir_close (mailbox_t * mbox)
{
if (mbox->db)
mbox->db->close (mbox->db, 0);
/* release the mutex on the mailbox */
dotlock_unlock (&mbox->lockfd);
/* per the maildir(5) specification, delivery agents are supposed to
* set a 24-hour timer on items placed in the `tmp' directory.
*/
maildir_clean_tmp (mbox->path);
free (mbox->path);
free_message (mbox->msgs);
memset (mbox, 0xff, sizeof (mailbox_t));
free (mbox);
}
int
maildir_set_uidvalidity (mailbox_t * mbox, unsigned int uidvalidity)
{
char path[_POSIX_PATH_MAX];
char buf[16];
int fd;
int ret;
snprintf (path, sizeof (path), "%s/isyncuidvalidity", mbox->path);
fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd == -1)
{
perror ("open");
return -1;
}
snprintf (buf, sizeof (buf), "%u\n", uidvalidity);
ret = write (fd, buf, strlen (buf));
if (ret == -1)
perror ("write");
else if ((size_t) ret != strlen (buf))
ret = -1;
else
ret = 0;
if (close (fd))
{
perror ("close");
ret = -1;
}
if (ret)
if (unlink (path))
perror ("unlink");
return (ret);
}

863
src/main.c

@ -1,6 +1,5 @@
/* $Id$
*
* isync - IMAP4 to maildir mailbox synchronizer
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
@ -18,92 +17,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* As a special exception, isync may be linked with the OpenSSL library,
* As a special exception, mbsync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
#include "isync.h"
#include <sys/types.h>
#include <sys/stat.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>
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);
}
void
warn (const char *msg, ...)
{
va_list va;
if (Quiet < 2)
{
va_start (va, msg);
vfprintf (stderr, msg, va);
va_end (va);
}
}
#if HAVE_GETOPT_LONG
# define _GNU_SOURCE
# include <getopt.h>
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'},
{"user", 1, NULL, 'u'},
{"version", 0, NULL, 'v'},
{"verbose", 0, NULL, 'V'},
{0, 0, 0, 0}
};
#endif
config_t global;
char Hostname[256];
int Verbose = 0;
int Pid; /* for maildir and imap */
char Hostname[256]; /* for maildir */
const char *Home; /* for config */
static void
version( void )
@ -116,35 +42,34 @@ static void
usage( int code )
{
fputs(
PACKAGE " " VERSION " IMAP4 to maildir synchronizer\n"
PACKAGE " " VERSION " - mailbox synchronizer\n"
"Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>\n"
"Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>\n"
"Copyright (C) 2004 Theodore Ts'o <tytso@mit.edu>\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\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"
" " EXE " [flags] {{channel[:box,...]|group} ...|-a}\n"
" -a, --all operate on all defined channels\n"
" -l, --list list mailboxes instead of syncing them\n"
" -n, --new propagate new messages\n"
" -d, --delete propagate message deletions\n"
" -f, --flags propagate message flag changes\n"
" -N, --renew propagate previously not propagated new messages\n"
" -L, --pull propagate from master to slave\n"
" -H, --push propagate from slave to master\n"
" -C, --create create mailboxes if nonexistent\n"
" -X, --expunge expunge deleted messages\n"
" -c, --config CONFIG read an alternate config file (default: ~/." EXE "rc)\n"
" -D, --debug print debugging messages\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"
"\nIf neither --pull nor --push are specified, both are active.\n"
"If neither --new, --delete, --flags nor --renew are specified, all are active.\n"
"Direction and operation can be concatenated like --pull-new, etc.\n"
"--create and --expunge can be suffixed with -master/-slave. Read the man page.\n"
"\nSupported mailbox formats are: IMAP4rev1, Maildir\n"
"\nCompile time options:\n"
#if HAVE_LIBSSL
" +HAVE_LIBSSL\n"
#else
@ -154,337 +79,535 @@ PACKAGE " " VERSION " IMAP4 to maildir synchronizer\n"
exit( code );
}
char *
next_arg (char **s)
static int
matches( const char *t, const char *p )
{
char *ret;
if (!s)
for (;;) {
if (!*p)
return !*t;
if (*p == '*') {
p++;
do {
if (matches( t, p ))
return 1;
} while (*t++);
return 0;
if (!*s)
} else if (*p == '%') {
p++;
do {
if (*t == '.' || *t == '/') /* this is "somewhat" hacky ... */
return 0;
while (isspace ((unsigned char) **s))
(*s)++;
if (!**s)
{
*s = 0;
if (matches( t, p ))
return 1;
} while (*t++);
return 0;
} else {
if (*p != *t)
return 0;
p++, t++;
}
if (**s == '"')
{
++*s;
ret = *s;
*s = strchr (*s, '"');
}
else
}
static string_list_t *
filter_boxes( string_list_t *boxes, string_list_t *patterns )
{
ret = *s;
while (**s && !isspace ((unsigned char) **s))
(*s)++;
string_list_t *nboxes = 0, *cpat;
const char *ps;
int not, fnot;
for (; boxes; boxes = boxes->next) {
fnot = 1;
for (cpat = patterns; cpat; cpat = cpat->next) {
ps = cpat->string;
if (*ps == '!') {
ps++;
not = 1;
} else
not = 0;
if (matches( boxes->string, ps )) {
fnot = not;
break;
}
}
if (!fnot)
add_string_list( &nboxes, boxes->string );
}
if (*s)
return nboxes;
}
static void
merge_actions( channel_conf_t *chan, int mops, int sops, int have, int mask, int def )
{
if (**s)
*(*s)++ = 0;
if (!**s)
*s = 0;
if (mops & have) {
chan->mops &= ~mask;
chan->mops |= mops & mask;
chan->sops &= ~mask;
chan->sops |= sops & mask;
} else if (!(chan->mops & have)) {
if (global_mops & have) {
chan->mops |= global_mops & mask;
chan->sops |= global_sops & mask;
} else {
chan->mops |= def;
chan->sops |= def;
}
}
return ret;
}
int
main( int argc, char **argv )
{
int i;
int ret;
config_t *box = 0;
mailbox_t *mail = 0;
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;
int all = 0;
int list = 0;
int o2o = 0;
int mbox_open_mode = 0;
int imap_flags = 0;
pw = getpwuid (getuid ());
channel_conf_t *chan;
store_conf_t *mconf, *sconf;
group_conf_t *group;
driver_t *mdriver, *sdriver;
store_t *mctx, *sctx;
string_list_t *umboxes, *usboxes, *mboxes, *sboxes, *mbox, *sbox, **mboxp, **sboxp, *cboxes, *chanptr;
char *config = 0, *channame, *boxlist, *opt, *ochar;
int all = 0, list = 0, cops = 0, mops = 0, sops = 0, gumboxes, gusboxes;
int oind, ret, op, multiple, pseudo = 0;
/* 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.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_tlsv1 = 1;
#endif
#define FLAGS "alCLRc:defhp:qu:r:F:M:1I:s:vV"
gethostname( Hostname, sizeof(Hostname) );
if ((ochar = strchr( Hostname, '.' )))
*ochar = 0;
Pid = getpid();
if (!(Home = getenv("HOME"))) {
fputs( "Fatal: $HOME not set\n", stderr );
return 1;
}
arc4_init();
#if HAVE_GETOPT_LONG
while ((i = getopt_long (argc, argv, FLAGS, Opts, NULL)) != -1)
#else
while ((i = getopt (argc, argv, FLAGS)) != -1)
#endif
{
switch (i)
{
case 'l':
for (oind = 1, ochar = 0; oind < argc; ) {
if (!ochar || !*ochar) {
if (argv[oind][0] != '-')
break;
if (argv[oind][1] == '-') {
opt = argv[oind++] + 2;
if (!*opt)
break;
if (!strcmp( opt, "config" )) {
if (oind >= argc) {
fprintf( stderr, "--config requires an argument.\n" );
return 1;
}
config = argv[oind++];
} else if (!memcmp( opt, "config=", 7 ))
config = opt + 7;
else if (!strcmp( opt, "all" ))
all = 1;
else if (!strcmp( opt, "list" ))
list = 1;
/* plopp */
else if (!strcmp( opt, "help" ))
usage( 0 );
else if (!strcmp( opt, "version" ))
version();
else if (!strcmp( opt, "quiet" ))
Quiet++;
else if (!strcmp( opt, "verbose" )) {
Verbose = 1;
if (!Quiet)
Quiet = 1;
} else if (!strcmp( opt, "debug" )) {
Debug = 1;
if (!Quiet)
Quiet = 1;
} else if (!strcmp( opt, "pull" ))
cops |= XOP_PULL, mops |= XOP_HAVE_TYPE;
else if (!strcmp( opt, "push" ))
cops |= XOP_PUSH, mops |= XOP_HAVE_TYPE;
else if (!memcmp( opt, "create", 6 )) {
opt += 6;
op = OP_CREATE|XOP_HAVE_CREATE;
lcop:
if (!*opt)
cops |= op;
else if (!strcmp( opt, "-master" ))
mops |= op, ochar++;
else if (!strcmp( opt, "-slave" ))
sops |= op, ochar++;
else
goto badopt;
mops |= op & (XOP_HAVE_CREATE|XOP_HAVE_EXPUNGE);
} else if (!memcmp( opt, "expunge", 7 )) {
opt += 7;
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
goto lcop;
} else if (!strcmp( opt, "no-expunge" ))
mops |= XOP_HAVE_EXPUNGE;
else if (!strcmp( opt, "no-create" ))
mops |= XOP_HAVE_CREATE;
else if (!strcmp( opt, "full" ))
mops |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH;
else if (!strcmp( opt, "noop" ))
mops |= XOP_HAVE_TYPE;
else if (!memcmp( opt, "pull", 4 )) {
op = XOP_PULL;
lcac:
opt += 4;
if (!*opt)
cops |= op;
else if (*opt == '-') {
opt++;
goto rlcac;
} else
goto badopt;
} else if (!memcmp( opt, "push", 4 )) {
op = XOP_PUSH;
goto lcac;
} else {
op = 0;
rlcac:
if (!strcmp( opt, "new" ))
op |= OP_NEW;
else if (!strcmp( opt, "renew" ))
op |= OP_RENEW;
else if (!strcmp( opt, "delete" ))
op |= OP_DELETE;
else if (!strcmp( opt, "flags" ))
op |= OP_FLAGS;
else {
badopt:
fprintf( stderr, "Unknown option '%s'\n", argv[oind - 1] );
return 1;
}
switch (op & XOP_MASK_DIR) {
case XOP_PULL: sops |= op & OP_MASK_TYPE; break;
case XOP_PUSH: mops |= op & OP_MASK_TYPE; break;
default: cops |= op; break;
}
mops |= XOP_HAVE_TYPE;
}
continue;
}
ochar = argv[oind++] + 1;
if (!*ochar) {
fprintf( stderr, "Invalid option '-'\n" );
return 1;
}
}
switch (*ochar++) {
case 'a':
all = 1;
break;
case '1':
o2o = 1;
break;
case 'C':
mbox_open_mode |= OPEN_CREATE;
imap_flags |= IMAP_CREATE;
break;
case 'L':
mbox_open_mode |= OPEN_CREATE;
break;
case 'R':
imap_flags |= IMAP_CREATE;
case 'l':
list = 1;
break;
case 'c':
config = optarg;
if (*ochar == 'T') {
ochar++;
pseudo = 1;
}
if (oind >= argc) {
fprintf( stderr, "-c requires an argument.\n" );
return 1;
}
config = argv[oind++];
break;
case 'd':
delete = 1;
case 'C':
op = OP_CREATE|XOP_HAVE_CREATE;
cop:
if (*ochar == 'm')
mops |= op, ochar++;
else if (*ochar == 's')
sops |= op, ochar++;
else if (*ochar == '-')
ochar++;
else
cops |= op;
mops |= op & (XOP_HAVE_CREATE|XOP_HAVE_EXPUNGE);
break;
case 'e':
expunge = 1;
case 'X':
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
goto cop;
case 'F':
cops |= XOP_PULL|XOP_PUSH;
case '0':
mops |= XOP_HAVE_TYPE;
break;
case 'n':
case 'd':
case 'f':
mbox_open_mode |= OPEN_FAST;
fast = 1;
case 'N':
--ochar;
op = 0;
cac:
for (;; ochar++) {
if (*ochar == 'n')
op |= OP_NEW;
else if (*ochar == 'd')
op |= OP_DELETE;
else if (*ochar == 'f')
op |= OP_FLAGS;
else if (*ochar == 'N')
op |= OP_RENEW;
else
break;
case 'p':
global.port = atoi (optarg);
}
if (op & OP_MASK_TYPE)
switch (op & XOP_MASK_DIR) {
case XOP_PULL: sops |= op & OP_MASK_TYPE; break;
case XOP_PUSH: mops |= op & OP_MASK_TYPE; break;
default: cops |= op; break;
}
else
cops |= op;
mops |= XOP_HAVE_TYPE;
break;
case 'L':
op = XOP_PULL;
goto cac;
case 'H':
op = XOP_PUSH;
goto cac;
case 'q':
Quiet++;
Verbose = 0;
break;
case 'r':
global.box = optarg;
break;
case 'F':
global.folder = optarg;
break;
case 'M':
global.maildir = optarg;
break;
case 'I':
global.inbox = optarg;
break;
case 's':
#if HAVE_LIBSSL
if (!strncasecmp ("imaps:", optarg, 6))
{
global.use_imaps = 1;
global.port = 993;
global.use_sslv2 = 1;
global.use_sslv3 = 1;
optarg += 6;
}
#endif
global.host = optarg;
break;
case 'u':
global.user = optarg;
break;
case 'V':
Verbose = 1;
if (!Quiet)
Quiet = 1;
break;
case 'D':
Debug = 1;
if (!Quiet)
Quiet = 1;
break;
case 'v':
version();
case 'h':
usage( 0 );
default:
usage (1);
}
}
if (!argv[optind] && !all)
{
fprintf (stderr, "No mailbox specified. Try isync -h\n");
fprintf( stderr, "Unknown option '-%c'\n", *(ochar - 1) );
return 1;
}
gethostname (Hostname, sizeof (Hostname));
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));
if (merge_ops( cops, &mops, &sops ))
return 1;
}
while ((de = readdir (dir))) {
struct stat st;
char buf[PATH_MAX];
if (*de->d_name == '.')
continue;
if (global.inbox && !strcmp (global.inbox, de->d_name))
continue;
snprintf (buf, sizeof(buf), "%s/%s/cur", global.maildir, de->d_name);
if (stat (buf, &st) || !S_ISDIR (st.st_mode))
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)
if (load_config( config, pseudo ))
return 1;
if (imap_list (imap))
if (!all && !argv[oind]) {
fputs( "No channel specified. Try '" EXE " -h'\n", stderr );
return 1;
}
if (list)
{
for (box = boxes; box; box = box->next)
puts (box->path);
return 0;
if (!channels) {
fputs( "No channels defined. Try 'man " EXE "'\n", stderr );
return 1;
}
ret = 0;
for (box = boxes; (all && box) || (!all && argv[optind]); optind++)
{
if (!all)
{
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.
*/
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;
if (o2o)
global.box =
(global.inbox && !strcmp (global.path, global.inbox)) ?
"INBOX" : global.path;
chan = channels;
chanptr = 0;
mctx = sctx = 0;
mconf = sconf = 0; /* make-gcc-happy */
mdriver = sdriver = 0; /* make-gcc-happy */
gumboxes = gusboxes = 0;
umboxes = usboxes = 0;
if (all)
multiple = channels->next != 0;
else if (argv[oind + 1])
multiple = 1;
else {
multiple = 0;
for (group = groups; group; group = group->next)
if (!strcmp( group->name, argv[oind] )) {
multiple = 1;
break;
}
}
do {
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);
for (;;) {
boxlist = 0;
if (!all) {
if (chanptr)
channame = chanptr->string;
else {
for (group = groups; group; group = group->next)
if (!strcmp( group->name, argv[oind] )) {
chanptr = group->channels;
channame = chanptr->string;
goto gotgrp;
}
channame = argv[oind];
gotgrp: ;
}
if ((boxlist = strchr( channame, ':' )))
*boxlist++ = 0;
for (chan = channels; chan; chan = chan->next)
if (!strcmp( chan->name, channame ))
goto gotchan;
fprintf( stderr, "No channel or group named '%s' defined.\n", channame );
ret = 1;
break;
}
if (box->max_size)
imap_flags |= IMAP_GET_SIZE;
imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap, imap_flags);
if (!imap)
{
fprintf (stderr, "%s: skipping mailbox due to IMAP error\n",
box->path);
goto gotnone;
gotchan: ;
}
merge_actions( chan, mops, sops, XOP_HAVE_TYPE, OP_MASK_TYPE, OP_MASK_TYPE );
merge_actions( chan, mops, sops, XOP_HAVE_CREATE, OP_CREATE, 0 );
merge_actions( chan, mops, sops, XOP_HAVE_EXPUNGE, OP_EXPUNGE, 0 );
mboxes = sboxes = cboxes = 0;
/* possible todo: handle master <-> slave swaps */
if (mctx) {
if (mconf == chan->master)
goto gotmctx;
free_string_list( umboxes );
umboxes = 0;
gumboxes = 0;
if (mconf->driver != chan->master->driver) {
mdriver->close_store( mctx );
mctx = 0;
}
}
mconf = chan->master;
mdriver = mconf->driver;
if (!(mctx = mdriver->open_store( chan->master, mctx ))) {
ret = 1;
break;
}
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))
{
imap_close (imap); /* Just to be safe. Don't really know
* what the problem was.
*/
imap = NULL; /* context no longer valid */
goto next;
}
gotmctx:
if (sctx) {
if (sconf == chan->slave)
goto gotsctx;
free_string_list( usboxes );
usboxes = 0;
gusboxes = 0;
if (sconf->driver != chan->slave->driver) {
sdriver->close_store( sctx );
sctx = 0;
}
}
sconf = chan->slave;
sdriver = sconf->driver;
if (!(sctx = sdriver->open_store( chan->slave, sctx ))) {
ret = 1;
break;
}
if (!fast)
{
if ((expunge || box->expunge) &&
(imap->deleted || mail->deleted))
{
/* remove messages marked for deletion */
info ("Expunging %d messages from server\n", imap->deleted);
if (imap_expunge (imap))
{
imap_close (imap);
imap = NULL;
goto next;
}
gotsctx:
info( "Channel %s\n", chan->name );
if (list && multiple)
printf( "%s:\n", chan->name );
if (boxlist) {
for (boxlist = strtok( boxlist, ",\n" ); boxlist; boxlist = strtok( 0, ",\n" ))
if (list)
puts( boxlist );
else
switch (sync_boxes( mctx, boxlist, sctx, boxlist, chan )) {
case SYNC_MASTER_BAD: goto screwm;
case SYNC_SLAVE_BAD: goto screws;
case SYNC_FAIL: ret = 1;
}
} else if (chan->patterns) {
if (!gumboxes) {
if (mdriver->list( mctx, &umboxes ) != DRV_OK) {
screwm:
mdriver->close_store( mctx );
free_string_list( umboxes );
umboxes = 0;
gumboxes = 0;
mctx = 0;
ret = 1;
break;
}
info ("Expunging %d messages from local mailbox\n",
mail->deleted);
if (maildir_expunge (mail, 0)) {
goto next;
} else {
gumboxes = 1;
if (mctx->conf->map_inbox)
add_string_list( &umboxes, mctx->conf->map_inbox );
}
}
if (!gusboxes) {
if (sdriver->list( sctx, &usboxes ) != DRV_OK) {
screws:
sdriver->close_store( sctx );
free_string_list( usboxes );
usboxes = 0;
gusboxes = 0;
sctx = 0;
ret = 1;
break;
goto next;
} else {
gusboxes = 1;
if (sctx->conf->map_inbox)
add_string_list( &usboxes, sctx->conf->map_inbox );
}
}
mboxes = filter_boxes( umboxes, chan->patterns );
sboxes = filter_boxes( usboxes, chan->patterns );
for (mboxp = &mboxes; (mbox = *mboxp); ) {
for (sboxp = &sboxes; (sbox = *sboxp); sboxp = &sbox->next)
if (!strcmp( sbox->string, mbox->string )) {
*sboxp = sbox->next;
free( sbox );
*mboxp = mbox->next;
mbox->next = cboxes;
cboxes = mbox;
goto gotdupe;
}
mboxp = &mbox->next;
gotdupe: ;
}
for (mbox = cboxes; mbox; mbox = mbox->next)
if (list)
puts( mbox->string );
else
switch (sync_boxes( mctx, mbox->string, sctx, mbox->string, chan )) {
case SYNC_MASTER_BAD: goto screwm;
case SYNC_SLAVE_BAD: goto screws;
case SYNC_FAIL: ret = 1;
}
if ((chan->sops & OP_MASK_TYPE) && (chan->sops & OP_CREATE)) {
for (mbox = mboxes; mbox; mbox = mbox->next)
if (list)
puts( mbox->string );
else
switch (sync_boxes( mctx, mbox->string, sctx, mbox->string, chan )) {
case SYNC_MASTER_BAD: goto screwm;
case SYNC_SLAVE_BAD: goto screws;
case SYNC_FAIL: ret = 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) {
if (maildir_expunge (mail, 1)) {
ret = 1;
break;
}
if ((chan->mops & OP_MASK_TYPE) && (chan->mops & OP_CREATE)) {
for (mbox = sboxes; mbox; mbox = mbox->next)
if (list)
puts( mbox->string );
else
switch (sync_boxes( mctx, mbox->string, sctx, mbox->string, chan )) {
case SYNC_MASTER_BAD: goto screwm;
case SYNC_SLAVE_BAD: goto screws;
case SYNC_FAIL: ret = 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;
} else
if (list)
printf( "%s <=> %s\n", chan->master_name, chan->slave_name );
else
switch (sync_boxes( mctx, chan->master_name, sctx, chan->slave_name, chan )) {
case SYNC_MASTER_BAD: goto screwm;
case SYNC_SLAVE_BAD: goto screws;
case SYNC_FAIL: ret = 1;
}
next:
free_string_list( cboxes );
free_string_list( mboxes );
free_string_list( sboxes );
if (all) {
if (!(chan = chan->next))
break;
} else {
if (chanptr && (chanptr = chanptr->next))
continue;
gotnone:
if (!argv[++oind])
break;
}
/* gracefully close connection to the IMAP server */
imap_close (imap);
}
free_string_list( usboxes );
if (sctx)
sdriver->close_store( sctx );
free_string_list( umboxes );
if (mctx)
mdriver->close_store( mctx );
return ret;
}

471
src/mbsync.1

@ -0,0 +1,471 @@
.ig
\" mbsync - mailbox synchronizer
\" Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
\" Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
\" Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
\"
\" 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
\"
\" As a special exception, mbsync may be linked with the OpenSSL library,
\" despite that library's more restrictive license.
..
.TH mbsync 1 "2004 Mar 27"
..
.SH NAME
mbsync - synchronize IMAP4 and Maildir mailboxes
..
.SH SYNOPSIS
\fBmbsync\fR [\fIoptions\fR ...] {{\fIchannel\fR[\fB:\fIbox\fR[{\fB,\fR|\fB\\n\fR}...]]|\fIgroup\fR} ...|\fB-a\fR}
..
.SH DESCRIPTION
\fBmbsync\fR is a command line application which synchronizes mailboxes;
currently Maildir and IMAP4 mailboxes are supported.
New messages, message deletions and flag changes can be propagated both ways;
the operation set can be selected in a fine-grained manner.
.br
Synchronization is based on unique message identifiers (UIDs), so no
identification conflicts can occur (as opposed to some other mail synchronizers).
OTOH, \fBmbsync\fR is susceptible to UID validity changes (that \fIshould\fR
never happen, but see "Compatibility" in the README).
Synchronization state is kept in one local text file per mailbox pair;
multiple replicas of a mailbox can be maintained.
..
.SH OPTIONS
.TP
\fB-c\fR, \fB--config\fR \fIfile\fR
Read configuration from \fIfile\fR.
By default, the configuration is read from ~/.mbsyncrc.
.TP
\fB-a\fR, \fB--all\fR
Select all configured channels. Any channel/group specifications on the command
line are ignored.
.TP
\fB-l\fR, \fB--list\fR
Don't synchronize anything, but list all mailboxes in the selected channels
and exit.
.TP
\fB-C\fR[\fBm\fR][\fBs\fR], \fB--create\fR[\fB-master\fR|\fB-slave\fR]
Override any \fBCreate\fR options from the config file. See below.
.TP
\fB-X\fR[\fBm\fR][\fBs\fR], \fB--expunge\fR[\fB-master\fR|\fB-slave\fR]
Override any \fBExpunge\fR options from the config file. See below.
.TP
{\fB-n\fR|\fB-N\fR|\fB-d\fR|\fB-f\fR|\fB-0\fR|\fB-F\fR},\
{\fB--new\fR|\fB--renew\fR|\fB--delete\fR|\fB--flags\fR|\fB--noop\fR|\fB--full\fR}
.TP
\r{\fB-L\fR|\fB-H\fR}[\fBn\fR][\fBN\fR][\fBd\fR][\fBf\fR],\
{\fB--pull\fR|\fB--push\fR}[\fB-new\fR|\fB-renew\fR|\fB-delete\fR|\fB-flags\fR]
Override any \fBSync\fR options from the config file. See below.
.TP
\fB-h\fR, \fB--help\fR
Display a summary of command line options.
.TP
\fB-v\fR, \fB--version\fR
Display version information.
.TP
\fB-V\fR, \fB--verbose\fR
Enable \fIverbose\fR mode, which displays the IMAP4 network traffic.
.TP
\fB-D\fR, \fB--debug\fR
Enable printing \fIdebug\fR information.
.TP
\fB-q\fR, \fB--quiet\fR
Suppress informational messages.
If specified twice, suppress warning messages as well.
..
.SH CONFIGURATION
The configuration file is mandatory; \fBmbsync\fR will not run without it.
Lines starting with a hash mark (\fB#\fR) are comments and are ignored entirely.
Configuration items are keywords followed by one or more arguments;
arguments containing spaces must be enclosed in double quotes (\fB"\fR).
All keywords (including those used as arguments) are case-insensitive.
There are a few global options, the rest applies to particular sections.
Sections are started by a section keyword and are terminated by an empty line
or end of file.
Every section defines an object with a an identifier unique within that
object class.
.P
There are two basic object classes: Stores and Channels. A Store defines
a collection of mailboxes; basically a folder, either local or remote.
A Channel connects two Stores, describing the way the two are synchronized.
.br
There are two auxiliary object classes: Accounts and Groups. An Account
describes the connection part of remote Stores, so a server connection can be
shared between multiple Stores. A Group aggregates multiple Channels to
save typing on the command line.
..
.SS All Stores
These options can be used in all supported Store types.
.br
In this context, the term "remote" describes the second Store within a Channel,
and not necessarily a remote server.
.br
The special mailbox \fBINBOX\fR exists in every Store; its physical location
in the file system is Store type specific.
..
.TP
\fBPath\fR \fIpath\fR
The location of the Store in the (server's) file system.
If this is no absolute path, the reference point is Store type specific.
This string is prepended to the mailbox names addressed in this Store,
but is not considered part of them; this is important for \fBPatterns\fR
in the Channels section.
Note that you \fBmust\fR append a slash if you want to specify an entire
directory.
(Default: \fI""\fR)
..
.TP
\fBMaxSize\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR]
Messages larger than that will not be propagated into this Store.
This is useful for weeding out messages with large attachments.
\fBK\fR and \fBM\fR can be appended to the size to specify KiBytes resp.
MeBytes instead of bytes. \fBB\fR is accepted but superflous.
If \fIsize\fR is 0, the maximum message size is \fBunlimited\fR.
(Default: \fI0\fR)
..
.TP
\fBMapInbox\fR \fImailbox\fR
Create a virtual mailbox (relative to \fBPath\fR), which is backed by
the \fBINBOX\fR. Makes sense in conjunction with \fBPatterns\fR in the
Channels section.
..
.TP
\fBTrash\fR \fImailbox\fR
Specifies a mailbox (relative to \fBPath\fR) to copy deleted messages to
prior to expunging. See \fBINHERENT PROBLEMS\fR below.
(Default: none)
..
.TP
\fBTrashNewOnly\fR \fIyes\fR|\fIno\fR
When trashing, copy only not yet propagated messages. This makes sense if the
remote Store has a \fBTrash\fR as well (with \fBTrashNewOnly\fR \fIno\fR).
(Default: \fIno\fR)
..
.TP
\fBTrashRemoteNew\fR \fIyes\fR|\fIno\fR
When expunging the remote Store, copy not yet propagated messages to this
Store's \fBTrash\fR. When using this, the remote Store does not need an own
\fBTrash\fR at all, yet all messages are archived.
(Default: \fIno\fR)
..
.SS Maildir Stores
The reference point for relative \fBPath\fRs is $HOME.
.P
As \fBmbsync\fR needs UIDs, but no standardized UID storage scheme exists for
Maildir, \fBmbsync\fR supports two schemes, each with its pros and cons.
.br
The \fBnative\fR scheme is stolen from the latest Maildir patches to \fBc-client\fR
and is therefore compatible with \fBpine\fR. The UID validity is stored in a
file named .uidvalidity; the UIDs are encoded in the file names of the messages.
.br
The \fBalternative\fR scheme is based on the UID mapping used by \fBisync\fR
versions 0.8 and 0.9.x. The invariant parts of the file names of the messages
are used as keys into a Berkeley database named .isyncuidmap.db, which holds
the UID validity as well.
.br
The \fBnative\fR scheme is faster, more space efficient, endianess independent
and "human readable", but will be disrupted if a message is copied from another
mailbox without getting a new file name; this would result in duplicated UIDs
sooner or later, which in turn results in a UID validity change, making
synchronization fail.
The \fBalternative\fR scheme would fail if a MUA changed a message's file name
in a part \fBmbsync\fR considers invariant; this would be interpreted as a
message deletion and a new message, resulting in unnecessary traffic.
.br
\fBMutt\fR is known to work fine with both schemes.
.br
Use \fBmdconvert\fR to convert mailboxes from one scheme to the other.
..
.TP
\fBMaildirStore\fR \fIname\fR
Define the Maildir Store \fIname\fR, opening a section for its parameters.
..
.TP
\fBAltMap\fR \fIyes\fR|\fIno\fR
Use the \fBalternative\fR UID storage scheme for mailboxes in this Store.
This does not affect mailboxes that do already have a UID storage scheme;
use \fBmdconvert\fR to change it.
(Default: \fIno\fR)
..
.TP
\fBInbox\fR \fIpath\fR
The location of the \fBINBOX\fR. This is \fInot\fR relative to \fBPath\fR.
(Default: \fI~/Maildir\fR)
..
.SS IMAP4 Accounts
.TP
\fBIMAPAccount\fR \fIname\fR
Define the IMAP4 Account \fIname\fR, opening a section for its parameters.
..
.TP
\fBHost\fR [\fBimaps:\fR]\fIhost\fR
Specify the DNS name or IP address of the IMAP server. If \fIhost\fR is
prefixed with \fBimaps:\fR the connection is assumed to be an SSL connection
to port 993.
Note that modern servers support SSL on the default port 143 via the
STARTTLS extension, which will be used automatically by default.
..
.TP
\fBPort\fR \fIport\fR
Specify the TCP port number of the IMAP server. (Default: 143 for imap,
993 for imaps)
..
.TP
\fBUser\fR \fIusername\fR
Specify the login name on the IMAP server. (Default: current local user)
..
.TP
\fBPass\fR \fIpassword\fR
Specify 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, \fBmbsync\fR
will prompt you for it.
..
.TP
\fBTunnel\fR \fIcommand\fR
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. This makes most other IMAPAccount options superflous.
..
.TP
\fBRequireCRAM\fR \fIyes\fR|\fIno\fR
If set to \fIyes\fR, \fBmbsync\fR will abort the connection if no CRAM-MD5
authentication is possible. (Default: \fIno\fR)
..
.TP
\fBRequireSSL\fR \fIyes\fR|\fIno\fR
\fBmbsync\fR will abort the connection if a TLS/SSL session cannot be
established with the IMAP server. (Default: \fIyes\fR)
..
.TP
\fBCertificateFile\fR \fIpath\fR
File containing X.509 CA certificates used to verify server identities.
This option is \fImandatory\fR if SSL is used. See \fBSSL CERTIFICATES\fR below.
..
.TP
\fBUseSSLv2\fR \fIyes\fR|\fIno\fR
Use SSLv2 for communication with the IMAP server over SSL?
(Default: \fIyes\fR if an imaps \fBHost\fR is used, otherwise \fIno\fR)
..
.TP
\fBUseSSLv3\fR \fIyes\fR|\fIno\fR
Use SSLv3 for communication with the IMAP server over SSL?
(Default: \fIyes\fR if an imaps \fBHost\fR is used, otherwise \fIno\fR)
..
.TP
\fBUseTLSv1\fR \fIyes\fR|\fIno\fR
Use TLSv1 for communication with the IMAP server over SSL?
(Default: \fIyes\fR)
..
.SS IMAP Stores
The reference point for relative \fBPath\fRs is whatever the server likes it
to be; probably the user's $HOME or $HOME/Mail on that server. The location
of \fBINBOX\fR is up to the server as well and is usually irrelevant.
.TP
\fBIMAPStore\fR \fIname\fR
Define the IMAP4 Store \fIname\fR, opening a section for its parameters.
..
.TP
\fBAccount\fR \fIaccount\fR
Specify which IMAP4 Account to use. Instead of defining an Account and
referencing it here, it is also possible to specify all the Account options
directly in the Store's section - this makes sense if an Account is used for
one Store only anyway.
..
.TP
\fBUseNamespace\fR \fIyes\fR|\fIno\fR
Selects whether the server's first "personal" NAMESPACE should be prefixed to
mailbox names. Disabling this makes sense for some broken IMAP servers.
This option is meaningless if a \fBPath\fR was specified.
(Default: \fIyes\fR)
..
.SS Channels
.TP
\fBChannel\fR \fIname\fR
Define the Channel \fIname\fR, opening a section for its parameters.
..
.TP
{\fBMaster\fR|\fBSlave\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
Specify the Master resp. Slave Store to be connected by this Channel.
If \fImailbox\fR is omitted, \fBINBOX\fR is assumed.
The \fImailbox\fR specification can be overridden by \fBPatterns\fR, which
in turn can be overridden by a mailbox list in a Channel reference (a Group
specification or the command line).
..
.TP
\fBPattern\fR[\fBs\fR] [\fB!\fR]\fIpattern\fR ...
Instead of synchronizing only one mailbox pair, synchronize all mailboxes
that match the \fIpattern\fR(s). The mailbox names are the same on both
Master and Slave. Patterns are IMAP4 patterns, i.e., \fB*\fR matches anything
and \fB%\fR matches anything up to the next hierarchy delimiter. Prepending
\fB!\fR to a pattern makes it an exclusion. Multiple patterns can be specified
(either by supplying multiple arguments or by using \fBPattern\fR multiple
times); later matches take precedence.
Example: "\fBPatterns\fR\ \fI%\ !Trash\fR"
..
.TP
\fBMaxSize\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR]
Analogous to the homonymous option in the Stores section, but applies equally
to Master and Slave. Note that this actually modifies the Stores, so take care
not to provide conflicting settings if you use the Stores in multiple Channels.
..
.TP
\fBMaxMessages\fR \fIcount\fR
Sets the maximum number of messages to keep in each Slave 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 (independently of
the actual date of the message) will be deleted first.
Messages that are flagged (marked as important) and recent messages will not
be automatically deleted.
If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR
(Default: \fI0\fR).
..
.TP
\fBSync\fR {\fINone\fR|[\fIPull\fR] [\fIPush\fR] [\fINew\fR] [\fIReNew\fR] [\fIDelete\fR] [\fIFlags\fR]|\fIFull\fR}
Select the synchronization operation(s) to perform:
.br
\fBPull\fR - propagate changes from Master to Slave.
.br
\fBPush\fR - propagate changes from Slave to Master.
.br
\fBNew\fR - propagate newly appeared messages.
.br
\fBReNew\fR - previously refused messages are re-evaluated for propagation.
Useful after flagging affected messages in the source Store or enlarging
MaxSize in the destination Store.
.br
\fBDelete\fR - propagate message deletions. This applies only to messages that
are actually gone, i.e., were expunged. The affected messages in the remote
Store are marked as deleted only, i.e., they won't be really deleted until
that Store is expunged.
.br
\fBFlags\fR - propagate flag changes. Note that Deleted/Trashed is a flag as
well; this is particularily interesting if you use \fBmutt\fR with the
maildir_trash option.
.br
\fBAll\fR (\fB--full\fR on the command line) - all of the above.
This is the global default.
.br
\fBNone\fR (\fB--noop\fR on the command line) - don't propagate anything.
Useful if you want to expunge only.
.IP
\fBPull\fR and \fBPush\fR are direction flags, while \fBNew\fR, \fBReNew\fR,
\fBDelete\fR and \fBFlags\fR are type flags. The two flag classes make up a
two-dimensional matrix (a table). Its cells are the individual actions to
perform. There are two styles of asserting the cells:
.br
In the first style, the flags select entire rows/colums in the matrix. Only
the cells which are selected both horizontally and vertically are asserted.
Specifying no flags from a class is like specifying all flags from this class.
For example, "\fBSync\fR\ \fBPull\fR\ \fBNew\fR\ \fBFlags\fR" will propagate
new messages and flag changes from the Master to the Slave,
"\fBSync\fR\ \fBNew\fR\ \fBDelete\fR" will propagate message arrivals and
deletions both ways, and "\fBSync\fR\ \fBPush\fR" will propagate all changes
from the Slave to the Master.
.br
In the second style, direction flags are concatenated with type flags; every
compound flag immediately asserts a cell in the matrix. In addition to at least
one compound flag, the individual flags can be used as well, but as opposed to
the first style, they immediately assert all cells in their respective
row/column. For example,
"\fBSync\fR\ \fBPullNew\fR\ \fBPullDelete\fR\ \fBPush\fR" will propagate
message arrivals and deletions from the Master to the Slave and any changes
from the Slave to the Master.
Note that it is not allowed to assert a cell in two ways, e.g.
"\fBSync\fR\ \fBPullNew\fR\ \fBPull\fR" and
"\fBSync\fR\ \fBPullNew\fR\ \fBDelete\fR\ \fBPush\fR" induce error messages.
..
.TP
\fBCreate\fR {\fINone\fR|\fIMaster\fR|\fISlave\fR|\fIBoth\fR}
Automatically create missing mailboxes [on the Master/Slave].
Otherwise print an error message and skip that mailbox pair if a mailbox
does not exist.
(Global default: \fINone\fR)
..
.TP
\fBExpunge\fR {\fINone\fR|\fIMaster\fR|\fISlave\fR|\fIBoth\fR}
Permanently remove all messages [on the Master/Slave] marked for deletion.
(Global default: \fINone\fR)
..
.P
\fBSync\fR, \fBCreate\fR and \fBExpunge\fR can be used outside any section for
a global effect. The global settings are overridden by Channel-specific options,
which in turn are overridden by command line switches.
..
.TP
\fBSyncState\fR {\fB*\fR|\fIpath\fR}
Set the location of this Channel's synchronization state files. \fB*\fR means
that the state should be saved in a file named .mbsyncstate in the
Slave mailbox itself; this has the advantage that you needn't to care for the
state file if you delete the mailbox, but it works only with Maildir mailboxes,
obviously. Otherwise this is interpreted as a string to prepend to the Slave
mailbox name to make up a complete path.
.br
This option can be used outside any section for a global effect. In this case
the appended string is made up according to the pattern
\fB:\fImaster\fB:\fImaster-box\fB_:\fIslave\fB:\fIslave-box\fR.
.br
(Global default: \fI~/.mbsync/\fR).
..
.SS Groups
.TP
\fBGroup\fR \fIname\fR [\fIchannel\fR[\fB:\fIbox\fR[\fB,\fR...]]] ...
Define the Group \fIname\fR, opening a section for its parameters.
Note that even though Groups have an own namespace, they will "hide" Channels
with the same name on the command line.
.br
One or more Channels can be specified on the same line.
.br
If you supply one or more \fIbox\fRes to a \fIchannel\fR, they will be used
instead of what is specified in the Channel. The same can be done on the command
line, except that there newlines can be used as mailbox name separators as well.
..
.TP
\fBChannel\fR[\fBs\fR] \fIchannel\fR[\fB:\fIbox\fR[\fB,\fR...]] ...
Add the specified channels to the group. This option can be specified multiple
times within a Group.
..
.SH SSL CERTIFICATES
[to be done]
..
.SH INHERENT PROBLEMS
Changes done after \fBmbsync\fR has retrieved the message list will not be
synchronised until the next time \fBmbsync\fR is invoked.
.P
Using \fBTrash\fR on IMAP Stores bears a race condition: messages will be
lost if they are marked as deleted after the message list was retrieved but
before the mailbox is expunged. There is no risk as long as the IMAP mailbox
is not simultaneously accessed by \fBmbsync\fR and another mail client.
..
.SH FILES
.TP
.B ~/.mbsyncrc
Default configuration file
.TP
.B ~/.mbsync/
Directory containing synchronization state files
..
.SH SEE ALSO
mdconvert(1), isync(1), mutt(1), maildir(5)
.P
Up to date information on \fBmbsync\fR can be found at http://isync.sf.net/
..
.SH AUTHOR
Written by Michael R. Elkins <me@mutt.org>,
.br
rewritten and maintained by Oswald Buddenhagen <ossi@users.sf.net>,
.br
contributions by Theodore Y. Ts'o <tytso@mit.edu>.

82
src/mbsyncrc.sample

@ -0,0 +1,82 @@
# Global configuration section
# Values here are used as defaults for any following Channel section that
# doesn't specify them.
Expunge None
Create Both
MaildirStore local
Path ~/Mail/
Trash Trash
IMAPStore work
Host work.host.com
Pass xxxxxxxx
CertificateFile /etc/ssl/certs/ca-certificates.crt
Channel work
Master :work:
Slave :local:work
Expunge Slave
Sync PullNew Push
IMAPStore personal
Host host.play.com
Port 6789
RequireSSL no
Channel personal
Master :personal:
Slave :local:personal
Expunge Both
MaxMessages 150
MaxSize 200k
IMAPStore remote
Tunnel "ssh -q host.remote.com /usr/sbin/imapd"
Channel remote
Master :remote:
Slave :local:remote
Group boxes
Channels work personal remote
IMAPStore st1
Host st1.domain.com
RequireCRAM yes
CertificateFile ~/.st1-certificate.crt
IMAPStore st2
Host imap.another-domain.com
Path non-standard/
RequireSSL no
UseTLSv1 no
Channel rst
Master :st1:somebox
Slave :st2:
IMAPAccount server
Host imaps:foo.bar.com
CertificateFile ~/.server-certificate.crt
IMAPStore server
Account server
MapInbox inbox
Trash ~/trash
TrashRemoteNew yes
MaildirStore mirror
Path ~/Maildir/
Channel o2o
Master :server:
Slave :mirror:
Patterns %
Group partial o2o:inbox,sent-mail,foobar

51
src/mdconvert.1

@ -0,0 +1,51 @@
.ig
\" mdconvert - Maildir mailbox UID storage scheme converter
\" Copyright (C) 2004 Oswald Buddenhagen <ossi@users.sf.net>
\"
\" 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
..
.TH mdconvert 1 "2004 Mar 27"
..
.SH NAME
mdconvert - Maildir mailbox UID storage scheme converter
..
.SH SYNOPSIS
\fBmdconvert\fR [\fIoptions\fR ...] \fImailbox\fR ...
..
.SH DESCRIPTION
\fBmdconvert\fR converts Maildir mailboxes between the two UID storage schemes
supported by \fBmbsync\fR. See \fBmbsync\fR's manual page for details on these
schemes.
..
.SH OPTIONS
.TP
\fB-a\fR, \fB--alt\fR
Convert to the \fBalternative\fR (Berkeley DB based) UID storage scheme.
.TP
\fB-n\fR, \fB--native\fR
Convert to the \fBnative\fR (file name based) UID storage scheme.
This is the default.
.TP
\fB-h\fR, \fB--help\fR
Displays a summary of command line options.
.TP
\fB-v\fR, \fB--version\fR
Displays version information.
..
.SH SEE ALSO
mbsync(1)
..
.SH AUTHOR
Written and maintained by Oswald Buddenhagen <ossi@users.sf.net>.

256
src/mdconvert.c

@ -0,0 +1,256 @@
/*
* mdconvert - Maildir UID scheme converter
* Copyright (C) 2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* 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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <dirent.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <db.h>
#define EXE "mdconvert"
static int
nfsnprintf( char *buf, int blen, const char *fmt, ... )
{
int ret;
va_list va;
va_start( va, fmt );
if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen) {
fputs( "Fatal: buffer too small. Please report a bug.\n", stderr );
abort();
}
va_end( va );
return ret;
}
static const char *subdirs[] = { "cur", "new" };
static struct flock lck;
static DBT key, value;
static inline int
convert( const char *box, int altmap )
{
DB *db;
DIR *d;
struct dirent *e;
const char *u, *ru;
char *p, *s, *dpath, *spath, *dbpath;
int i, n, ret, sfd, dfd, bl, ml, uv[2], uid;
struct stat st;
char buf[_POSIX_PATH_MAX], buf2[_POSIX_PATH_MAX];
char umpath[_POSIX_PATH_MAX], uvpath[_POSIX_PATH_MAX], tdpath[_POSIX_PATH_MAX];
if (stat( box, &st ) || !S_ISDIR(st.st_mode)) {
fprintf( stderr, "'%s' is no Maildir mailbox.\n", box );
return 1;
}
nfsnprintf( umpath, sizeof(umpath), "%s/.isyncuidmap.db", box );
nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", box );
if (altmap)
dpath = umpath, spath = uvpath, dbpath = tdpath;
else
spath = umpath, dpath = uvpath, dbpath = umpath;
nfsnprintf( tdpath, sizeof(tdpath), "%s.tmp", dpath );
if ((sfd = open( spath, O_RDWR )) < 0) {
if (errno != ENOENT)
perror( spath );
return 1;
}
if (fcntl( sfd, F_SETLKW, &lck )) {
perror( spath );
goto sbork;
}
if ((dfd = open( tdpath, O_RDWR|O_CREAT, 0600 )) < 0) {
perror( tdpath );
goto sbork;
}
if (db_create( &db, 0, 0 )) {
fputs( "Error: db_create() failed\n", stderr );
goto tbork;
}
if ((ret = db->open( db, 0, dbpath, 0, DB_HASH, DB_CREATE, 0 ))) {
db->err( db, ret, "Error: db->open(%s)", dbpath );
dbork:
db->close( db, 0 );
tbork:
unlink( tdpath );
close( dfd );
sbork:
close( sfd );
return 1;
}
key.data = (void *)"UIDVALIDITY";
key.size = 11;
if (altmap) {
if ((n = read( sfd, buf, sizeof(buf) )) <= 0 ||
(buf[n] = 0, sscanf( buf, "%d\n%d", &uv[0], &uv[1] ) != 2))
{
fprintf( stderr, "Error: cannot read UIDVALIDITY of '%s'.\n", box );
goto dbork;
}
value.data = uv;
value.size = sizeof(uv);
if ((ret = db->put( db, 0, &key, &value, 0 ))) {
db->err( db, ret, "Error: cannot write UIDVALIDITY for '%s'", box );
goto dbork;
}
} else {
if ((ret = db->get( db, 0, &key, &value, 0 ))) {
db->err( db, ret, "Error: cannot read UIDVALIDITY of '%s'", box );
goto dbork;
}
n = sprintf( buf, "%d\n%d\n", ((int *)value.data)[0], ((int *)value.data)[1] );
if (write( dfd, buf, n ) != n) {
fprintf( stderr, "Error: cannot write UIDVALIDITY for '%s'.\n", box );
goto dbork;
}
}
again:
for (i = 0; i < 2; i++) {
bl = nfsnprintf( buf, sizeof(buf), "%s/%s/", box, subdirs[i] );
if (!(d = opendir( buf ))) {
perror( "opendir" );
goto dbork;
}
while ((e = readdir( d ))) {
if (*e->d_name == '.')
continue;
nfsnprintf( buf + bl, sizeof(buf) - bl, "%s", e->d_name );
memcpy( buf2, buf, bl );
p = strstr( e->d_name, ",U=" );
if (p)
for (u = p, ru = p + 3; isdigit( (unsigned char)*ru ); ru++);
else
u = ru = strchr( e->d_name, ':' );
if (u)
ml = u - e->d_name;
else
ru = "", ml = INT_MAX;
if (altmap) {
if (!p)
continue;
key.data = e->d_name;
key.size = (size_t)(strchr( e->d_name, ',' ) - e->d_name);
uid = atoi( p + 3 );
value.data = &uid;
value.size = sizeof(uid);
if ((ret = db->put( db, 0, &key, &value, 0 ))) {
db->err( db, ret, "Error: cannot write UID for '%s'", box );
goto ebork;
}
nfsnprintf( buf2 + bl, sizeof(buf2) - bl, "%.*s%s", ml, e->d_name, ru );
} else {
s = strpbrk( e->d_name, ",:" );
key.data = e->d_name;
key.size = s ? (size_t)(s - e->d_name) : strlen( e->d_name );
if ((ret = db->get( db, 0, &key, &value, 0 ))) {
if (ret != DB_NOTFOUND) {
db->err( db, ret, "Error: cannot read UID for '%s'", box );
goto ebork;
}
continue;
}
uid = *(int *)value.data;
nfsnprintf( buf2 + bl, sizeof(buf2) - bl, "%.*s,U=%d%s", ml, e->d_name, uid, ru );
}
if (rename( buf, buf2 )) {
if (errno == ENOENT)
goto again;
perror( buf );
ebork:
closedir( d );
goto dbork;
}
}
closedir( d );
}
db->close( db, 0 );
close( dfd );
if (rename( tdpath, dpath )) {
perror( dpath );
return 1;
}
if (unlink( spath ))
perror( spath );
close( sfd );
return 0;
}
int
main( int argc, char **argv )
{
int oint, ret, altmap = 0;
for (oint = 1; oint < argc; oint++) {
if (!strcmp( argv[oint], "-h" ) || !strcmp( argv[oint], "--help" )) {
puts(
"Usage: " EXE " [-a] mailbox...\n"
" -a, --alt convert to alternative (DB based) UID scheme\n"
" -n, --native convert to native (file name based) UID scheme (default)\n"
" -h, --help show this help message\n"
" -v, --version display version"
);
return 0;
} else if (!strcmp( argv[oint], "-v" ) || !strcmp( argv[oint], "--version" )) {
puts( EXE " " VERSION " - Maildir UID scheme converter" );
return 0;
} else if (!strcmp( argv[oint], "-a" ) || !strcmp( argv[oint], "--alt" )) {
altmap = 1;
} else if (!strcmp( argv[oint], "-n" ) || !strcmp( argv[oint], "--native" )) {
altmap = 0;
} else if (!strcmp( argv[oint], "--" )) {
oint++;
break;
} else if (argv[oint][0] == '-') {
fprintf( stderr, "Unrecognized option '%s'. Try " EXE " -h\n", argv[oint] );
return 1;
} else
break;
}
if (oint == argc) {
fprintf( stderr, "Mailbox specification missing. Try " EXE " -h\n" );
return 1;
}
#if SEEK_SET != 0
lck.l_whence = SEEK_SET;
#endif
#if F_WRLCK != 0
lck.l_type = F_WRLCK;
#endif
ret = 0;
for (; oint < argc; oint++)
ret |= convert( argv[oint], altmap );
return ret;
}

1238
src/sync.c

File diff suppressed because it is too large Load Diff

389
src/util.c

@ -0,0 +1,389 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* 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
*
* As a special exception, mbsync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
#include "isync.h"
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pwd.h>
#include <ctype.h>
int Verbose, Quiet, Debug;
void
debug( const char *msg, ... )
{
va_list va;
if (Debug) {
va_start( va, msg );
vprintf( msg, va );
va_end( va );
}
}
void
info( const char *msg, ... )
{
va_list va;
if (!Quiet) {
va_start( va, msg );
vprintf( msg, va );
va_end( va );
fflush( stdout );
}
}
void
infoc( char c )
{
if (!Quiet) {
putchar( c );
fflush( stdout );
}
}
void
warn( const char *msg, ... )
{
va_list va;
if (Quiet < 2) {
va_start( va, msg );
vfprintf( stderr, msg, va );
va_end( va );
}
}
char *
next_arg( char **s )
{
char *ret;
if (!s || !*s)
return 0;
while (isspace( (unsigned char) **s ))
(*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;
}
return ret;
}
void
add_string_list( string_list_t **list, const char *str )
{
string_list_t *elem;
int len;
len = strlen( str );
elem = nfmalloc( sizeof(*elem) + len );
elem->next = *list;
*list = elem;
memcpy( elem->string, str, len + 1 );
}
void
free_string_list( string_list_t *list )
{
string_list_t *tlist;
for (; list; list = tlist) {
tlist = list->next;
free( list );
}
}
void
free_generic_messages( message_t *msgs )
{
message_t *tmsg;
for (; msgs; msgs = tmsg) {
tmsg = msgs->next;
free( msgs );
}
}
void
strip_cr( msg_data_t *msgdata )
{
int i, o;
if (msgdata->crlf) {
for (i = o = 0; i < msgdata->len; i++)
if (msgdata->data[i] != '\r')
msgdata->data[o++] = msgdata->data[i];
msgdata->len = o;
msgdata->crlf = 0;
}
}
#ifndef HAVE_VASPRINTF
static int
vasprintf( char **strp, const char *fmt, va_list ap )
{
int len;
char tmp[1024];
if ((len = vsnprintf( tmp, sizeof(tmp), fmt, ap) ) < 0)
*strp = 0;
else if ((*strp = malloc( len + 1 ))) {
if (len >= sizeof(tmp))
vsprintf( *strp, fmt, ap );
else
memcpy( *strp, tmp, len + 1 );
}
return len;
}
#endif
void
oob( void )
{
fputs( "Fatal: buffer too small. Please report a bug.\n", stderr );
abort();
}
int
nfsnprintf( char *buf, int blen, const char *fmt, ... )
{
int ret;
va_list va;
va_start( va, fmt );
if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
oob();
va_end( va );
return ret;
}
static void ATTR_NORETURN
oom( void )
{
fputs( "Fatal: Out of memory\n", stderr );
abort();
}
void *
nfmalloc( size_t sz )
{
void *ret;
if (!(ret = malloc( sz )))
oom();
return ret;
}
void *
nfcalloc( size_t sz )
{
void *ret;
if (!(ret = calloc( sz, 1 )))
oom();
return ret;
}
void *
nfrealloc( void *mem, size_t sz )
{
char *ret;
if (!(ret = realloc( mem, sz )) && sz)
oom();
return ret;
}
char *
nfstrdup( const char *str )
{
char *ret;
if (!(ret = strdup( str )))
oom();
return ret;
}
int
nfvasprintf( char **str, const char *fmt, va_list va )
{
int ret = vasprintf( str, fmt, va );
if (!*str)
oom();
return ret;
}
int
nfasprintf( char **str, const char *fmt, ... )
{
int ret;
va_list va;
va_start( va, fmt );
ret = nfvasprintf( str, fmt, va );
va_end( va );
return ret;
}
/*
static struct passwd *
cur_user( void )
{
char *p;
struct passwd *pw;
uid_t uid;
uid = getuid();
if ((!(p = getenv("LOGNAME")) || !(pw = getpwnam( p )) || pw->pw_uid != uid) &&
(!(p = getenv("USER")) || !(pw = getpwnam( p )) || pw->pw_uid != uid) &&
!(pw = getpwuid( uid )))
{
fputs ("Cannot determinate current user\n", stderr);
return 0;
}
return pw;
}
*/
static char *
my_strndup( const char *s, size_t nchars )
{
char *r = nfmalloc( nchars + 1 );
memcpy( r, s, nchars );
r[nchars] = 0;
return r;
}
char *
expand_strdup( const char *s )
{
struct passwd *pw;
const char *p, *q;
char *r;
if (*s == '~') {
s++;
if (!*s) {
p = 0;
q = Home;
} else if (*s == '/') {
p = s;
q = Home;
} else {
if ((p = strchr( s, '/' ))) {
r = my_strndup( s, (int)(p - s) );
pw = getpwnam( r );
free( r );
} else
pw = getpwnam( s );
if (!pw)
return 0;
q = pw->pw_dir;
}
nfasprintf( &r, "%s%s", q, p ? p : "" );
return r;
} else
return nfstrdup( s );
}
static int
compare_ints( const void *l, const void *r )
{
return *(int *)l - *(int *)r;
}
void
sort_ints( int *arr, int len )
{
qsort( arr, len, sizeof(int), compare_ints );
}
static struct {
unsigned char i, j, s[256];
} rs;
void
arc4_init( void )
{
int i, fd;
unsigned char j, si, dat[128];
if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) {
fprintf( stderr, "Fatal: no random number source available.\n" );
exit( 3 );
}
if (read( fd, dat, 128 ) != 128) {
fprintf( stderr, "Fatal: cannot read random number source.\n" );
exit( 3 );
}
close( fd );
for (i = 0; i < 256; i++)
rs.s[i] = i;
for (i = j = 0; i < 256; i++) {
si = rs.s[i];
j += si + dat[i & 127];
rs.s[i] = rs.s[j];
rs.s[j] = si;
}
rs.i = rs.j = 0;
for (i = 0; i < 256; i++)
arc4_getbyte();
}
unsigned char
arc4_getbyte( void )
{
unsigned char si, sj;
rs.i++;
si = rs.s[rs.i];
rs.j += si;
sj = rs.s[rs.j];
rs.s[rs.i] = sj;
rs.s[rs.j] = si;
return rs.s[(si + sj) & 0xff];
}
Loading…
Cancel
Save