Compare commits

..

No commits in common. "filter" and "v1.2.1" have entirely different histories.

53 changed files with 5224 additions and 6296 deletions

58
.gitignore vendored
View file

@ -1,32 +1,30 @@
/.autoconf_trace
/ChangeLog
/INSTALL
/autom4te.cache/
/aclocal.m4
/autodefs.h
/autodefs.h.in
/autodefs.h.in~
/build-stamp
/compile
/config.cache
/config.guess
/config.log
/config.status
/config.sub
/configure
/configure.lineno
/configure-stamp
/cov-int/
/depcomp
/install-sh
/isync.spec
/isync-*.tar.gz
/isync-*.tar.gz.asc
/missing
/patch-stamp
/stamp-h
/stamp-h.in
/stamp-h1
.autoconf_trace
ChangeLog
INSTALL
Makefile
Makefile.in
autom4te.cache
aclocal.m4
autodefs.h
autodefs.h.in
build-stamp
compile
config.cache
config.guess
config.log
config.status
config.sub
configure
configure.lineno
configure-stamp
cov-int
depcomp
install-sh
isync.spec
isync-*.tar.gz
missing
patch-stamp
stamp-h
stamp-h.in
stamp-h1
*~

View file

@ -1,41 +0,0 @@
#FROM debian:bookworm-20231030-slim
FROM debian:bullseye-20220801
# need to add
# removed
# libsasl2-modules \
# ca-certificates \
# version pinning is being handled in our from line
# hadolint ignore=DL3008
RUN true && \
apt-get update && \
groupadd --gid 1000 user && \
useradd -m --home-dir /home/user --shell /bin/sh --uid 1000 --gid 1000 user && \
apt-get install -y --no-install-recommends \
libsasl2-2 \
libsasl2-dev \
perl \
libdatetime-format-dateparse-perl \
autoconf \
automake \
zlib1g-dev \
libdb-dev \
libsasl2-dev \
libssl-dev \
gcc \
make \
git \
&& \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
true
# Built with
# apt install build-essential dh-autoreconf git
# apt install libssl-dev
# apt install zlib1g-dev
# apt install libsasl2-dev
WORKDIR /home/user
USER user

View file

@ -1,5 +1,5 @@
SUBDIRS = src
bin_SCRIPTS = mbsync-get-cert
bin_SCRIPTS = get-cert
EXTRA_DIST = debian isync.spec $(bin_SCRIPTS)
LOG_PL = \
@ -47,26 +47,23 @@ LOG_PL = \
print $$log."\n"; \
}
$(srcdir)/.git/index:
$(srcdir)/ChangeLog: $(srcdir)/.git/index
$(MAKE) log
$(srcdir)/ChangeLog: log
log:
@test -z "$(srcdir)" || cd $(srcdir) && \
( ! test -d .git || \
git log --pretty=medium --date=iso --log-size --name-only --no-merges | \
git log --date=iso --log-size --name-only --no-merges | \
perl -e '$(LOG_PL)' > ChangeLog )
cov-scan: clean
/opt/cov-analysis-*/bin/cov-build --dir cov-int $(MAKE)
tar cavf isync-cov.tar.xz cov-int
deb: deb-clean
CFLAGS= fakeroot debian/rules binary
deb:
CFLAGS= INSTALL= dpkg-buildpackage -b --no-sign
deb-clean:
dh_clean -Xsrc/
fakeroot debian/rules clean
dist-hook:
find $(distdir)/debian \( -name .#\*# -o -type l \) -print0 | xargs -0r rm -rf
-cd $(distdir)/debian && test -f .gitignore && rm -rf `cut -c2- .gitignore` .gitignore
-cd $(distdir)/debian && test -f .gitignore && rm -rf `cat .gitignore` .gitignore
dist-sign: dist
gpg -b -a $(PACKAGE)-$(VERSION).tar.gz

34
NEWS
View file

@ -1,37 +1,3 @@
[1.4.0]
The 'isync' compatibility wrapper was removed.
Added support for disabling TLS v1.3 - adjust SSLVersions if you set it.
Removed support for obsolete/insecure SSL v3.
The IMAP '$Forwarded' / Maildir 'P' (passed) flag is supported now.
Support for configuring a TLS cipher string was added.
IMAP mailbox subscriptions are supported now.
The IMAP user query can be scripted now.
Added built-in support for macOS Keychain.
Messages excluded by MaxSize will now result in placeholders.
The use of Master/Slave terminology has been deprecated.
[1.3.0]
Network timeout handling has been added.
Support for proper Maildir++ and a Maildir sub-folder naming style
without extra dots have been added.
Support for TLS client certificates was added.
Support for recovering from baseless UID validity changes was added.
The get-cert script was renamed to mbsync-get-cert.
[1.2.0]
The 'isync' compatibility wrapper is now deprecated.

21
README
View file

@ -23,7 +23,8 @@ Mailboxes can be safely modified while ``mbsync'' operates.
Multiple replicas of each mailbox can be maintained.
isync is the project name, while mbsync is the current executable name; this
change was necessary because of massive changes in the user interface.
change was necessary because of massive changes in the user interface. An
isync executable still exists; it is a compatibility wrapper around mbsync.
* Features
@ -44,8 +45,15 @@ change was necessary because of massive changes in the user interface.
Courier 1.4.3 is known to be buggy, version 1.7.3 works fine.
M$ Exchange (2013 at least) needs DisableExtension MOVE to be compatible
with the Trash functionality.
c-client (UW-IMAP, Pine) is mostly fine, but versions less than 2004a.352
tend to change UIDVALIDITY pretty often when used with unix/mbox mailboxes,
making isync refuse synchronization.
M$ Exchange (up to 2010 at least) occasionally exposes the same problem.
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).
* Platforms
@ -54,18 +62,11 @@ change was necessary because of massive changes in the user interface.
* Requirements
perl v5.14+
Berkeley DB 4.1+ (optional)
OpenSSL for TLS/SSL support (optional)
Cyrus SASL (optional)
zlib (optional)
The build from git also requires:
GNU autotools (autoconf & automake)
perl module Date::Parse (libtimedate-perl on Debian, perl-TimeDate on
Fedora and Suse)
* Installation
./autogen.sh (only when building from git)

46
TODO
View file

@ -1,21 +1,12 @@
f{,data}sync() usage could be optimized by batching the calls.
add some marker about message being already [remotely] trashed.
real transactions would be certainly not particularly useful ...
make SSL (connect) timeouts produce a bit more than "Unidentified socket error".
automatically resume upon transient errors, e.g. "connection reset by peer"
or timeout after some data was already transmitted.
possibly also try to handle Exchange's "glitches" somehow.
add support for IMAP UTF-7 (for internationalized mailbox names).
uidvalidity lock timeout handling would be a good idea.
should complain when multiple Channels match the same folders.
propagate folder deletions even when the folders are non-empty.
- verify that "most" of the folders in the Channel are still there.
- refuse to delete unpropagated messages when trashing on the remote side.
- refuse to delete far side if it has unpropagated messages. symmetry?
network timeout handling in general would be a good idea.
lock timeout handling, too.
add message expiration based on arrival date (message date would be too
unreliable). MaxAge; probably mutually exclusive to MaxMessages.
@ -24,18 +15,11 @@ add alternative treatments of expired messages. ExpiredMessageMode: Prune
(delete messages like now), Keep (just don't sync) and Archive (move to
separate folder - ArchiveSuffix, default .archive).
add support for event notification callbacks.
it would be also possible to report more differentiated exit codes, but
that seems too limiting in the general case.
make it possible to have different mailbox names for far and near side in
Patterns.
- use far:near for the pattern
- for quoting, use more colons: the longest sequence of colons is the
separator
- this makes Groups mostly useless, as they are mostly a workaround for this
function being missing so far
- this is needed for move detection, which would work only within one Channel
kill the concept of an INBOX, it is a relic from single-channel operation.
if somebody needs it, he can have two stores with different Paths. the path
can name a single (in-)box (curr. broken with maildir). an empty box name
actually means empty, so the IMAP mailbox should use INBOX for Path (can't
make that the default, as it would mess up the NAMESPACE).
add regexp-based mailbox path rewriting to the drivers. user would provide
expressions for both directions. every transformation would be immediately
@ -46,7 +30,6 @@ add daemon mode. primary goal: keep imap password in memory.
also: idling mode.
parallel fetching of multiple mailboxes.
TLS session resumption becomes interesting then as well.
imap_set_flags(): group commands for efficiency, don't call back until
imap_commit().
@ -61,16 +44,15 @@ messages.
use MULTIAPPEND and FETCH with multiple messages.
dummy messages resulting from MaxSize should contain a dump of the original
message's MIME structure and its (reasonably sized) text parts.
create dummies describing MIME structure of messages bigger than MaxSize.
flagging the dummy would fetch the real message. possibly remove --renew.
note that all interaction needs to happen on the slave side probably.
don't SELECT boxes unless really needed; in particular not for appending,
and in write-only mode not before changes are made.
problem: UIDVALIDITY change detection is delayed, significantly complicating
matters.
some error messages are unhelpful in non-verbose mode due to missing context.
possibly use ^[[1m to highlight error messages.
consider alternative approach to trashing: instead of the current trash-before-
@ -83,4 +65,4 @@ however, this implies a huge working set.
consider optional use of messages-id (and X-GM-MSGID):
- detection of message moves between folders
- recovery from loss of sync state, migration from other tools
- recovery from unmotivated UIDVALIDITY change, or total loss of sync state

3
build
View file

@ -1,3 +0,0 @@
#!/bin/sh
# Note we assume this is run with podman. Take -u 0:0 out if using docker
docker run -u 0:0 -it --rm -v $(pwd):/make -w /make isync-build make

View file

@ -1,69 +1,19 @@
AC_INIT([isync], [1.4.4])
AC_INIT([isync], [1.2.1])
AC_CONFIG_HEADERS([autodefs.h])
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE
AM_MAINTAINER_MODE
AC_PROG_CC
if test "$GCC" = yes; then
warnings="
-Wall -Wextra
-Wshadow
-Wcast-qual
-Wformat=2 -Wformat-signedness -Wformat-nonliteral
-Wstrict-prototypes
-Wno-overlength-strings
"
CFLAGS="$CFLAGS -pipe -std=c11 -pedantic $(echo $warnings)"
CFLAGS="$CFLAGS -pipe -W -Wall -Wshadow -Wstrict-prototypes -ansi -pedantic -Wno-overlength-strings"
fi
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
void fkt(void)
{
int a = 42; // c99 comment
for (int i = 0; i < a; i++) {} // declaration inside for()
int b; // declaration after code
}
// c11 anonymous structs/unions
struct base {
int a;
};
union deriv {
struct base gen;
struct {
int a;
int b;
};
};
])], , [AC_MSG_ERROR([compiler does not support required C11 features])])
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
AC_CHECK_PROG(PERL, perl, perl)
if test "x$PERL" = "x"; then
AC_MSG_ERROR([perl not found])
fi
need_perl=5.14
AC_CACHE_CHECK([whether perl is recent enough], ob_cv_perl_ver, [
if $PERL -e "use v$need_perl;" 2> /dev/null; then
ob_cv_perl_ver=yes
else
ob_cv_perl_ver=no
fi
])
if test "x$ob_cv_perl_ver" = "xno"; then
AC_MSG_ERROR([perl is too old, need v$need_perl])
fi
AC_CACHE_CHECK([whether strftime supports %z], ob_cv_strftime_z,
[AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <time.h>
[AC_TRY_RUN(
[#include <time.h>
#include <string.h>
int main(void)
@ -73,12 +23,12 @@ int main(void)
strftime(buf, sizeof(buf), "%z", localtime(&t));
return !(buf[0] == '+' || buf[0] == '-');
}
]])], [ob_cv_strftime_z=yes], [ob_cv_strftime_z=no], [ob_cv_strftime_z="yes (assumed)"])])
], [ob_cv_strftime_z=yes], [ob_cv_strftime_z=no], [ob_cv_strftime_z="yes (assumed)"])])
if test "x$ob_cv_strftime_z" = x"no"; then
AC_MSG_ERROR([libc lacks necessary feature])
fi
AC_CHECK_HEADERS(poll.h sys/select.h)
AC_CHECK_HEADERS(sys/poll.h sys/select.h)
AC_CHECK_FUNCS(vasprintf strnlen memrchr timegm)
AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
@ -96,7 +46,7 @@ fi
have_ssl_paths=
AC_ARG_WITH(ssl,
AS_HELP_STRING([--with-ssl[=PATH]], [where to look for SSL [detect]]),
AC_HELP_STRING([--with-ssl[=PATH]], [where to look for SSL [detect]]),
[ob_cv_with_ssl=$withval])
if test "x$ob_cv_with_ssl" != xno; then
case $ob_cv_with_ssl in
@ -104,13 +54,13 @@ if test "x$ob_cv_with_ssl" != xno; then
dnl Detect the pkg-config tool, as it may have extra info about the openssl
dnl installation we can use. I *believe* this is what we are expected to do
dnl on really recent Redhat Linux hosts.
PKG_PROG_PKG_CONFIG
if test "x$PKG_CONFIG" != "x" ; then
AC_PATH_PROG(PKGCONFIG, pkg-config, no, $PATH:/usr/bin:/usr/local/bin)
if test "$PKGCONFIG" != "no" ; then
AC_MSG_CHECKING([OpenSSL presence with pkg-config])
if $PKG_CONFIG --exists openssl; then
SSL_LIBS=`$PKG_CONFIG --libs-only-l openssl`
SSL_LDFLAGS=`$PKG_CONFIG --libs-only-L openssl`
SSL_CPPFLAGS=`$PKG_CONFIG --cflags-only-I openssl`
if $PKGCONFIG --exists openssl; then
SSL_LIBS=`$PKGCONFIG --libs-only-l openssl`
SSL_LDFLAGS=`$PKGCONFIG --libs-only-L openssl`
SSL_CPPFLAGS=`$PKGCONFIG --cflags-only-I openssl`
have_ssl_paths=yes
AC_MSG_RESULT([found])
else
@ -127,7 +77,7 @@ if test "x$ob_cv_with_ssl" != xno; then
sav_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $SSL_LDFLAGS"
AC_CHECK_LIB(dl, dlopen, [LIBDL=-ldl])
AC_CHECK_LIB(crypto, X509_cmp, [LIBCRYPTO=-lcrypto])
AC_CHECK_LIB(crypto, CRYPTO_lock, [LIBCRYPTO=-lcrypto])
AC_CHECK_LIB(ssl, SSL_connect,
[SSL_LIBS="-lssl $LIBCRYPTO $LIBDL" have_ssl_paths=yes])
LDFLAGS=$sav_LDFLAGS
@ -193,13 +143,12 @@ AC_CACHE_CHECK([for Berkeley DB >= 4.1], ac_cv_berkdb4,
[ac_cv_berkdb4=no
sav_LIBS=$LIBS
LIBS="$LIBS -ldb"
AC_LINK_IFELSE([AC_LANG_PROGRAM(
[#include <db.h>],
[DB *db;
db_create(&db, 0, 0);
db->truncate(db, 0, 0, 0);
db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 0);
])], [ac_cv_berkdb4=yes], [])
AC_TRY_LINK([#include <db.h>],
[DB *db;
db_create(&db, 0, 0);
db->truncate(db, 0, 0, 0);
db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 0)],
[ac_cv_berkdb4=yes])
LIBS=$sav_LIBS
])
if test "x$ac_cv_berkdb4" = xyes; then
@ -221,32 +170,16 @@ if test "x$ob_cv_with_zlib" != xno; then
)
fi
AC_ARG_ENABLE(compat,
AC_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 -a "x$ac_cv_berkdb4" = xyes)
AM_CONDITIONAL(with_mdconvert, test "x$ac_cv_berkdb4" = xyes)
case $target_os in
darwin*)
darwin=yes
;;
*)
darwin=no
;;
esac
AC_ARG_WITH(
macos-keychain,
[AS_HELP_STRING([--with-macos-keychain], [Support macOS keychain])],
[have_macos_keychain=$withval],
[have_macos_keychain=$darwin])
if test "x$have_macos_keychain" != xno; then
if test $darwin = no; then
AC_MSG_ERROR([Cannot use macOS Keychain outside macOS.])
fi
have_macos_keychain=yes
AC_DEFINE(HAVE_MACOS_KEYCHAIN, 1, [Define to 1 if you have the macOS Keychain Services API.])
AC_SUBST(KEYCHAIN_LIBS, ["-Wl,-framework,Security,-framework,CoreFoundation"])
fi
AC_CONFIG_FILES([Makefile src/Makefile isync.spec])
AC_CONFIG_FILES([Makefile src/Makefile src/compat/Makefile isync.spec])
AC_OUTPUT
AC_MSG_RESULT()
@ -270,11 +203,4 @@ if test "x$ac_cv_berkdb4" = xyes; then
else
AC_MSG_RESULT([Not using Berkeley DB])
fi
if test $darwin = yes; then
if test "x$have_macos_keychain" = xyes; then
AC_MSG_RESULT([Using macOS Keychain])
else
AC_MSG_RESULT([Not using macOS Keychain])
fi
fi
AC_MSG_RESULT()

12
debian/.gitignore vendored
View file

@ -1,7 +1,5 @@
/.debhelper
/autoreconf.after
/autoreconf.before
/files
/isync
/isync.debhelper.log
/isync.substvars
files
isync
isync.debhelper.log
isync.postrm.debhelper
isync.substvars

13
debian/README.Debian vendored
View file

@ -1,13 +0,0 @@
A note from isync's web site:
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>!mbsync the_channel:maildir\n"'
where the_channel is the Channel used to sync this mailbox, and maildir is the
name of the local mailbox itself. 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).

94
debian/changelog vendored
View file

@ -1,97 +1,3 @@
isync (1.2.3-0) unstable; urgency=low
* Upload to unstable (with urgency=low)
-- Oswald Buddenhagen <ossi@users.sf.net> Sun, 01 Oct 2017 12:12:12 +0000
isync (1.2.1-2) unstable; urgency=low
* Upload to unstable (with urgency=low)
* Don't call uupdate after uscan
* Import patch to fix build with OpenSSL 1.1 (Closes: #828357)
* Bump Standards-Version to 3.9.8 (no changes needed)
* Add pkg-config to Build-Depends
* Update Vcs-* URLs
* Fix spelling-error-in-binary
-- Alessandro Ghedini <ghedo@debian.org> Sat, 19 Nov 2016 17:14:42 +0000
isync (1.2.1-1) experimental; urgency=medium
[ Evgeni Golov ]
* New upstream release.
* Explicitly Build-Depend on zlib1g-dev
-- Alessandro Ghedini <ghedo@debian.org> Sat, 09 Jan 2016 12:56:39 +0000
isync (1.2.0-1) experimental; urgency=medium
* New upstream release
- Only show sync progress by default (Closes: #765052)
* Enable libsasl support
-- Alessandro Ghedini <ghedo@debian.org> Mon, 06 Apr 2015 13:42:24 +0200
isync (1.1.2-1) unstable; urgency=medium
* New upstream release
* Bump Standards-Version to 3.9.6 (no changes needed)
-- Alessandro Ghedini <ghedo@debian.org> Sun, 01 Feb 2015 20:42:25 +0100
isync (1.1.1-1) unstable; urgency=medium
* New upstream release
- Don't lie about the default of User (Closes: #744389)
- Don't forget to reset message counts when skipping scan (Closes: #744259)
- Rework maildir store mapping (Closes: #737708)
* Drop 01_fix-manpages.patch (merged upstream)
* Drop 02_fix-empty-folder-sync.patch (merged upstream)
-- Alessandro Ghedini <ghedo@debian.org> Tue, 03 Jun 2014 21:00:44 +0200
isync (1.1.0-2) unstable; urgency=medium
* Drop 02_fix-duplicate-changelog.patch
(rm the file after installation instead)
* Update 01_fix-manpages.patch
* Add 02_fix-empty-folder-sync.patch (Closes: #738873)
-- Alessandro Ghedini <ghedo@debian.org> Fri, 14 Feb 2014 20:41:49 +0100
isync (1.1.0-1) unstable; urgency=low
* New upstream release (Closes: #674403)
- Fix overlapping memcpy (Closes: #650373)
- Fix segfault while syncing mailboxes (Closes: #411120)
- Fix segfault when invoked with arguments without configuration
(Closes: #727239)
* Bump debhelper compat level, update Build-Depends
* Switch to short-form dh rules, remove useless files
* Switch to 3.0 (quilt) source format
* Remove empty patches/ directory
* Drop local source modifications
* Update short/long descriptions
* Add 01_fix-manpages.patch to fix manpage errors and typos
* Add Homepage field
* Update copyright file to Copyright-Format 1.0
* Add Vcs-* fields
* Add 02_fix-duplicate-changelog.patch to avoid duplicate changelog install
* Add myself to Uploaders
* Bump Standards-Version to 3.9.5 (no changes needed)
* Use dh-autoreconf instead of autotools-dev
-- Alessandro Ghedini <ghedo@debian.org> Sun, 12 Jan 2014 16:35:52 +0100
isync (1.0.4-2.2) unstable; urgency=low
* Non-maintainer upload.
* Apply upstream patch for CVE-2013-0289.
Fix incorrect server's SSL x509.v3 certificate validation when
performing IMAP synchronization. (Closes: #701052)
-- Salvatore Bonaccorso <carnil@debian.org> Sun, 24 Feb 2013 09:27:55 +0100
isync (1.0.4-2.1) unstable; urgency=low
* Non-maintainer upload.

2
debian/compat vendored
View file

@ -1 +1 @@
9
4

42
debian/control vendored
View file

@ -2,37 +2,33 @@ Source: isync
Section: mail
Priority: optional
Maintainer: Nicolas Boullis <nboullis@debian.org>
Uploaders: Theodore Y. Ts'o <tytso@mit.edu>,
Alessandro Ghedini <ghedo@debian.org>
Standards-Version: 3.9.8
Build-Depends: debhelper (>= 9),
dh-autoreconf,
libdb-dev,
libsasl2-dev,
libssl-dev,
pkg-config,
zlib1g-dev
Vcs-Git: https://anonscm.debian.org/git/collab-maint/isync.git
Vcs-Browser: https://anonscm.debian.org/gitweb/?p=collab-maint/isync.git
Homepage: http://isync.sourceforge.net/
Uploaders: Theodore Y. Ts'o <tytso@mit.edu>
Standards-Version: 3.7.3
Build-Depends: libssl-dev (>= 0.9.8), debhelper (>= 4.1.16), dpkg-dev (>= 1.9.0), libdb-dev
Package: isync
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Suggests: mutt
Description: IMAP and MailDir mailbox synchronizer
mbsync/isync 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. isync is suitable
for use in IMAP-disconnected mode.
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).
.
The main application was much improved in version 1.0. Those
improvements lead to interface changes and the application being
renamed to mbsync. The application isync is now only a wrapper to
keep compatibility with earlier versions.
.
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:
* Security: supports TLS/SSL via imaps: (port 993) and STARTTLS; SASL
for authentication
* Supports NAMESPACE for simplified configuration
* Pipelining for maximum speed
* 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

59
debian/copyright vendored
View file

@ -1,33 +1,32 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: isync
Source: http://isync.sourceforge.net
This package was debianized by Tommi Virtanen <tv@debian.org> and is now
maintained by Nicolas Boullis <nboullis@debian.org>.
Files: *
Copyright: 2000-2002, Michael R. Elkins <me@mutt.org>
2002-2017, Oswald Buddenhagen <ossi@users.sf.net>
2004, Theodore Y. Ts'o <tytso@mit.edu>
License: GPL-2+
It was downloaded from http://isync.sourceforge.net/
Files: debian/*
Copyright: 2013, Alessandro Ghedini <ghedo@debian.org>
License: GPL-2+
Upstream Author: Michael R. Elkins <me@mutt.org>,
Oswald Buddenhagen <ossi@users.sf.net>
License: GPL-2+
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 package 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, see <http://www.gnu.org/licenses/>.
.
As a special exception, mbsync may be linked with the OpenSSL library,
despite that library's more restrictive license.
.
On Debian systems, the complete text of the GNU General Public License version
2 can be found in "/usr/share/common-licenses/GPL-2".
Copyright:
* isync - IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2006 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, see <http://www.gnu.org/licenses/>.
*
* As a special exception, isync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
On Debian systems, the complete text of the GNU General Public
License can be found in /usr/share/common-licenses/GPL

1
debian/dirs vendored Normal file
View file

@ -0,0 +1 @@
usr/bin

3
debian/docs vendored Normal file
View file

@ -0,0 +1,3 @@
NEWS
README
TODO

39
debian/generate-deb vendored Executable file
View file

@ -0,0 +1,39 @@
#!/bin/sh
#
# Intended to be run from the root of the isync source tree in the repository.
#
VERSION=`dpkg-parsechangelog | sed -ne 's/^Version: \(.*\)-[^-]\+$/\1/p'`
OLDVERSION=$VERSION
if echo $VERSION | grep +cvsXXXXXXXX; then
DATE=`date +%Y%m%d`
VERSION=`echo $VERSION | sed -e s/+cvsXXXXXXXX/+cvs${DATE}/`
else
if [ ! -f ../isync_$VERSION.orig.tar.gz ]; then
echo isync_$VERSION.orig.tar.gz must be found in the parent directory.
exit 1
fi
fi
rm -rf ../isync-$VERSION
fakeroot ./debian/rules clean
cp -rl . ../isync-$VERSION
cd ../isync-$VERSION
if [ "$OLDVERSION" != "$VERSION" ]; then
sed -e s/+cvsXXXXXXXX/+cvs${DATE}/ < debian/changelog > debian/changelog.new
mv debian/changelog.new debian/changelog
fi
find . -name .git -print0 | xargs -0r rm -rf
find . -name .gitignore -print0 | xargs -0r rm
find . -type l -print0 | xargs -0r rm
find . -name .#\*# -print0 | xargs -0r rm
aclocal
autoheader
automake --add-missing --copy
autoconf
if [ -n "$DOSIGN" ]; then
SIGNOPTS=
else
SIGNOPTS="-us -uc"
fi
dpkg-buildpackage -rfakeroot $SIGNOPTS

57
debian/rules vendored
View file

@ -1,8 +1,55 @@
#!/usr/bin/make -f
%:
dh $@ --with=autoreconf
CFLAGS = -Wall -g
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
CFLAGS += -O0
else
CFLAGS += -O2
endif
override_dh_auto_install:
dh_auto_install
$(RM) $(CURDIR)/debian/isync/usr/share/doc/isync/ChangeLog
DEB_HOST_GNU_TYPE := $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
DEB_BUILD_GNU_TYPE := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
build: build-stamp
build-stamp:
dh_testdir
./configure --build=$(DEB_BUILD_GNU_TYPE) --host=$(DEB_HOST_GNU_TYPE) --prefix=/usr --mandir=/usr/share/man
$(MAKE) CFLAGS="$(CFLAGS)"
touch build-stamp
clean:
dh_testdir
dh_testroot
rm -f build-stamp
[ ! -f Makefile ] || $(MAKE) distclean
dh_clean Makefile config.log config.status
install: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs usr/bin usr/share/man/man1
$(MAKE) DESTDIR=$(CURDIR)/debian/isync install
rm -r $(CURDIR)/debian/isync/usr/share/doc
mv $(CURDIR)/debian/isync/usr/bin/get-cert $(CURDIR)/debian/isync/usr/bin/mbsync-get-cert
binary-indep: build install
binary-arch: build install
dh_testdir
dh_testroot
dh_installchangelogs ChangeLog
dh_installdocs AUTHORS NEWS README TODO
dh_installexamples src/mbsyncrc.sample src/compat/isyncrc.sample
dh_installman
dh_strip
dh_compress
dh_fixperms
dh_installdeb
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install

View file

@ -1 +0,0 @@
3.0 (quilt)

2
debian/watch vendored
View file

@ -1,2 +1,2 @@
version=3
http://sf.net/isync/ isync-(.*)\.tar\.gz
http://sf.net/isync/ isync-(.*)\.tar\.gz debian uupdate

View file

@ -30,6 +30,6 @@ rm -rf $RPM_BUILD_ROOT%{_docdir}/%{name}
rm -rf $RPM_BUILD_ROOT
%files
%doc AUTHORS COPYING NEWS README TODO ChangeLog src/mbsyncrc.sample
%doc AUTHORS COPYING NEWS README TODO ChangeLog src/mbsyncrc.sample src/compat/isyncrc.sample
%{_bindir}/*
%{_mandir}/man1/*

13
src/.gitignore vendored
View file

@ -1,8 +1,7 @@
/drv_proxy.inc
/mbsync
/mdconvert
/tst_timers
/tmp/
.deps/
.deps
Makefile
Makefile.in
mbsync
mdconvert
tmp
*.o

View file

@ -1,10 +1,11 @@
mbsync_SOURCES = main.c sync.c config.c util.c socket.c driver.c drv_imap.c drv_maildir.c drv_proxy.c
mbsync_LDADD = $(DB_LIBS) $(SSL_LIBS) $(SOCK_LIBS) $(SASL_LIBS) $(Z_LIBS) $(KEYCHAIN_LIBS)
noinst_HEADERS = common.h config.h driver.h sync.h socket.h
if with_compat
compat_dir = compat
endif
SUBDIRS = $(compat_dir)
drv_proxy.$(OBJEXT): drv_proxy.inc
drv_proxy.inc: $(srcdir)/driver.h $(srcdir)/drv_proxy.c $(srcdir)/drv_proxy_gen.pl
perl $(srcdir)/drv_proxy_gen.pl $(srcdir)/driver.h $(srcdir)/drv_proxy.c drv_proxy.inc
mbsync_SOURCES = main.c sync.c config.c util.c socket.c driver.c drv_imap.c drv_maildir.c
mbsync_LDADD = $(DB_LIBS) $(SSL_LIBS) $(SOCK_LIBS) $(SASL_LIBS) $(Z_LIBS)
noinst_HEADERS = common.h config.h driver.h sync.h socket.h
mdconvert_SOURCES = mdconvert.c
mdconvert_LDADD = $(DB_LIBS)
@ -23,6 +24,4 @@ man_MANS = mbsync.1 $(mdconvert_man)
exampledir = $(docdir)/examples
example_DATA = mbsyncrc.sample
EXTRA_DIST = drv_proxy_gen.pl run-tests.pl $(example_DATA) $(man_MANS)
CLEANFILES = drv_proxy.inc
EXTRA_DIST = run-tests.pl $(example_DATA) $(man_MANS)

View file

@ -33,22 +33,11 @@
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
#define as(ar) (sizeof(ar)/sizeof(ar[0]))
#define stringify__(x) #x
#define stringify(x) stringify__(x)
// From https://stackoverflow.com/a/62984543/3685191
#define deparen(x) esc_(ish_ x)
#define esc_(...) esc__(__VA_ARGS__)
#define esc__(...) van_ ## __VA_ARGS__
#define ish_(...) ish_ __VA_ARGS__
#define van_ish_
#define shifted_bit(in, from, to) \
((int)(((uint)(in) / (from > to ? from / to : 1) * (to > from ? to / from : 1)) & to))
#define __stringify(x) #x
#define stringify(x) __stringify(x)
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_UNUSED __attribute__((unused))
@ -60,28 +49,6 @@ typedef unsigned long ulong;
# define ATTR_PRINTFLIKE(fmt,var)
#endif
#if defined(__clang__)
# define DO_PRAGMA__(text) _Pragma(#text)
# define DIAG_PUSH DO_PRAGMA__(clang diagnostic push)
# define DIAG_POP DO_PRAGMA__(clang diagnostic pop)
# define DIAG_DISABLE(text) DO_PRAGMA__(clang diagnostic ignored text)
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5)
# define DO_PRAGMA__(text) _Pragma(#text)
# define DIAG_PUSH DO_PRAGMA__(GCC diagnostic push)
# define DIAG_POP DO_PRAGMA__(GCC diagnostic pop)
# define DIAG_DISABLE(text) DO_PRAGMA__(GCC diagnostic ignored text)
#else
# define DIAG_PUSH
# define DIAG_POP
# define DIAG_DISABLE(text)
#endif
#if __GNUC__ >= 7 || defined(__clang__)
# define FALLTHROUGH __attribute__((fallthrough));
#else
# define FALLTHROUGH
#endif
#ifdef __GNUC__
# define INLINE __inline__
#else
@ -98,19 +65,15 @@ typedef unsigned long ulong;
#define DEBUG_NET_ALL 0x08
#define DEBUG_SYNC 0x10
#define DEBUG_MAIN 0x20
#define DEBUG_DRV 0x40
#define DEBUG_DRV_ALL 0x80
#define DEBUG_ALL (0xFF & ~(DEBUG_NET_ALL | DEBUG_DRV_ALL))
#define DEBUG_ALL (0xFF & ~DEBUG_NET_ALL)
#define QUIET 0x100
#define VERYQUIET 0x200
#define PROGRESS 0x400
#define VERBOSE 0x800
#define KEEPJOURNAL 0x1000
#define ZERODELAY 0x2000
#define FORCEASYNC 0x4000
extern int DFlags;
extern int JLimit;
extern int UseFSync;
extern char FieldDelimiter;
@ -118,7 +81,7 @@ extern int Pid;
extern char Hostname[256];
extern const char *Home;
extern uint BufferLimit;
extern int BufferLimit;
extern int new_total[2], new_done[2];
extern int flags_total[2], flags_done[2];
@ -128,15 +91,14 @@ void stats( void );
/* util.c */
void ATTR_PRINTFLIKE(2, 0) vdebug( int, const char *, va_list va );
void ATTR_PRINTFLIKE(2, 0) vdebugn( int, const char *, va_list va );
void vdebug( int, const char *, va_list va );
void vdebugn( int, const char *, va_list va );
void ATTR_PRINTFLIKE(1, 2) info( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) infon( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) progress( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) notice( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) warn( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) error( const char *, ... );
void ATTR_PRINTFLIKE(1, 0) vsys_error( const char *, va_list va );
void ATTR_PRINTFLIKE(1, 2) sys_error( const char *, ... );
void flushn( void );
@ -145,20 +107,17 @@ typedef struct string_list {
char string[1];
} string_list_t;
void add_string_list_n( string_list_t **list, const char *str, uint len );
void add_string_list_n( string_list_t **list, const char *str, int len );
void add_string_list( string_list_t **list, const char *str );
void free_string_list( string_list_t *list );
#ifndef HAVE_MEMRCHR
void *memrchr( const void *s, int c, size_t n );
#endif
#ifndef HAVE_STRNLEN
size_t strnlen( const char *str, size_t maxlen );
#endif
int starts_with( const char *str, int strl, const char *cmp, uint cmpl );
int starts_with_upper( const char *str, int strl, const char *cmp, uint cmpl );
int equals( const char *str, int strl, const char *cmp, uint cmpl );
int starts_with( const char *str, int strl, const char *cmp, int cmpl );
int starts_with_upper( const char *str, int strl, const char *cmp, int cmpl );
int equals( const char *str, int strl, const char *cmp, int cmpl );
#ifndef HAVE_TIMEGM
time_t timegm( struct tm *tm );
@ -169,54 +128,21 @@ void *nfcalloc( size_t sz );
void *nfrealloc( void *mem, size_t sz );
char *nfstrndup( const char *str, size_t nchars );
char *nfstrdup( const char *str );
int ATTR_PRINTFLIKE(2, 0) nfvasprintf( char **str, const char *fmt, va_list va );
int nfvasprintf( char **str, const char *fmt, va_list va );
int ATTR_PRINTFLIKE(2, 3) nfasprintf( char **str, const char *fmt, ... );
int ATTR_PRINTFLIKE(3, 4) nfsnprintf( char *buf, int blen, const char *fmt, ... );
void ATTR_NORETURN oob( void );
void ATTR_NORETURN oom( void );
char *expand_strdup( const char *s );
int map_name( const char *arg, char **result, uint reserve, const char *in, const char *out );
int map_name( const char *arg, char **result, int reserve, const char *in, const char *out );
#define DEFINE_ARRAY_TYPE(T) \
typedef struct { \
T *data; \
uint size; \
} T##_array_t; \
typedef union { \
T##_array_t array; \
struct { \
T *data; \
uint size; \
uint alloc; \
}; \
} T##_array_alloc_t; \
static INLINE T *T##_array_append( T##_array_alloc_t *arr ) \
{ \
if (arr->size == arr->alloc) { \
arr->alloc = arr->alloc * 2 + 100; \
arr->data = nfrealloc( arr->data, arr->alloc * sizeof(T) ); \
} \
return &arr->data[arr->size++]; \
}
#define ARRAY_INIT(arr) \
do { (arr)->data = NULL; (arr)->size = (arr)->alloc = 0; } while (0)
#define ARRAY_SQUEEZE(arr) \
do { \
(arr)->data = nfrealloc( (arr)->data, (arr)->size * sizeof((arr)->data[0]) ); \
} while (0)
DEFINE_ARRAY_TYPE(uint)
void sort_uint_array( uint_array_t array );
int find_uint_array( const uint_array_t array, uint value );
void sort_ints( int *arr, int len );
void arc4_init( void );
uchar arc4_getbyte( void );
uint bucketsForSize( uint size );
int bucketsForSize( int size );
typedef struct list_head {
struct list_head *next, *prev;
@ -226,16 +152,15 @@ typedef struct notifier {
struct notifier *next;
void (*cb)( int what, void *aux );
void *aux;
#ifdef HAVE_POLL_H
uint index;
#ifdef HAVE_SYS_POLL_H
int index;
#else
int fd;
short events;
int fd, events;
#endif
} notifier_t;
#ifdef HAVE_POLL_H
# include <poll.h>
#ifdef HAVE_SYS_POLL_H
# include <sys/poll.h>
#else
# define POLLIN 1
# define POLLOUT 4
@ -243,8 +168,7 @@ typedef struct notifier {
#endif
void init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux );
void conf_notifier( notifier_t *sn, short and_events, short or_events );
short notifier_config( notifier_t *sn );
void conf_notifier( notifier_t *sn, int and_events, int or_events );
void wipe_notifier( notifier_t *sn );
typedef struct {
@ -257,7 +181,7 @@ typedef struct {
void init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux );
void conf_wakeup( wakeup_t *tmr, int timeout );
void wipe_wakeup( wakeup_t *tmr );
static INLINE int ATTR_UNUSED pending_wakeup( wakeup_t *tmr ) { return tmr->links.next != NULL; }
static INLINE int pending_wakeup( wakeup_t *tmr ) { return tmr->links.next != 0; }
void main_loop( void );

5
src/compat/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
.deps
Makefile
Makefile.in
isync
*.o

12
src/compat/Makefile.am Normal file
View file

@ -0,0 +1,12 @@
bin_PROGRAMS = isync
isync_SOURCES = main.c config.c convert.c util.c
isync_LDADD = $(DB_LIBS)
noinst_HEADERS = isync.h
man_MANS = isync.1
exampledir = $(docdir)/examples
example_DATA = isyncrc.sample
EXTRA_DIST = $(example_DATA) $(man_MANS)

554
src/compat/config.c Normal file
View file

@ -0,0 +1,554 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#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>
#include <ctype.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)
sys_error( "Cannot read config file '%s'", path );
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 (starts_with( val, -1, "imaps:", 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 const char *
quotify( const char *str )
{
char *ostr;
int i;
for (i = 0; str[i]; i++) {
if (isspace( str[i] )) {
nfasprintf( &ostr, "\"%s\"", str );
return ostr;
}
}
return str;
}
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], ubuf[64];
static int tunnels;
/* The old server names determine the derived store names. They are kinda stupid,
* but can't be changed, because store names are encoded in state file names. */
if (cfg->tunnel)
nfasprintf( (char **)&cfg->old_server_name, "tunnel%d", ++tunnels );
else {
if (sscanf( cfg->host, "%d.%d.%d.%d", &a1, &a2, &a3, &a4 ) == 4)
hl = nfsnprintf( buf, sizeof(buf), "%s", cfg->host );
else {
/* XXX this does not avoid clashes. add port? */
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 (equals( pbox->old_server_name, -1, buf, hl )) {
nfasprintf( (char **)&cfg->old_server_name, "%s-%d", buf, ++pbox->old_servers );
goto gotsrv;
}
cfg->old_server_name = nfstrdup( buf );
cfg->old_servers = 1;
gotsrv: ;
}
/* The "new" server names determine the names of the accounts themselves.
* They are optimized for descriptiveness, e.g. in password prompts. */
if (cfg->user)
nfsnprintf( ubuf, sizeof(ubuf), "%s@", cfg->user );
else
ubuf[0] = 0;
if (!cfg->host)
hl = nfsnprintf( buf, sizeof(buf), "%stunnel", ubuf );
else {
if (cfg->port != (cfg->use_imaps ? 993 : 143))
hl = nfsnprintf( buf, sizeof(buf), "%s%s_%d", ubuf, cfg->host, cfg->port );
else
hl = nfsnprintf( buf, sizeof(buf), "%s%s", ubuf, cfg->host );
}
if (boxes) /* !o2o */
for (pbox = boxes; pbox != cfg; pbox = pbox->next)
if (equals( pbox->server_name, -1, buf, hl )) {
nfasprintf( (char **)&cfg->server_name, "%s-%d", buf, ++pbox->servers );
goto ngotsrv;
}
cfg->server_name = nfstrdup( buf );
cfg->servers = 1;
ngotsrv: ;
fprintf( fp, "IMAPAccount %s\n", cfg->server_name );
if (cfg->tunnel)
fprintf( fp, "Tunnel \"%s\"\n", cfg->tunnel );
else {
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", quotify( cfg->user ) );
if (cfg->pass)
fprintf( fp, "Pass %s\n", quotify( 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", quotify( 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->old_server_name, cfg->stores );
else
cfg->store_name = cfg->old_server_name;
fprintf( fp, "IMAPStore %s\nAccount %s\n",
cfg->store_name, cfg->server_name );
if (*folder)
fprintf( fp, "Path %s\n", quotify( folder ) );
else
fprintf( fp, "UseNamespace %s\n", tb(cfg->use_namespace) );
if (inbox)
fprintf( fp, "MapInbox %s\n", quotify( inbox ) );
if (cfg->copy_deleted_to)
fprintf( fp, "Trash %s\n", quotify( 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 && !global.delete && !delete)
fputs( "Sync All\n", fp );
if (cfg->expunge && !global.expunge && !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;
char *path, *local_box, *local_store;
config_t *box, *sbox, *pbox;
int pl;
if (!(fp = fdopen( fd, "w" ))) {
perror( "fdopen" );
return;
}
fputs( "SyncState *\n", fp );
if (!global.delete && !delete)
fputs( "Sync New ReNew Flags\n", fp );
if (global.expunge || expunge)
fputs( "Expunge Both\n", fp );
fputc( '\n', fp );
if (o2o) {
write_imap_server( fp, &global );
write_imap_store( fp, &global );
fprintf( fp, "MaildirStore local\nPath %s/\n", quotify( maildir ) );
if (!inbox) { /* just in case listing actually produces an INBOX ... */
nfasprintf( (char **)&inbox, "%s/INBOX", maildir );
fprintf( fp, "Inbox %s\n", quotify( inbox ) );
}
if (altmap > 0)
fputs( "AltMap yes\n", fp );
fprintf( fp, "\nChannel 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:
path = expand_strdup( box->path );
if (starts_with( path, -1, Home, HomeLen ) && path[HomeLen] == '/')
nfasprintf( &path, "~%s", path + HomeLen );
local_store = local_box = strrchr( path, '/' ) + 1;
pl = local_store - path;
/* try to re-use existing store */
for (pbox = boxes; pbox != box; pbox = pbox->next)
if (pbox->local_store_path && equals( pbox->local_store_path, -1, path, pl ))
goto gotstor;
box->local_store_path = my_strndup( path, pl );
/* derive a suitable name */
if (!strcmp( box->local_store_path, "/var/mail/" ) || !strcmp( box->local_store_path, "/var/spool/mail/" )) {
local_store = nfstrdup( "spool" );
} else if (!strcmp( box->local_store_path, "~/" )) {
local_store = nfstrdup( "home" );
} else {
local_store = memrchr( box->local_store_path, '/', pl - 1 );
if (local_store) {
local_store = my_strndup( local_store + 1, pl - 2 - (local_store - box->local_store_path) );
for (pl = 0; local_store[pl]; pl++)
local_store[pl] = tolower( local_store[pl] );
} else {
local_store = nfstrdup( "local" );
}
}
/* avoid name clashes with imap stores */
for (pbox = boxes; pbox != box; pbox = pbox->next)
if (!strcmp( pbox->store_name, local_store )) {
nfasprintf( &local_store, "local_%s", local_store );
goto gotsdup;
}
gotsdup:
/* avoid name clashes with other local stores */
for (pbox = boxes; pbox != box; pbox = pbox->next)
if (pbox->local_store_name && !strcmp( pbox->local_store_name, local_store )) {
nfasprintf( (char **)&box->local_store_name, "%s-%d", local_store, ++pbox->local_stores );
goto gotdup;
}
box->local_store_name = local_store;
box->local_stores = 1;
gotdup:
fprintf( fp, "MaildirStore %s\nPath %s\n", box->local_store_name, quotify( box->local_store_path ) );
if (altmap > 0)
fputs( "AltMap yes\n", fp );
fputc( '\n', fp );
pbox = box;
gotstor:
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 :%s:%s\n",
box->channel_name, box->store_name, quotify( box->box ), pbox->local_store_name, quotify( local_box ) );
write_channel_parm( fp, box );
}
}
fclose( fp );
}
config_t *
find_box( const char *s )
{
config_t *p;
char *t;
if (starts_with( s, -1, Home, HomeLen ) && s[HomeLen] == '/')
s += HomeLen + 1;
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;
}

265
src/compat/convert.c Normal file
View file

@ -0,0 +1,265 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#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 <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, 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 )) {
sys_error( "ERROR: cannot access %s", buf );
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) {
sys_error( "Cannot create %s", 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 )) {
sys_error( "Cannot lock %s", ilname );
err2:
close( fd );
goto err1;
}
if (!(fp = fopen( iuvname, "r" ))) {
sys_error( "Cannot open %s", iuvname );
goto err2;
}
if (fscanf( fp, "%d", &uidval ) != 1) {
sys_error( "Cannot read %s", iuvname );
err3:
fclose( fp );
goto err2;
}
fclose( fp );
if (!(fp = fopen( imuname, "r" ))) {
sys_error( "Cannot open %s", imuname );
goto err2;
}
if (fscanf( fp, "%d", &maxuid ) != 1) {
sys_error( "Cannot read %s", imuname );
goto err3;
}
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++) {
nfsnprintf( buf, sizeof(buf), "%s/%s/", mboxdir, subdirs[i] );
if (!(d = opendir( buf ))) {
sys_error( "Cannot list %s", buf );
err4:
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" ))) {
sys_error( "Cannot create %s", 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 );
if (rename( iumname, diumname )) {
sys_error( "Cannot rename %s to %s", iumname, diumname );
goto err4;
}
} else {
if (!(fp = fopen( uvname, "w" ))) {
sys_error( "Cannot create %s", uvname );
goto err4;
}
fprintf( fp, "%d\n%d\n", uidval, maxuid );
fclose( fp );
}
unlink( iuvname );
unlink( imuname );
close( fd );
unlink( ilname );
free( msgs );
free( mboxdir );
return;
}

328
src/compat/isync.1 Normal file
View file

@ -0,0 +1,328 @@
.ig
\" 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, see <http://www.gnu.org/licenses/>.
\"
..
.TH isync 1 "2010 Feb 7"
..
.SH NAME
isync - synchronize IMAP4 and Maildir mailboxes
..
.SH SYNOPSIS
\fBisync\fR [\fIoptions\fR ...] {\fImailbox\fR ...|\fI-a\fR|\fI-l\fR}
..
.SH DESCRIPTION
\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.
.br
\fBisync\fR is deprecated. Please use the \fB-w\fR option to permanently
migrate the configuration and start using \fBmbsync\fR directly.
..
.SH OPTIONS
.TP
\fB-c\fR, \fB--config\fR \fIfile\fR
Read configuration from \fIfile\fR.
By default, the configuration is read from ~/.isyncrc if it exists.
.TP
\fB-1\fR, \fB--one-to-one\fR
Instead of using the mailbox specifications in ~/.isyncrc, isync will pick up
all mailboxes from the local directory and remote folder and map them 1:1
onto each other according to their names.
.TP
\fB-I\fR, \fB--inbox\fR \fImailbox\fR
Exception to the 1:1 mapping created by -1: the special IMAP mailbox \fIINBOX\fR
is mapped to the local \fImailbox\fR (relative to the maildir).
.TP
\fB-a\fR, \fB--all\fR
Synchronize all mailboxes (either specified in ~/.isyncrc or determined by the
1:1 mapping).
.TP
\fB-l\fR, \fB--list\fR
Don't synchronize anything, but list all mailboxes and exit.
.TP
\fB-L\fR, \fB--create-local\fR
Automatically create the local Maildir mailbox if it doesn't already
exist.
.TP
\fB-R\fR, \fB--create-remote\fR
Automatically create the remote IMAP mailbox if it doesn't already exist.
.TP
\fB-C\fR, \fB--create\fR
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 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.
By default, \fIdeleted\fR messages are \fBnot\fR expunged.
.TP
\fB-f\fR, \fB--fast\fR
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 for imap,
993 for imaps)
.TP
\fB-q\fR, \fB--quiet\fR
Suppress informational messages.
If specified twice, suppress warning messages as well.
.TP
\fB-r\fR, \fB--remote\fR \fIbox\fR
Specifies the name of the remote IMAP mailbox to synchronize with
(Default: INBOX)
.TP
\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
Specifies the login name to access the IMAP server (default: $USER)
.TP
\fB-P\fR, \fB--pass\fR \fIpassword\fR
Specifies the password to access the IMAP server (prompted for by default)
.TP
\fB-M\fR, \fB--maildir\fR \fIdir\fR
Specifies the location for your local mailboxes.
.TP
\fB-F\fR, \fB--folder\fR \fIfolder\fR/
Specifies the location for your remote mailboxes.
.TP
\fB-v\fR, \fB--version\fR
Displays \fBisync\fR version information.
.TP
\fB-V\fR, \fB--verbose\fR
Enables \fIverbose\fR mode, which displays the IMAP4 network traffic.
.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 most command line options that would affect an actual sync operation
will be incorporated into the new config file as well; exceptions are
--fast and --create[-remote|-local].
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 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
line, up until the next \fIMailbox\fR command, apply to this mailbox only.
..
.TP
\fBHost\fR [\fBimaps:\fR]\fIname\fR
Defines the DNS name or IP address of the IMAP server. If the hostname is
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 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 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)
..
.TP
\fBUser\fR \fIusername\fR
Defines the login name on the IMAP server (Default: current user)
..
.TP
\fBPass\fR \fIpassword\fR
Defines the password for \fIusername\fR on the IMAP server.
Note that this option is \fBNOT\fR required.
If no password is specified in the configuration file, \fBisync\fR
will prompt you for it.
..
.TP
\fBAlias\fR \fIstring\fR
Defines an alias for the mailbox which can be used as a shortcut on the
command line.
..
.TP
\fBCopyDeletedTo\fR \fImailbox\fR
Specifies the remote IMAP mailbox to copy deleted messages to prior to
expunging (Default: none).
..
.TP
\fBDelete\fR \fIyes\fR|\fIno\fR
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. (Default: no).
\fBNOTE:\fR The \fI-e\fR command line option overrides this setting when
set to \fIno\fR.
..
.TP
\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 allowed only in the \fIglobal\fR
section (see below).
..
.TP
\fBFolder\fR \fIdirectory\fR/
Specifies the location of your IMAP mailboxes
specified in \fIBox\fR commands (Default: \fI""\fR).
\fBNOTE:\fR You \fBmust\fR append the hierarchy delimiter (usually
a slash) to this specification.
\fBNOTE 2:\fR This directive is allowed only in the \fIglobal\fR
section (see below).
..
.TP
\fBMaxMessages\fR \fIcount\fR
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 unread 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
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
Specify a command to run to establish a connection rather than opening a TCP
socket. This allows you to run an IMAP session over an SSH tunnel, for
example.
.TP
\fBUseNamespace\fR \fIyes\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 \fIFolder\fR was specified.
(Default: \fIyes\fR)
..
.TP
\fBRequireCRAM\fR \fIyes\fR|\fIno\fR
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 cannot be
established with the IMAP server. (Default: \fIyes\fR)
..
.TP
\fBCertificateFile\fR \fIpath\fR
File containing additional X.509 certificates used to verify server
identities. Directly matched peer certificates are always trusted,
regardless of validity.
.br
Note that the system's default certificate store is always used
and should not be specified here.
..
.TP
\fBUseSSLv2\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use SSLv2 for communication with the IMAP server over SSL?
(Default: \fIno\fR)
..
.TP
\fBUseSSLv3\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use SSLv3 for communication with the IMAP server over SSL?
(Default: \fIyes\fR if the imaps port is used, otherwise \fIno\fR)
..
.TP
\fBUseTLSv1\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use TLSv1 for communication with the IMAP server over SSL?
(Default: \fIyes\fR)
..
.TP
\fBOneToOne\fR
\fBisync\fR will ignore any \fIMailbox\fR specifications and instead pick up
all mailboxes from the local \fIMailDir\fR and remote \fIFolder\fR and map
them 1:1 onto each other according to their names.
\fBNOTE:\fR This directive is allowed only in the \fIglobal\fR
section (see below).
..
.TP
\fBInbox\fR \fImailbox\fR
Exception to the OneToOne mapping: the special IMAP mailbox \fIINBOX\fR
is mapped to the local \fImailbox\fR (relative to the \fIMailDir\fR).
\fBNOTE:\fR This directive is only meaningful in the \fIglobal\fR
section (see below).
..
.P
Configuration commands that appear prior to the first \fBMailbox\fR
command are considered to be \fIglobal\fR
options which are used as defaults when those specific options are not
specifically set for a defined Mailbox. For example, if you use the same
login name for several IMAP servers, you can put a \fBUser\fR command before
the first \fBMailbox\fR command, and then leave out the \fBUser\fR command
in the sections for each mailbox.
\fBisync\fR will then use the global value by default.
..
.SH FILES
.TP
.B ~/.isyncrc
Default configuration file
..
.SH BUGS
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
mbsync(1), mdconvert(1), mutt(1), maildir(5)
.P
Up to date information on \fBisync\fR can be found at http://isync.sf.net/
..
.SH AUTHORS
Originally written by Michael R. Elkins,
currently maintained by Oswald Buddenhagen.

111
src/compat/isync.h Normal file
View file

@ -0,0 +1,111 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <autodefs.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;
const char *old_server_name;
int servers;
int old_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;
const char *local_store_name;
const char *local_store_path;
int local_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 int HomeLen;
extern config_t global, *boxes;
extern const char *maildir, *xmaildir, *folder, *inbox;
extern int o2o, altmap, delete, expunge;
/* 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 sys_error( const char *, ... );
void ATTR_NORETURN oob( void );
#ifndef HAVE_MEMRCHR
void *memrchr( const void *s, int c, size_t n );
#endif
int starts_with( const char *str, int strl, const char *cmp, int cmpl );
int equals( const char *str, int strl, const char *cmp, int cmpl );

55
src/compat/isyncrc.sample Normal file
View file

@ -0,0 +1,55 @@
# Global configuration section
# Values here are used as defaults for any following Mailbox section that
# doesn't specify it.
# SSL server certificate file
CertificateFile ~/.isync.certs
# by default, expunge deleted messages (same as -e on command line)
Expunge yes
# by default delete messages in the local mailbox which no longer exist
# on the server
Delete yes
# copy deleted messages to the IMAP "Trash" folder
CopyDeletedTo "Trash"
# my default username, if different from the local username
User me
#Port 143
#Box INBOX
# don't download messages larger than 200K bytes
MaxSize 200000
###
### work mailbox
###
Mailbox /home/me/Mail/work
Host work.host.com
Pass xxxxxxxx
# define a shortcut so I can just use "isync work" from the command line
Alias work
# don't auto expunge messages in this box (overridden by -e on command line)
Expunge no
###
### personal mailbox
###
Mailbox /home/me/Mail/personal
Host host.play.com
# use a non-default port for this connection
Port 6789
Alias personal
###
### Remote mailbox over a SSH tunnel
###
Mailbox /home/me/Mail/remote
Host host.remote.com
Tunnel "ssh -q host.remote.com /usr/sbin/imapd"
Alias remote

439
src/compat/main.c Normal file
View file

@ -0,0 +1,439 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "isync.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#ifdef HAVE_GETOPT_LONG
# 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'},
{"pass", 1, NULL, 'P'},
{"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-2006,2008,2010-2012 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"
" -P, --pass PASSWORD IMAP password\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_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, delete, expunge;
const char *Home;
int HomeLen;
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;
}
HomeLen = strlen( Home );
/* defaults */
/* XXX the precedence is borked:
it's defaults < cmdline < file instead of defaults < file < cmdline */
#ifdef BSD
global.user = getenv( "USER" );
#else
global.user = getenv( "LOGNAME" );
#endif
global.port = 143;
global.box = ""; /* implicit INBOX in resulting Master/Slave entries */
global.use_namespace = 1;
global.require_ssl = 1;
global.use_tlsv1 = 1;
folder = "";
maildir = "~";
xmaildir = Home;
#define FLAGS "wW:alCLRc:defhp:qu:P:r:F:M:1I:s:vVD"
mod = all = list = ops = writeout = Quiet = Verbose = Debug = 0;
#ifdef 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':
delete = 1;
break;
case 'e':
expunge = 1;
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':
#ifdef HAVE_LIBSSL
if (!strncasecmp( "imaps:", optarg, 6 )) {
global.use_imaps = 1;
global.port = 993;
global.use_sslv2 = 0;
global.use_sslv3 = 1;
optarg += 6;
}
#endif
global.host = optarg;
mod = 1;
break;
case 'u':
global.user = optarg;
mod = 1;
break;
case 'P':
global.pass = optarg;
mod = 1;
break;
case 'D':
Debug = 1;
break;
case 'V':
Verbose++;
break;
case 'q':
Quiet++;
break;
case 'v':
version();
case 'h':
usage( 0 );
default:
usage( 1 );
}
}
if (!writeout)
fputs( "Notice: please run 'isync -w' and start using 'mbsync' directly.\n", stderr );
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 (!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 ))) {
sys_error( "Cannot list '%s'", xmaildir );
return 1;
}
while ((de = readdir( dir ))) {
if (*de->d_name == '.')
continue;
nfsnprintf( path2, sizeof(path2), "%s/%s/cur", xmaildir, de->d_name );
if (stat( path2, &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) {
sys_error( "Error: cannot create config file '%s'", outconfig );
return 1;
}
} else {
strcpy( path2, "/tmp/mbsyncrcXXXXXX" );
if ((fd = mkstemp( path2 )) < 0) {
sys_error( "Error: cannot create temporary config file" );
return 1;
}
}
write_config( fd );
if (writeout)
return 0;
args = 0;
add_arg( &args, "mbsync" );
while (--Verbose >= 0)
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" );
if (ops & OP_CREATE_REMOTE)
add_arg( &args, "-Cm" );
if (ops & OP_CREATE_LOCAL)
add_arg( &args, "-Cs" );
if (list)
add_arg( &args, "-lC" );
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 );
sys_error( "Cannot execute %s", args[0] );
return 1;
}

208
src/compat/util.c Normal file
View file

@ -0,0 +1,208 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "isync.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pwd.h>
#include <ctype.h>
void
sys_error( const char *msg, ... )
{
va_list va;
char buf[1024];
va_start( va, msg );
if ((unsigned)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
oob();
va_end( va );
perror( buf );
}
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 = malloc( len + 1 )))
return -1;
if (len >= (int)sizeof(tmp))
vsprintf( *strp, fmt, ap );
else
memcpy( *strp, tmp, len + 1 );
return len;
}
#endif
#ifndef HAVE_MEMRCHR
void *
memrchr( const void *s, int c, size_t n )
{
u_char *b = (u_char *)s, *e = b + n;
while (--e >= b)
if (*e == c)
return (void *)e;
return 0;
}
#endif
#ifndef HAVE_STRNLEN
int
strnlen( const char *str, size_t maxlen )
{
size_t len;
/* It's tempting to use memchr(), but it's allowed to read past the end of the actual string. */
for (len = 0; len < maxlen && str[len]; len++) {}
return len;
}
#endif
int
starts_with( const char *str, int strl, const char *cmp, int cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return (strl >= cmpl) && !memcmp( str, cmp, cmpl );
}
int
equals( const char *str, int strl, const char *cmp, int cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return (strl == cmpl) && !memcmp( str, cmp, cmpl );
}
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 (ret < 0)
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;
}

View file

@ -34,7 +34,7 @@
#include <stdlib.h>
#include <stdio.h>
static store_conf_t *stores;
store_conf_t *stores;
char *
get_arg( conffile_t *cfile, int required, int *comment )
@ -54,7 +54,7 @@ get_arg( conffile_t *cfile, int required, int *comment )
error( "%s:%d: parameter missing\n", cfile->file, cfile->line );
cfile->err = 1;
}
ret = NULL;
ret = 0;
} else {
for (escaped = 0, quoted = 0, ret = t = p; c; c = *p) {
p++;
@ -74,19 +74,19 @@ get_arg( conffile_t *cfile, int required, int *comment )
if (escaped) {
error( "%s:%d: unterminated escape sequence\n", cfile->file, cfile->line );
cfile->err = 1;
ret = NULL;
ret = 0;
}
if (quoted) {
error( "%s:%d: missing closing quote\n", cfile->file, cfile->line );
cfile->err = 1;
ret = NULL;
ret = 0;
}
}
cfile->rest = p;
return ret;
}
char
int
parse_bool( conffile_t *cfile )
{
if (!strcasecmp( cfile->val, "yes" ) ||
@ -121,13 +121,13 @@ parse_int( conffile_t *cfile )
return ret;
}
uint
int
parse_size( conffile_t *cfile )
{
char *p;
uint ret;
int ret;
ret = strtoul( cfile->val, &p, 10 );
ret = strtol (cfile->val, &p, 10);
if (*p == 'k' || *p == 'K')
ret *= 1024, p++;
else if (*p == 'm' || *p == 'M')
@ -174,21 +174,21 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
else if (!strcasecmp( "Flags", arg ))
*cops |= OP_FLAGS;
else if (!strcasecmp( "PullReNew", arg ))
conf->ops[N] |= OP_RENEW;
conf->ops[S] |= OP_RENEW;
else if (!strcasecmp( "PullNew", arg ))
conf->ops[N] |= OP_NEW;
conf->ops[S] |= OP_NEW;
else if (!strcasecmp( "PullDelete", arg ))
conf->ops[N] |= OP_DELETE;
conf->ops[S] |= OP_DELETE;
else if (!strcasecmp( "PullFlags", arg ))
conf->ops[N] |= OP_FLAGS;
conf->ops[S] |= OP_FLAGS;
else if (!strcasecmp( "PushReNew", arg ))
conf->ops[F] |= OP_RENEW;
conf->ops[M] |= OP_RENEW;
else if (!strcasecmp( "PushNew", arg ))
conf->ops[F] |= OP_NEW;
conf->ops[M] |= OP_NEW;
else if (!strcasecmp( "PushDelete", arg ))
conf->ops[F] |= OP_DELETE;
conf->ops[M] |= OP_DELETE;
else if (!strcasecmp( "PushFlags", arg ))
conf->ops[F] |= OP_FLAGS;
conf->ops[M] |= OP_FLAGS;
else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg ))
*cops |= XOP_PULL|XOP_PUSH;
else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg )) {
@ -196,8 +196,8 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
cfile->file, cfile->line, arg );
cfile->err = 1;
}
while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
conf->ops[F] |= XOP_HAVE_TYPE;
while ((arg = get_arg( cfile, ARG_OPTIONAL, 0 )));
conf->ops[M] |= XOP_HAVE_TYPE;
} else if (!strcasecmp( "SyncState", cfile->cmd ))
conf->sync_state = expand_strdup( cfile->val );
else if (!strcasecmp( "CopyArrivalDate", cfile->cmd ))
@ -214,23 +214,17 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
do {
if (!strcasecmp( "Both", arg )) {
*cops |= op;
} else if (!strcasecmp( "Far", arg )) {
conf->ops[F] |= op;
} else if (!strcasecmp( "Master", arg )) { // Pre-1.4 legacy
conf->ops[F] |= op;
cfile->ms_warn = 1;
} else if (!strcasecmp( "Near", arg )) {
conf->ops[N] |= op;
} else if (!strcasecmp( "Slave", arg )) { // Pre-1.4 legacy
conf->ops[N] |= op;
cfile->ms_warn = 1;
} else if (!strcasecmp( "Master", arg )) {
conf->ops[M] |= op;
} else if (!strcasecmp( "Slave", arg )) {
conf->ops[S] |= op;
} else if (strcasecmp( "None", arg )) {
error( "%s:%d: invalid %s arg '%s'\n",
cfile->file, cfile->line, boxOps[i].name, arg );
cfile->err = 1;
}
} while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
conf->ops[F] |= op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE);
} while ((arg = get_arg( cfile, ARG_OPTIONAL, 0 )));
conf->ops[M] |= op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE);
return 1;
}
}
@ -245,7 +239,7 @@ getcline( conffile_t *cfile )
char *arg;
int comment;
if (cfile->rest && (arg = get_arg( cfile, ARG_OPTIONAL, NULL ))) {
if (cfile->rest && (arg = get_arg( cfile, ARG_OPTIONAL, 0 ))) {
error( "%s:%d: excess token '%s'\n", cfile->file, cfile->line, arg );
cfile->err = 1;
}
@ -257,7 +251,7 @@ getcline( conffile_t *cfile )
continue;
return 1;
}
if (!(cfile->val = get_arg( cfile, ARG_REQUIRED, NULL )))
if (!(cfile->val = get_arg( cfile, ARG_REQUIRED, 0 )))
continue;
return 1;
}
@ -271,25 +265,25 @@ merge_ops( int cops, int ops[] )
int aops, op;
uint i;
aops = ops[F] | ops[N];
if (ops[F] & XOP_HAVE_TYPE) {
aops = ops[M] | ops[S];
if (ops[M] & XOP_HAVE_TYPE) {
if (aops & OP_MASK_TYPE) {
if (aops & cops & OP_MASK_TYPE) {
cfl:
error( "Conflicting Sync args specified.\n" );
return 1;
}
ops[F] |= cops & OP_MASK_TYPE;
ops[N] |= cops & OP_MASK_TYPE;
ops[M] |= cops & OP_MASK_TYPE;
ops[S] |= cops & OP_MASK_TYPE;
if (cops & XOP_PULL) {
if (ops[N] & OP_MASK_TYPE)
if (ops[S] & OP_MASK_TYPE)
goto cfl;
ops[N] |= OP_MASK_TYPE;
ops[S] |= OP_MASK_TYPE;
}
if (cops & XOP_PUSH) {
if (ops[F] & OP_MASK_TYPE)
if (ops[M] & OP_MASK_TYPE)
goto cfl;
ops[F] |= OP_MASK_TYPE;
ops[M] |= OP_MASK_TYPE;
}
} else if (cops & (OP_MASK_TYPE|XOP_MASK_DIR)) {
if (!(cops & OP_MASK_TYPE))
@ -297,27 +291,27 @@ merge_ops( int cops, int ops[] )
else if (!(cops & XOP_MASK_DIR))
cops |= XOP_PULL|XOP_PUSH;
if (cops & XOP_PULL)
ops[N] |= cops & OP_MASK_TYPE;
ops[S] |= cops & OP_MASK_TYPE;
if (cops & XOP_PUSH)
ops[F] |= cops & OP_MASK_TYPE;
ops[M] |= cops & OP_MASK_TYPE;
}
}
for (i = 0; i < as(boxOps); i++) {
op = boxOps[i].op;
if (ops[F] & (op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE))) {
if (ops[M] & (op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE))) {
if (aops & cops & op) {
error( "Conflicting %s args specified.\n", boxOps[i].name );
return 1;
}
ops[F] |= cops & op;
ops[N] |= cops & op;
ops[M] |= cops & op;
ops[S] |= cops & op;
}
}
return 0;
}
int
load_config( const char *where )
load_config( const char *where, int pseudo )
{
conffile_t cfile;
store_conf_t *store, **storeapp = &stores;
@ -325,18 +319,19 @@ load_config( const char *where )
group_conf_t *group, **groupapp = &groups;
string_list_t *chanlist, **chanlistapp;
char *arg, *p;
uint len, max_size;
int cops, gcops, glob_ok, fn, i;
int len, cops, gcops, max_size, ms, i;
char path[_POSIX_PATH_MAX];
char buf[1024];
if (!where) {
assert( !pseudo );
nfsnprintf( path, sizeof(path), "%s/." EXE "rc", Home );
cfile.file = path;
} else
cfile.file = where;
info( "Reading configuration file %s\n", cfile.file );
if (!pseudo)
info( "Reading configuration file %s\n", cfile.file );
if (!(cfile.fp = fopen( cfile.file, "r" ))) {
sys_error( "Cannot open config file '%s'", cfile.file );
@ -347,11 +342,9 @@ load_config( const char *where )
cfile.bufl = sizeof(buf) - 1;
cfile.line = 0;
cfile.err = 0;
cfile.ms_warn = 0;
cfile.rest = NULL;
cfile.rest = 0;
gcops = 0;
glob_ok = 1;
global_conf.expire_unread = -1;
reloop:
while (getcline( &cfile )) {
@ -361,14 +354,11 @@ load_config( const char *where )
if (drivers[i]->parse_store( &cfile, &store )) {
if (store) {
if (!store->max_size)
store->max_size = UINT_MAX;
if (!store->flat_delim)
store->flat_delim = "";
store->max_size = INT_MAX;
*storeapp = store;
storeapp = &store->next;
*storeapp = NULL;
*storeapp = 0;
}
glob_ok = 0;
goto reloop;
}
if (!strcasecmp( "Channel", cfile.cmd ))
@ -379,7 +369,7 @@ load_config( const char *where )
channel->expire_unread = global_conf.expire_unread;
channel->use_internal_date = global_conf.use_internal_date;
cops = 0;
max_size = UINT_MAX;
max_size = -1;
while (getcline( &cfile ) && cfile.cmd) {
if (!strcasecmp( "MaxSize", cfile.cmd ))
max_size = parse_size( &cfile );
@ -389,21 +379,13 @@ load_config( const char *where )
arg = cfile.val;
do
add_string_list( &channel->patterns, arg );
while ((arg = get_arg( &cfile, ARG_OPTIONAL, NULL )));
while ((arg = get_arg( &cfile, ARG_OPTIONAL, 0 )));
}
else if (!strcasecmp( "Far", cfile.cmd )) {
fn = F;
else if (!strcasecmp( "Master", cfile.cmd )) {
ms = M;
goto linkst;
} else if (!strcasecmp( "Master", cfile.cmd )) { // Pre-1.4 legacy
fn = F;
goto olinkst;
} else if (!strcasecmp( "Near", cfile.cmd )) {
fn = N;
goto linkst;
} else if (!strcasecmp( "Slave", cfile.cmd )) { // Pre-1.4 legacy
fn = N;
olinkst:
cfile.ms_warn = 1;
} else if (!strcasecmp( "Slave", cfile.cmd )) {
ms = S;
linkst:
if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) {
error( "%s:%d: malformed mailbox spec\n",
@ -414,7 +396,7 @@ load_config( const char *where )
*p = 0;
for (store = stores; store; store = store->next)
if (!strcmp( store->name, cfile.val + 1 )) {
channel->stores[fn] = store;
channel->stores[ms] = store;
goto stpcom;
}
error( "%s:%d: unknown store '%s'\n",
@ -423,32 +405,29 @@ load_config( const char *where )
continue;
stpcom:
if (*++p)
channel->boxes[fn] = nfstrdup( p );
channel->boxes[ms] = nfstrdup( p );
} else if (!getopt_helper( &cfile, &cops, channel )) {
error( "%s:%d: keyword '%s' is not recognized in Channel sections\n",
cfile.file, cfile.line, cfile.cmd );
error( "%s:%d: unknown keyword '%s'\n", cfile.file, cfile.line, cfile.cmd );
cfile.err = 1;
}
}
if (!channel->stores[F]) {
error( "channel '%s' refers to no far side store\n", channel->name );
if (!channel->stores[M]) {
error( "channel '%s' refers to no master store\n", channel->name );
cfile.err = 1;
} else if (!channel->stores[N]) {
error( "channel '%s' refers to no near side store\n", channel->name );
} else if (!channel->stores[S]) {
error( "channel '%s' refers to no slave store\n", channel->name );
cfile.err = 1;
} else if (merge_ops( cops, channel->ops ))
cfile.err = 1;
else {
if (max_size != UINT_MAX) {
if (max_size >= 0) {
if (!max_size)
max_size = UINT_MAX;
channel->stores[F]->max_size = channel->stores[N]->max_size = max_size;
max_size = INT_MAX;
channel->stores[M]->max_size = channel->stores[S]->max_size = max_size;
}
*channelapp = channel;
channelapp = &channel->next;
}
glob_ok = 0;
goto reloop;
}
else if (!strcasecmp( "Group", cfile.cmd ))
{
@ -456,19 +435,21 @@ load_config( const char *where )
group->name = nfstrdup( cfile.val );
*groupapp = group;
groupapp = &group->next;
*groupapp = NULL;
*groupapp = 0;
chanlistapp = &group->channels;
*chanlistapp = NULL;
while ((arg = get_arg( &cfile, ARG_OPTIONAL, NULL ))) {
*chanlistapp = 0;
while ((arg = get_arg( &cfile, ARG_OPTIONAL, 0 ))) {
addone:
len = strlen( arg );
chanlist = nfmalloc( sizeof(*chanlist) + len );
memcpy( chanlist->string, arg, len + 1 );
*chanlistapp = chanlist;
chanlistapp = &chanlist->next;
*chanlistapp = NULL;
*chanlistapp = 0;
}
while (getcline( &cfile ) && cfile.cmd) {
while (getcline( &cfile )) {
if (!cfile.cmd)
goto reloop;
if (!strcasecmp( "Channel", cfile.cmd ) ||
!strcasecmp( "Channels", cfile.cmd ))
{
@ -477,13 +458,12 @@ load_config( const char *where )
}
else
{
error( "%s:%d: keyword '%s' is not recognized in Group sections\n",
error( "%s:%d: unknown keyword '%s'\n",
cfile.file, cfile.line, cfile.cmd );
cfile.err = 1;
}
}
glob_ok = 0;
goto reloop;
break;
}
else if (!strcasecmp( "FSync", cfile.cmd ))
{
@ -505,14 +485,14 @@ load_config( const char *where )
else if (!strcasecmp( "BufferLimit", cfile.cmd ))
{
BufferLimit = parse_size( &cfile );
if (!BufferLimit) {
error( "%s:%d: BufferLimit cannot be zero\n", cfile.file, cfile.line );
if (BufferLimit <= 0) {
error( "%s:%d: BufferLimit must be positive\n", cfile.file, cfile.line );
cfile.err = 1;
}
}
else if (!getopt_helper( &cfile, &gcops, &global_conf ))
{
error( "%s:%d: '%s' is not a recognized section-starting or global keyword\n",
error( "%s:%d: unknown section keyword '%s'\n",
cfile.file, cfile.line, cfile.cmd );
cfile.err = 1;
while (getcline( &cfile ))
@ -520,17 +500,12 @@ load_config( const char *where )
goto reloop;
break;
}
if (!glob_ok) {
error( "%s:%d: global options may not follow sections\n",
cfile.file, cfile.line );
cfile.err = 1;
}
}
fclose (cfile.fp);
if (cfile.ms_warn)
warn( "Notice: Master/Slave are deprecated; use Far/Near instead.\n" );
cfile.err |= merge_ops( gcops, global_conf.ops );
if (!global_conf.sync_state)
global_conf.sync_state = expand_strdup( "~/." EXE "/" );
if (!cfile.err && pseudo)
unlink( where );
return cfile.err;
}

View file

@ -25,14 +25,13 @@
#include "common.h"
typedef struct {
typedef struct conffile {
const char *file;
FILE *fp;
char *buf;
int bufl;
int line;
int err;
int ms_warn;
char *cmd, *val, *rest;
} conffile_t;
@ -41,11 +40,11 @@ typedef struct {
char *get_arg( conffile_t *cfile, int required, int *comment );
char parse_bool( conffile_t *cfile );
int parse_bool( conffile_t *cfile );
int parse_int( conffile_t *cfile );
uint parse_size( conffile_t *cfile );
int parse_size( conffile_t *cfile );
int getcline( conffile_t *cfile );
int merge_ops( int cops, int ops[] );
int load_config( const char *filename );
int load_config( const char *filename, int pseudo );
#endif

View file

@ -27,15 +27,6 @@
driver_t *drivers[N_DRIVERS] = { &maildir_driver, &imap_driver };
uint
count_generic_messages( message_t *msgs )
{
uint count = 0;
for (; msgs; msgs = msgs->next)
count++;
return count;
}
void
free_generic_messages( message_t *msgs )
{
@ -43,13 +34,12 @@ free_generic_messages( message_t *msgs )
for (; msgs; msgs = tmsg) {
tmsg = msgs->next;
free( msgs->msgid );
free( msgs );
}
}
void
parse_generic_store( store_conf_t *store, conffile_t *cfg, const char *type )
parse_generic_store( store_conf_t *store, conffile_t *cfg )
{
if (!strcasecmp( "Trash", cfg->cmd )) {
store->trash = nfstrdup( cfg->val );
@ -72,7 +62,7 @@ parse_generic_store( store_conf_t *store, conffile_t *cfg, const char *type )
}
store->flat_delim = nfstrdup( cfg->val );
} else {
error( "%s:%d: keyword '%s' is not recognized in %s sections\n", cfg->file, cfg->line, cfg->cmd, type );
error( "%s:%d: unknown keyword '%s'\n", cfg->file, cfg->line, cfg->cmd );
cfg->err = 1;
}
}

View file

@ -31,86 +31,87 @@ typedef struct driver driver_t;
#define FAIL_WAIT 1 /* Retry after some time (if at all) */
#define FAIL_FINAL 2 /* Don't retry until store reconfiguration */
#define STORE_CONF \
struct store_conf *next; \
char *name; \
driver_t *driver; \
const char *flat_delim; \
const char *map_inbox; \
const char *trash; \
uint max_size; /* off_t is overkill */ \
char trash_remote_new, trash_only_new;
typedef struct store_conf {
STORE_CONF
struct store_conf *next;
char *name;
driver_t *driver;
const char *path; /* should this be here? its interpretation is driver-specific */
const char *flat_delim;
const char *map_inbox;
const char *trash;
uint max_size; /* off_t is overkill */
char trash_remote_new, trash_only_new;
} store_conf_t;
/* For message->flags */
/* Keep the mailbox driver flag definitions in sync: */
/* grep for MAILBOX_DRIVER_FLAG */
/* 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_FORWARDED (1<<2) /* Passed */
#define F_ANSWERED (1<<3) /* Replied */
#define F_SEEN (1<<4) /* Seen */
#define F_DELETED (1<<5) /* Trashed */
#define NUM_FLAGS 6
#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 */
// The following are only for IMAP FETCH response parsing
#define M_DATE (1<<3)
#define M_SIZE (1<<4)
#define M_BODY (1<<5)
#define M_HEADER (1<<6)
#define TUIDL 12
#define MESSAGE(message) \
message *next; \
struct sync_rec *srec; \
char *msgid; /* owned */ \
/* string_list_t *keywords; */ \
uint size; /* zero implies "not fetched" */ \
uint uid; \
uchar flags, status; \
char tuid[TUIDL];
typedef struct message {
MESSAGE(struct message)
struct message *next;
struct sync_rec *srec;
/* string_list_t *keywords; */
size_t size; /* zero implies "not fetched" */
int uid;
uchar flags, status;
char tuid[TUIDL];
} message_t;
// For driver_t->prepare_load_box(), which may amend the passed flags.
// The drivers don't use the first two, but may set them if loading the
// particular range is required to handle some other flag; note that these
// ranges may overlap.
#define OPEN_OLD (1<<0) // Paired messages *in* this store.
#define OPEN_NEW (1<<1) // Messages (possibly) not yet propagated *from* this store.
#define OPEN_FLAGS (1<<2) // Note that fetch_msg() gets the flags regardless.
#define OPEN_NEW_SIZE (1<<4)
/* 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_EXPUNGE (1<<5)
#define OPEN_SETFLAGS (1<<6)
#define OPEN_APPEND (1<<7)
#define OPEN_FIND (1<<8)
#define OPEN_OLD_IDS (1<<9)
#define UIDVAL_BAD ((uint)-1)
#define STORE(store) \
store *next; \
driver_t *driver; \
store##_conf *conf; /* foreign */
typedef struct store {
STORE(struct store)
struct store *next;
store_conf_t *conf; /* foreign */
string_list_t *boxes; /* _list results - own */
char listed; /* was _list already run? */
void (*bad_callback)( void *aux );
void *bad_callback_aux;
/* currently open mailbox */
char *path; /* own */
message_t *msgs; /* own */
int uidvalidity;
int uidnext; /* from SELECT responses */
uint 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;
/* When the callback is invoked (at most once per store), the store is fubar;
* call the driver's cancel_store() to dispose of it. */
static INLINE void
set_bad_callback( store_t *ctx, void (*cb)( void *aux ), void *aux )
{
ctx->bad_callback = cb;
ctx->bad_callback_aux = aux;
}
typedef struct {
char *data;
uint len;
int len;
time_t date;
uchar flags;
} msg_data_t;
@ -120,15 +121,10 @@ typedef struct {
#define DRV_MSG_BAD 1
/* Something is wrong with the current mailbox - probably it is somehow inaccessible. */
#define DRV_BOX_BAD 2
/* Failed to connect store. */
#define DRV_STORE_BAD 3
/* The command has been cancel()ed or cancel_store()d. */
#define DRV_CANCELED 4
#define DRV_CANCELED 3
/* All memory belongs to the driver's user, unless stated otherwise. */
// If the driver is NOT DRV_ASYNC, memory owned by the driver returned
// through callbacks MUST remain valid until a related subsequent command
// is invoked, as the proxy driver may deliver these pointers with delay.
/*
This flag says that the driver CAN store messages with CRLFs,
@ -140,57 +136,39 @@ typedef struct {
This flag says that the driver will act upon (DFlags & VERBOSE).
*/
#define DRV_VERBOSE 2
/*
This flag says that the driver operates asynchronously.
*/
#define DRV_ASYNC 4
#define LIST_INBOX 1
#define LIST_PATH 2
#define LIST_PATH_MAYBE 4
#define xint uint // For auto-generation of appropriate printf() formats.
#define LIST_PATH 1
#define LIST_INBOX 2
struct driver {
/* Return driver capabilities. */
xint (*get_caps)( store_t *ctx );
int flags;
/* Parse configuration. */
int (*parse_store)( conffile_t *cfg, store_conf_t **storep );
/* Close remaining server connections. All stores must be discarded first. */
/* Close remaining server connections. All stores must be disowned first. */
void (*cleanup)( void );
/* Allocate a store with the given configuration. This is expected to
* return quickly, and must not fail. */
store_t *(*alloc_store)( store_conf_t *conf, const char *label );
/* Open a store with the given configuration. This may recycle existing
* server connections. Upon failure, a null store is passed to the callback. */
void (*open_store)( store_conf_t *conf, const char *label,
void (*cb)( store_t *ctx, void *aux ), void *aux );
/* When this callback is invoked (at most once per store), the store is fubar;
* call cancel_store() to dispose of it. */
void (*set_bad_callback)( store_t *ctx, void (*cb)( void *aux ), void *aux );
/* Open/connect the store. This may recycle existing server connections. */
void (*connect_store)( store_t *ctx,
void (*cb)( int sts, void *aux ), void *aux );
/* Discard the store. Underlying server connection may be kept alive. */
void (*free_store)( store_t *ctx );
/* Mark the store as available for recycling. Server connection may be kept alive. */
void (*disown_store)( store_t *ctx );
/* Discard the store after a bad_callback. The server connections will be closed.
* Pending commands will have their callbacks synchronously invoked with DRV_CANCELED. */
void (*cancel_store)( store_t *ctx );
/* List the mailboxes in this store. Flags are ORed LIST_* values.
* The returned box list remains owned by the driver. */
/* List the mailboxes in this store. Flags are ORed LIST_* values. */
void (*list_store)( store_t *ctx, int flags,
void (*cb)( int sts, string_list_t *boxes, void *aux ), void *aux );
void (*cb)( int sts, void *aux ), void *aux );
/* Invoked before open_box(), this informs the driver which box is to be opened. */
/* Invoked before open_box(), this informs the driver which box is to be opened.
* As a side effect, this should resolve ctx->path if applicable. */
int (*select_box)( store_t *ctx, const char *name );
/* Get the selected box' on-disk path, if applicable, null otherwise. */
const char *(*get_box_path)( store_t *ctx );
/* Create the selected mailbox. */
void (*create_box)( store_t *ctx,
void (*cb)( int sts, void *aux ), void *aux );
@ -198,13 +176,7 @@ struct driver {
/* Open the selected mailbox.
* Note that this should not directly complain about failure to open. */
void (*open_box)( store_t *ctx,
void (*cb)( int sts, uint uidvalidity, void *aux ), void *aux );
/* Return the minimal UID the next stored message will have. */
uint (*get_uidnext)( store_t *ctx );
/* Return the flags that can be stored in the selected mailbox. */
xint (*get_supported_flags)( store_t *ctx );
void (*cb)( int sts, void *aux ), void *aux );
/* Confirm that the open mailbox is empty. */
int (*confirm_box_empty)( store_t *ctx );
@ -221,54 +193,47 @@ struct driver {
/* Invoked before load_box(), this informs the driver which operations (OP_*)
* will be performed on the mailbox. The driver may extend the set by implicitly
* needed or available operations. Returns this possibly extended set. */
xint (*prepare_load_box)( store_t *ctx, xint opts );
* needed or available operations. */
void (*prepare_load_box)( store_t *ctx, int opts );
/* Load the message attributes needed to perform the requested operations.
* Consider only messages with UIDs between minuid and maxuid (inclusive)
* and those named in the excs array (smaller than minuid).
* The driver takes ownership of the excs array.
* Messages starting with finduid need to have the TUID populated when OPEN_FIND is set.
* Messages up to pairuid need to have the Message-Id populated when OPEN_OLD_IDS is set.
* Messages up to newuid need to have the size populated when OPEN_OLD_SIZE is set;
* likewise messages above newuid when OPEN_NEW_SIZE is set.
* The returned message list remains owned by the driver. */
void (*load_box)( store_t *ctx, uint minuid, uint maxuid, uint finduid, uint pairuid, uint newuid, uint_array_t excs,
void (*cb)( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux ), void *aux );
* The driver takes ownership of the excs array. Messages below newuid do not need
* to have the TUID populated even if OPEN_FIND is set. */
void (*load_box)( store_t *ctx, int minuid, int maxuid, int newuid, int *excs, int nexcs,
void (*cb)( int sts, void *aux ), void *aux );
/* Fetch the contents and flags of the given message from the current mailbox.
* If minimal is non-zero, fetch only a placeholder for the requested message -
* ideally, this is precisely the header, but it may be more. */
void (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data, int minimal,
/* Fetch the contents and flags of the given message from the current mailbox. */
void (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data,
void (*cb)( int sts, void *aux ), void *aux );
/* Store the given message to either the current mailbox or the trash folder.
* If the new copy's UID can be immediately determined, return it, otherwise 0. */
* If the new copy's UID can be immediately determined, return it, otherwise -2. */
void (*store_msg)( store_t *ctx, msg_data_t *data, int to_trash,
void (*cb)( int sts, uint uid, void *aux ), void *aux );
void (*cb)( int sts, int uid, void *aux ), void *aux );
/* Index the messages which have newly appeared in the mailbox, including their
* temporary UID headers. This is needed if store_msg() does not guarantee returning
* a UID; otherwise the driver needs to implement only the OPEN_FIND flag.
* The returned message list remains owned by the driver. */
void (*find_new_msgs)( store_t *ctx, uint newuid,
void (*cb)( int sts, message_t *msgs, void *aux ), void *aux );
* a UID; otherwise the driver needs to implement only the OPEN_FIND flag. */
void (*find_new_msgs)( store_t *ctx, int newuid,
void (*cb)( int sts, void *aux ), void *aux );
/* Add/remove the named flags to/from the given message. The message may be either
* a pre-fetched one (in which case the in-memory representation is updated),
* or it may be identifed by UID only. The operation may be delayed until commit()
* is called. */
void (*set_msg_flags)( store_t *ctx, message_t *msg, uint uid, int add, int del,
void (*set_msg_flags)( store_t *ctx, message_t *msg, int uid, int add, int del, /* msg can be null, therefore uid as a fallback */
void (*cb)( int sts, void *aux ), void *aux );
/* Move the given message from the current mailbox to the trash folder.
* This may expunge the original message immediately, but it needn't to. */
void (*trash_msg)( store_t *ctx, message_t *msg,
void (*trash_msg)( store_t *ctx, message_t *msg, /* This may expunge the original message immediately, but it needn't to */
void (*cb)( int sts, void *aux ), void *aux );
/* Expunge deleted messages from the current mailbox and close it.
* There is no need to explicitly close a mailbox if no expunge is needed. */
void (*close_box)( store_t *ctx,
void (*close_box)( store_t *ctx, /* IMAP-style: expunge inclusive */
void (*cb)( int sts, void *aux ), void *aux );
/* Cancel queued commands which are not in flight yet; they will have their
@ -282,21 +247,18 @@ struct driver {
void (*commit_cmds)( store_t *ctx );
/* Get approximate amount of memory occupied by the driver. */
uint (*get_memory_usage)( store_t *ctx );
int (*memory_usage)( store_t *ctx );
/* Get the FAIL_* state of the driver. */
int (*get_fail_state)( store_conf_t *conf );
int (*fail_state)( store_conf_t *conf );
};
uint count_generic_messages( message_t * );
void free_generic_messages( message_t * );
void parse_generic_store( store_conf_t *store, conffile_t *cfg, const char *type );
store_t *proxy_alloc_store( store_t *real_ctx, const char *label );
void parse_generic_store( store_conf_t *store, conffile_t *cfg );
#define N_DRIVERS 2
extern driver_t *drivers[N_DRIVERS];
extern driver_t maildir_driver, imap_driver, proxy_driver;
extern driver_t maildir_driver, imap_driver;
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,447 +0,0 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2017 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, see <http://www.gnu.org/licenses/>.
*
* As a special exception, mbsync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
#include "driver.h"
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
typedef struct gen_cmd gen_cmd_t;
typedef union proxy_store {
store_t gen;
struct {
STORE(union proxy_store)
const char *label; // foreign
uint ref_count;
driver_t *real_driver;
store_t *real_store;
gen_cmd_t *done_cmds, **done_cmds_append;
gen_cmd_t *check_cmds, **check_cmds_append;
wakeup_t wakeup;
void (*bad_callback)( void *aux );
void *bad_callback_aux;
};
} proxy_store_t;
static void ATTR_PRINTFLIKE(1, 2)
debug( const char *msg, ... )
{
va_list va;
va_start( va, msg );
vdebug( DEBUG_DRV, msg, va );
va_end( va );
}
static void ATTR_PRINTFLIKE(1, 2)
debugn( const char *msg, ... )
{
va_list va;
va_start( va, msg );
vdebugn( DEBUG_DRV, msg, va );
va_end( va );
}
/* Keep the mailbox driver flag definitions in sync: */
/* grep for MAILBOX_DRIVER_FLAG */
/* The order is according to alphabetical maildir flag sort */
static const char Flags[] = { 'D', 'F', 'P', 'R', 'S', 'T' };
static char *
proxy_make_flags( uchar flags, char *buf )
{
uint i, d;
for (d = 0, i = 0; i < as(Flags); i++)
if (flags & (1 << i))
buf[d++] = Flags[i];
buf[d] = 0;
return buf;
}
static void
proxy_store_deref( proxy_store_t *ctx )
{
if (!--ctx->ref_count) {
assert( !pending_wakeup( &ctx->wakeup ) );
free( ctx );
}
}
static int curr_tag;
#define GEN_CMD \
uint ref_count; \
int tag; \
proxy_store_t *ctx; \
gen_cmd_t *next; \
void (*queued_cb)( gen_cmd_t *gcmd );
struct gen_cmd {
GEN_CMD
};
#define GEN_STS_CMD \
GEN_CMD \
int sts;
typedef union {
gen_cmd_t gen;
struct {
GEN_STS_CMD
};
} gen_sts_cmd_t;
static gen_cmd_t *
proxy_cmd_new( proxy_store_t *ctx, uint sz )
{
gen_cmd_t *cmd = nfmalloc( sz );
cmd->ref_count = 2;
cmd->tag = ++curr_tag;
cmd->ctx = ctx;
ctx->ref_count++;
return cmd;
}
static void
proxy_cmd_done( gen_cmd_t *cmd )
{
if (!--cmd->ref_count) {
proxy_store_deref( cmd->ctx );
free( cmd );
}
}
static void
proxy_wakeup( void *aux )
{
proxy_store_t *ctx = (proxy_store_t *)aux;
gen_cmd_t *cmd = ctx->done_cmds;
assert( cmd );
if (!(ctx->done_cmds = cmd->next))
ctx->done_cmds_append = &ctx->done_cmds;
else
conf_wakeup( &ctx->wakeup, 0 );
cmd->queued_cb( cmd );
proxy_cmd_done( cmd );
}
static void
proxy_invoke_cb( gen_cmd_t *cmd, void (*cb)( gen_cmd_t * ), int checked, const char *name )
{
if (DFlags & FORCEASYNC) {
debug( "%s[% 2d] Callback queue %s%s\n", cmd->ctx->label, cmd->tag, name, checked ? " (checked)" : "" );
cmd->queued_cb = cb;
cmd->next = NULL;
if (checked) {
*cmd->ctx->check_cmds_append = cmd;
cmd->ctx->check_cmds_append = &cmd->next;
} else {
*cmd->ctx->done_cmds_append = cmd;
cmd->ctx->done_cmds_append = &cmd->next;
conf_wakeup( &cmd->ctx->wakeup, 0 );
}
} else {
cb( cmd );
proxy_cmd_done( cmd );
}
}
static void
proxy_flush_checked_cmds( proxy_store_t *ctx )
{
if (ctx->check_cmds) {
*ctx->done_cmds_append = ctx->check_cmds;
ctx->done_cmds_append = ctx->check_cmds_append;
ctx->check_cmds_append = &ctx->check_cmds;
ctx->check_cmds = NULL;
conf_wakeup( &ctx->wakeup, 0 );
}
}
static void
proxy_cancel_checked_cmds( proxy_store_t *ctx )
{
gen_cmd_t *cmd;
while ((cmd = ctx->check_cmds)) {
if (!(ctx->check_cmds = cmd->next))
ctx->check_cmds_append = &ctx->check_cmds;
((gen_sts_cmd_t *)cmd)->sts = DRV_CANCELED;
cmd->queued_cb( cmd );
}
}
#if 0
//# TEMPLATE GETTER
static @type@proxy_@name@( store_t *gctx )
{
proxy_store_t *ctx = (proxy_store_t *)gctx;
@type@rv = ctx->real_driver->@name@( ctx->real_store );
debug( "%sCalled @name@, ret=@fmt@\n", ctx->label, rv );
return rv;
}
//# END
//# TEMPLATE REGULAR
static @type@proxy_@name@( store_t *gctx@decl_args@ )
{
proxy_store_t *ctx = (proxy_store_t *)gctx;
@pre_print_args@
debug( "%sEnter @name@@print_fmt_args@\n", ctx->label@print_pass_args@ );
@print_args@
@type@rv = ctx->real_driver->@name@( ctx->real_store@pass_args@ );
debug( "%sLeave @name@, ret=@fmt@\n", ctx->label, rv );
return rv;
}
//# END
//# TEMPLATE REGULAR_VOID
static @type@proxy_@name@( store_t *gctx@decl_args@ )
{
proxy_store_t *ctx = (proxy_store_t *)gctx;
@pre_print_args@
debug( "%sEnter @name@@print_fmt_args@\n", ctx->label@print_pass_args@ );
@print_args@
ctx->real_driver->@name@( ctx->real_store@pass_args@ );
debug( "%sLeave @name@\n", ctx->label );
@action@
}
//# END
//# TEMPLATE CALLBACK
typedef union {
@gen_cmd_t@ gen;
struct {
@GEN_CMD@
@decl_cb_state@
void (*callback)( @decl_cb_args@void *aux );
void *callback_aux;
@decl_state@
};
} @name@_cmd_t;
static void
proxy_do_@name@_cb( gen_cmd_t *gcmd )
{
@name@_cmd_t *cmd = (@name@_cmd_t *)gcmd;
@pre_print_cb_args@
debug( "%s[% 2d] Callback enter @name@@print_fmt_cb_args@\n", cmd->ctx->label, cmd->tag@print_pass_cb_args@ );
@print_cb_args@
cmd->callback( @pass_cb_args@cmd->callback_aux );
debug( "%s[% 2d] Callback leave @name@\n", cmd->ctx->label, cmd->tag );
}
static void
proxy_@name@_cb( @decl_cb_args@void *aux )
{
@name@_cmd_t *cmd = (@name@_cmd_t *)aux;
@save_cb_args@
proxy_invoke_cb( @gen_cmd@, proxy_do_@name@_cb, @checked@, "@name@" );
}
static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), void *aux )
{
proxy_store_t *ctx = (proxy_store_t *)gctx;
@name@_cmd_t *cmd = (@name@_cmd_t *)proxy_cmd_new( ctx, sizeof(@name@_cmd_t) );
cmd->callback = cb;
cmd->callback_aux = aux;
@assign_state@
@pre_print_args@
debug( "%s[% 2d] Enter @name@@print_fmt_args@\n", ctx->label, cmd->tag@print_pass_args@ );
@print_args@
ctx->real_driver->@name@( ctx->real_store@pass_args@, proxy_@name@_cb, cmd );
debug( "%s[% 2d] Leave @name@\n", ctx->label, cmd->tag );
proxy_cmd_done( @gen_cmd@ );
}
//# END
//# UNDEFINE list_store_print_fmt_cb_args
//# UNDEFINE list_store_print_pass_cb_args
//# DEFINE list_store_print_cb_args
if (cmd->sts == DRV_OK) {
for (string_list_t *box = cmd->boxes; box; box = box->next)
debug( " %s\n", box->string );
}
//# END
//# DEFINE load_box_pre_print_args
static char ubuf[12];
//# END
//# DEFINE load_box_print_fmt_args , [%u,%s] (find >= %u, paired <= %u, new > %u)
//# DEFINE load_box_print_pass_args , minuid, (maxuid == UINT_MAX) ? "inf" : (nfsnprintf( ubuf, sizeof(ubuf), "%u", maxuid ), ubuf), finduid, pairuid, newuid
//# DEFINE load_box_print_args
if (excs.size) {
debugn( " excs:" );
for (uint t = 0; t < excs.size; t++)
debugn( " %u", excs.data[t] );
debug( "\n" );
}
//# END
//# DEFINE load_box_print_fmt_cb_args , sts=%d, total=%d, recent=%d
//# DEFINE load_box_print_pass_cb_args , cmd->sts, cmd->total_msgs, cmd->recent_msgs
//# DEFINE load_box_print_cb_args
if (cmd->sts == DRV_OK) {
static char fbuf[as(Flags) + 1];
for (message_t *msg = cmd->msgs; msg; msg = msg->next)
debug( " uid=%-5u flags=%-4s size=%-6u tuid=%." stringify(TUIDL) "s\n",
msg->uid, (msg->status & M_FLAGS) ? (proxy_make_flags( msg->flags, fbuf ), fbuf) : "?", msg->size, *msg->tuid ? msg->tuid : "?" );
}
//# END
//# DEFINE find_new_msgs_print_fmt_cb_args , sts=%d
//# DEFINE find_new_msgs_print_pass_cb_args , cmd->sts
//# DEFINE find_new_msgs_print_cb_args
if (cmd->sts == DRV_OK) {
for (message_t *msg = cmd->msgs; msg; msg = msg->next)
debug( " uid=%-5u tuid=%." stringify(TUIDL) "s\n", msg->uid, msg->tuid );
}
//# END
//# DEFINE fetch_msg_decl_state
msg_data_t *data;
//# END
//# DEFINE fetch_msg_assign_state
cmd->data = data;
//# END
//# DEFINE fetch_msg_print_fmt_args , uid=%u, want_flags=%s, want_date=%s
//# DEFINE fetch_msg_print_pass_args , msg->uid, !(msg->status & M_FLAGS) ? "yes" : "no", data->date ? "yes" : "no"
//# DEFINE fetch_msg_pre_print_cb_args
static char fbuf[as(Flags) + 1];
proxy_make_flags( cmd->data->flags, fbuf );
//# END
//# DEFINE fetch_msg_print_fmt_cb_args , flags=%s, date=%lld, size=%u
//# DEFINE fetch_msg_print_pass_cb_args , fbuf, (long long)cmd->data->date, cmd->data->len
//# DEFINE fetch_msg_print_cb_args
if (cmd->sts == DRV_OK && (DFlags & DEBUG_DRV_ALL)) {
printf( "%s=========\n", cmd->ctx->label );
fwrite( cmd->data->data, cmd->data->len, 1, stdout );
printf( "%s=========\n", cmd->ctx->label );
fflush( stdout );
}
//# END
//# DEFINE store_msg_pre_print_args
static char fbuf[as(Flags) + 1];
proxy_make_flags( data->flags, fbuf );
//# END
//# DEFINE store_msg_print_fmt_args , flags=%s, date=%lld, size=%u, to_trash=%s
//# DEFINE store_msg_print_pass_args , fbuf, (long long)data->date, data->len, to_trash ? "yes" : "no"
//# DEFINE store_msg_print_args
if (DFlags & DEBUG_DRV_ALL) {
printf( "%s>>>>>>>>>\n", ctx->label );
fwrite( data->data, data->len, 1, stdout );
printf( "%s>>>>>>>>>\n", ctx->label );
fflush( stdout );
}
//# END
//# DEFINE set_msg_flags_pre_print_args
static char fbuf1[as(Flags) + 1], fbuf2[as(Flags) + 1];
proxy_make_flags( add, fbuf1 );
proxy_make_flags( del, fbuf2 );
//# END
//# DEFINE set_msg_flags_print_fmt_args , uid=%u, add=%s, del=%s
//# DEFINE set_msg_flags_print_pass_args , uid, fbuf1, fbuf2
//# DEFINE set_msg_flags_checked sts == DRV_OK
//# DEFINE trash_msg_print_fmt_args , uid=%u
//# DEFINE trash_msg_print_pass_args , msg->uid
//# DEFINE commit_cmds_print_args
proxy_flush_checked_cmds( ctx );
//# END
//# DEFINE cancel_cmds_print_cb_args
proxy_cancel_checked_cmds( cmd->ctx );
//# END
//# DEFINE free_store_print_args
proxy_cancel_checked_cmds( ctx );
//# END
//# DEFINE free_store_action
proxy_store_deref( ctx );
//# END
//# DEFINE cancel_store_print_args
proxy_cancel_checked_cmds( ctx );
//# END
//# DEFINE cancel_store_action
proxy_store_deref( ctx );
//# END
#endif
//# SPECIAL set_bad_callback
static void
proxy_set_bad_callback( store_t *gctx, void (*cb)( void *aux ), void *aux )
{
proxy_store_t *ctx = (proxy_store_t *)gctx;
ctx->bad_callback = cb;
ctx->bad_callback_aux = aux;
}
static void
proxy_invoke_bad_callback( proxy_store_t *ctx )
{
ctx->ref_count++;
debug( "%sCallback enter bad store\n", ctx->label );
ctx->bad_callback( ctx->bad_callback_aux );
debug( "%sCallback leave bad store\n", ctx->label );
proxy_store_deref( ctx );
}
//# EXCLUDE alloc_store
store_t *
proxy_alloc_store( store_t *real_ctx, const char *label )
{
proxy_store_t *ctx;
ctx = nfcalloc( sizeof(*ctx) );
ctx->driver = &proxy_driver;
ctx->gen.conf = real_ctx->conf;
ctx->ref_count = 1;
ctx->label = label;
ctx->done_cmds_append = &ctx->done_cmds;
ctx->check_cmds_append = &ctx->check_cmds;
ctx->real_driver = real_ctx->driver;
ctx->real_store = real_ctx;
ctx->real_driver->set_bad_callback( ctx->real_store, (void (*)(void *))proxy_invoke_bad_callback, ctx );
init_wakeup( &ctx->wakeup, proxy_wakeup, ctx );
return &ctx->gen;
}
//# EXCLUDE parse_store
//# EXCLUDE cleanup
//# EXCLUDE get_fail_state
#include "drv_proxy.inc"

View file

@ -1,198 +0,0 @@
#!/usr/bin/perl
#
# mbsync - mailbox synchronizer
# Copyright (C) 2017 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, see <http://www.gnu.org/licenses/>.
#
# As a special exception, mbsync may be linked with the OpenSSL library,
# despite that library's more restrictive license.
#
use strict;
use warnings;
die("Usage: $0 driver.h drv_proxy.c drv_proxy.inc\n")
if ($#ARGV != 2);
my ($in_header, $in_source, $out_source) = @ARGV;
my %templates;
my %defines;
my %excluded;
my %special;
open(my $ins, $in_source) or die("Cannot open $in_source: $!\n");
my $template;
my $define;
my $conts;
while (<$ins>) {
if ($template) {
if (/^\/\/\# END$/) {
$templates{$template} = $conts;
$template = undef;
} else {
$conts .= $_;
}
} elsif ($define) {
if (/^\/\/\# END$/) {
$defines{$define} = $conts;
$define = undef;
} else {
($_ eq "\n") or s/^\t// or die("DEFINE content is not indented: $_");
$conts .= $_;
}
} else {
if (/^\/\/\# TEMPLATE (\w+)$/) {
$template = $1;
$conts = "";
} elsif (/^\/\/\# DEFINE (\w+)$/) {
$define = $1;
$conts = "";
} elsif (/^\/\/\# DEFINE (\w+) (.*)$/) {
$defines{$1} = $2;
} elsif (/^\/\/\# UNDEFINE (\w+)$/) {
$defines{$1} = "";
} elsif (/^\/\/\# EXCLUDE (\w+)$/) {
$excluded{$1} = 1;
} elsif (/^\/\/\# SPECIAL (\w+)$/) {
$special{$1} = 1;
}
}
}
close($ins);
open(my $inh, $in_header) or die("Cannot open $in_header: $!\n");
my $sts = 0;
my $cont = "";
while (<$inh>) {
if ($sts == 0) {
if (/^struct driver \{$/) {
$sts = 1;
}
} elsif ($sts == 1) {
if (/^\};$/) {
$sts = 0;
} else {
$cont .= $_;
}
}
}
close($inh);
$cont =~ s,\n, ,g;
$cont =~ s,/\*.*?\*/, ,g;
$cont =~ s,\h+, ,g;
my @ptypes = map { s,^ ,,r } split(/;/, $cont);
pop @ptypes; # last one is empty
my @cmd_table;
sub make_args($)
{
$_ = shift;
s/(?:^|(?<=, ))(?:const )?\w+ \*?//g;
return $_;
}
sub type_to_format($)
{
$_ = shift;
s/xint /\%\#x/g;
s/uint /\%u/g;
s/int /\%d/g;
s/const char \*/\%s/g;
return $_;
}
sub make_format($)
{
$_ = type_to_format(shift);
s/, (\%\#?.)(\w+)/, $2=$1/g;
return $_;
}
sub indent($$)
{
my ($str, $indent) = @_;
return $str =~ s,^(?=.),$indent,smgr;
}
open(my $outh, ">".$out_source) or die("Cannot create $out_source: $!\n");
for (@ptypes) {
/^([\w* ]+)\(\*(\w+)\)\( (.*) \)$/ or die("Cannot parse prototype '$_'\n");
my ($cmd_type, $cmd_name, $cmd_args) = ($1, $2, $3);
if (defined($excluded{$cmd_name})) {
push @cmd_table, "NULL";
next;
}
push @cmd_table, "proxy_$cmd_name";
next if (defined($special{$cmd_name}));
my %replace;
$replace{'name'} = $cmd_name;
$replace{'type'} = $cmd_type;
$cmd_args =~ s/^store_t \*ctx// or die("Arguments '$cmd_args' don't start with 'store_t *ctx'\n");
if ($cmd_name =~ /^get_/) {
$template = "GETTER";
$replace{'fmt'} = type_to_format($cmd_type);
} else {
if ($cmd_type eq "void " && $cmd_args =~ s/, void \(\*cb\)\( (.*)void \*aux \), void \*aux$//) {
my $cmd_cb_args = $1;
if (length($cmd_cb_args)) {
$replace{'decl_cb_args'} = $cmd_cb_args;
my $r_cmd_cb_args = $cmd_cb_args;
$r_cmd_cb_args =~ s/^int sts, // or die("Callback arguments of $cmd_name don't start with sts.\n");
$replace{'decl_cb_state'} = $r_cmd_cb_args =~ s/, /\;\n/gr;
my $pass_cb_args = make_args($cmd_cb_args);
$replace{'save_cb_args'} = $pass_cb_args =~ s/([^,]+), /cmd->$1 = $1\;\n/gr;
$pass_cb_args =~ s/([^, ]+)/cmd->$1/g;
$replace{'pass_cb_args'} = $pass_cb_args;
$replace{'print_pass_cb_args'} = $pass_cb_args =~ s/(.*), $/, $1/r;
$replace{'print_fmt_cb_args'} = make_format($cmd_cb_args =~ s/(.*), $/, $1/r);
$replace{'gen_cmd_t'} = "gen_sts_cmd_t";
$replace{'GEN_CMD'} = "GEN_STS_CMD\n";
$replace{'gen_cmd'} = "&cmd->gen.gen";
} else {
$replace{'gen_cmd_t'} = "gen_cmd_t";
$replace{'GEN_CMD'} = "GEN_CMD\n";
$replace{'gen_cmd'} = "&cmd->gen";
}
$replace{'checked'} //= '0';
$template = "CALLBACK";
} elsif ($cmd_type eq "void ") {
$template = "REGULAR_VOID";
} else {
$template = "REGULAR";
$replace{'fmt'} = type_to_format($cmd_type);
}
$replace{'decl_args'} = $cmd_args;
$replace{'print_pass_args'} = $replace{'pass_args'} = make_args($cmd_args);
$replace{'print_fmt_args'} = make_format($cmd_args);
}
for (keys %defines) {
$replace{$1} = delete $defines{$_} if (/^${cmd_name}_(.*)$/);
}
my %used;
my $text = $templates{$template};
$text =~ s/^(\h*)\@(\w+)\@\n/$used{$2} = 1; indent($replace{$2} \/\/ "", $1)/smeg;
$text =~ s/\@(\w+)\@/$used{$1} = 1; $replace{$1} \/\/ ""/eg;
print $outh $text."\n";
my @not_used = grep { !defined($used{$_}) } keys %replace;
die("Fatal: unconsumed replacements in $cmd_name: ".join(" ", @not_used)."\n") if (@not_used);
}
die("Fatal: unconsumed DEFINEs: ".join(" ", keys %defines)."\n") if (%defines);
print $outh "struct driver proxy_driver = {\n".join("", map { "\t$_,\n" } @cmd_table)."};\n";
close $outh;

View file

@ -1,7 +1,7 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2006,2010-2017 Oswald Buddenhagen <ossi@users.sf.net>
* Copyright (C) 2002-2006,2010-2012 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
@ -30,12 +30,8 @@
#include <signal.h>
#include <time.h>
#include <sys/wait.h>
#ifdef __linux__
# include <sys/prctl.h>
#endif
int DFlags;
int JLimit;
int UseFSync = 1;
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__CYGWIN__)
char FieldDelimiter = ';';
@ -47,28 +43,28 @@ int Pid; /* for maildir and imap */
char Hostname[256]; /* for maildir */
const char *Home; /* for config */
uint BufferLimit = 10 * 1024 * 1024;
int BufferLimit = 10 * 1024 * 1024;
static int chans_total, chans_done;
static int boxes_total, boxes_done;
int chans_total, chans_done;
int boxes_total, boxes_done;
int new_total[2], new_done[2];
int flags_total[2], flags_done[2];
int trash_total[2], trash_done[2];
static void ATTR_NORETURN
static void
version( void )
{
puts( PACKAGE " " VERSION );
exit( 0 );
}
static void ATTR_NORETURN
static void
usage( int code )
{
fputs(
PACKAGE " " VERSION " - mailbox synchronizer\n"
"Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>\n"
"Copyright (C) 2002-2006,2008,2010-2017 Oswald Buddenhagen <ossi@users.sf.net>\n"
"Copyright (C) 2002-2006,2008,2010-2012 Oswald Buddenhagen <ossi@users.sf.net>\n"
"Copyright (C) 2004 Theodore Ts'o <tytso@mit.edu>\n"
"usage:\n"
" " EXE " [flags] {{channel[:box,...]|group} ...|-a}\n"
@ -78,10 +74,9 @@ PACKAGE " " VERSION " - mailbox synchronizer\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 far to near side\n"
" -H, --push propagate from near to far side\n"
" -C, --create propagate creations of mailboxes\n"
" -R, --remove propagate deletions of mailboxes\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 debugging modes (see manual)\n"
@ -92,34 +87,13 @@ PACKAGE " " VERSION " - mailbox synchronizer\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, --remove, and --expunge can be suffixed with -far/-near.\n"
"See the man page for details.\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"
#ifdef HAVE_LIBSSL
" +HAVE_LIBSSL"
" +HAVE_LIBSSL\n"
#else
" -HAVE_LIBSSL"
#endif
#ifdef HAVE_LIBSASL
" +HAVE_LIBSASL"
#else
" -HAVE_LIBSASL"
#endif
#ifdef HAVE_LIBZ
" +HAVE_LIBZ"
#else
" -HAVE_LIBZ"
#endif
#ifdef USE_DB
" +USE_DB"
#else
" -USE_DB"
#endif
#ifdef HAVE_IPV6
" +HAVE_IPV6\n"
#else
" -HAVE_IPV6\n"
" -HAVE_LIBSSL\n"
#endif
, code ? stderr : stdout );
exit( code );
@ -136,7 +110,7 @@ debug( const char *msg, ... )
}
#ifdef __linux__
static void ATTR_NORETURN
static void
crashHandler( int n )
{
int dpid;
@ -147,35 +121,18 @@ crashHandler( int n )
dup2( 0, 1 );
dup2( 0, 2 );
error( "*** " EXE " caught signal %d. Starting debugger ...\n", n );
#ifdef PR_SET_PTRACER
int pip[2];
if (pipe( pip ) < 0) {
perror( "pipe()" );
exit( 3 );
}
#endif
switch ((dpid = fork())) {
case -1:
perror( "fork()" );
break;
case 0:
#ifdef PR_SET_PTRACER
close( pip[1] );
read( pip[0], pbuf, 1 );
close( pip[0] );
#endif
sprintf( pbuf, "%d", Pid );
sprintf( pabuf, "/proc/%d/exe", Pid );
execlp( "gdb", "gdb", pabuf, pbuf, (char *)0 );
perror( "execlp()" );
_exit( 1 );
default:
#ifdef PR_SET_PTRACER
prctl( PR_SET_PTRACER, (ulong)dpid );
close( pip[1] );
close( pip[0] );
#endif
waitpid( dpid, NULL, 0 );
waitpid( dpid, 0, 0 );
break;
}
exit( 3 );
@ -205,7 +162,7 @@ stats( void )
if (l > cls)
buf[t][cls - 1] = '~';
}
progress( "\r%s F: %.*s N: %.*s", buf[2], cls, buf[0], cls, buf[1] );
progress( "\r%s M: %.*s S: %.*s", buf[2], cls, buf[0], cls, buf[1] );
}
static int
@ -248,8 +205,8 @@ is_inbox( const char *name )
static int
cmp_box_names( const void *a, const void *b )
{
const char *as = *(const char * const *)a;
const char *bs = *(const char * const *)b;
const char *as = *(const char **)a;
const char *bs = *(const char **)b;
int ai = is_inbox( as );
int bi = is_inbox( bs );
int di = bi - ai;
@ -262,9 +219,9 @@ static char **
filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns )
{
string_list_t *cpat;
char **boxarr = NULL;
char **boxarr = 0;
const char *ps;
uint not, fnot, pfxl, num = 0, rnum = 0;
int not, fnot, pfxl, num = 0, rnum = 0;
pfxl = prefix ? strlen( prefix ) : 0;
for (; boxes; boxes = boxes->next) {
@ -287,7 +244,7 @@ filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns
if (num + 1 >= rnum)
boxarr = nfrealloc( boxarr, (rnum = (rnum + 10) * 2) * sizeof(*boxarr) );
boxarr[num++] = nfstrdup( boxes->string + pfxl );
boxarr[num] = NULL;
boxarr[num] = 0;
}
}
qsort( boxarr, num, sizeof(*boxarr), cmp_box_names );
@ -297,18 +254,18 @@ filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns
static void
merge_actions( channel_conf_t *chan, int ops[], int have, int mask, int def )
{
if (ops[F] & have) {
chan->ops[F] &= ~mask;
chan->ops[F] |= ops[F] & mask;
chan->ops[N] &= ~mask;
chan->ops[N] |= ops[N] & mask;
} else if (!(chan->ops[F] & have)) {
if (global_conf.ops[F] & have) {
chan->ops[F] |= global_conf.ops[F] & mask;
chan->ops[N] |= global_conf.ops[N] & mask;
if (ops[M] & have) {
chan->ops[M] &= ~mask;
chan->ops[M] |= ops[M] & mask;
chan->ops[S] &= ~mask;
chan->ops[S] |= ops[S] & mask;
} else if (!(chan->ops[M] & have)) {
if (global_conf.ops[M] & have) {
chan->ops[M] |= global_conf.ops[M] & mask;
chan->ops[S] |= global_conf.ops[S] & mask;
} else {
chan->ops[F] |= def;
chan->ops[N] |= def;
chan->ops[M] |= def;
chan->ops[S] |= def;
}
}
}
@ -348,10 +305,9 @@ add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
{
channel_conf_t *chan;
chan_ent_t *ce;
box_ent_t *boxes = NULL, **mboxapp = &boxes, *mbox;
box_ent_t *boxes = 0, **mboxapp = &boxes, *mbox;
char *boxp, *nboxp;
size_t boxl;
char boxlist = 0;
int boxl, boxlist = 0;
if ((boxp = strchr( channame, ':' )))
*boxp++ = 0;
@ -359,18 +315,18 @@ add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
if (!strcmp( chan->name, channame ))
goto gotchan;
error( "No channel or group named '%s' defined.\n", channame );
return NULL;
return 0;
gotchan:
if (boxp) {
if (!chan->patterns) {
error( "Cannot override mailbox in channel '%s' - no Patterns.\n", channame );
return NULL;
return 0;
}
boxlist = 1;
do {
nboxp = strpbrk( boxp, ",\n" );
if (nboxp) {
boxl = (size_t)(nboxp - boxp);
boxl = nboxp - boxp;
*nboxp++ = 0;
} else {
boxl = strlen( boxp );
@ -380,8 +336,8 @@ add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
mbox->name = nfstrndup( boxp, boxl );
else
mbox->name = nfstrndup( "INBOX", 5 );
mbox->present[F] = mbox->present[N] = BOX_POSSIBLE;
mbox->next = NULL;
mbox->present[M] = mbox->present[S] = BOX_POSSIBLE;
mbox->next = 0;
*mboxapp = mbox;
mboxapp = &mbox->next;
boxes_total++;
@ -405,7 +361,6 @@ typedef struct {
store_t *ctx[2];
chan_ent_t *chanptr;
box_ent_t *boxptr;
string_list_t *boxes[2];
char *names[2];
int ret, all, list, state[2];
char done, skip, cben;
@ -426,12 +381,12 @@ int
main( int argc, char **argv )
{
main_vars_t mvars[1];
chan_ent_t *chans = NULL, **chanapp = &chans;
chan_ent_t *chans = 0, **chanapp = &chans;
group_conf_t *group;
channel_conf_t *chan;
string_list_t *channame;
char *config = NULL, *opt, *ochar;
int oind, cops = 0, op, ops[2] = { 0, 0 }, ms_warn = 0;
char *config = 0, *opt, *ochar;
int oind, cops = 0, op, ops[2] = { 0, 0 }, pseudo = 0;
tzset();
gethostname( Hostname, sizeof(Hostname) );
@ -447,7 +402,7 @@ main( int argc, char **argv )
memset( mvars, 0, sizeof(*mvars) );
mvars->t[1] = 1;
for (oind = 1, ochar = NULL; ; ) {
for (oind = 1, ochar = 0; ; ) {
if (!ochar || !*ochar) {
if (oind >= argc)
break;
@ -486,10 +441,6 @@ main( int argc, char **argv )
op = VERBOSE | DEBUG_ALL;
else if (!strcmp( opt, "-crash" ))
op = DEBUG_CRASH;
else if (!strcmp( opt, "-driver" ))
op = VERBOSE | DEBUG_DRV;
else if (!strcmp( opt, "-driver-all" ))
op = VERBOSE | DEBUG_DRV | DEBUG_DRV_ALL;
else if (!strcmp( opt, "-maildir" ))
op = VERBOSE | DEBUG_MAILDIR;
else if (!strcmp( opt, "-main" ))
@ -504,26 +455,22 @@ main( int argc, char **argv )
goto badopt;
DFlags |= op;
} else if (!strcmp( opt, "pull" ))
cops |= XOP_PULL, ops[F] |= XOP_HAVE_TYPE;
cops |= XOP_PULL, ops[M] |= XOP_HAVE_TYPE;
else if (!strcmp( opt, "push" ))
cops |= XOP_PUSH, ops[F] |= XOP_HAVE_TYPE;
cops |= XOP_PUSH, ops[M] |= XOP_HAVE_TYPE;
else if (starts_with( opt, -1, "create", 6 )) {
opt += 6;
op = OP_CREATE|XOP_HAVE_CREATE;
lcop:
if (!*opt)
cops |= op;
else if (!strcmp( opt, "-far" ))
ops[F] |= op;
else if (!strcmp( opt, "-master" )) // Pre-1.4 legacy
ops[F] |= op, ms_warn = 1;
else if (!strcmp( opt, "-near" ))
ops[N] |= op;
else if (!strcmp( opt, "-slave" )) // Pre-1.4 legacy
ops[N] |= op, ms_warn = 1;
else if (!strcmp( opt, "-master" ))
ops[M] |= op;
else if (!strcmp( opt, "-slave" ))
ops[S] |= op;
else
goto badopt;
ops[F] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
} else if (starts_with( opt, -1, "remove", 6 )) {
opt += 6;
op = OP_REMOVE|XOP_HAVE_REMOVE;
@ -533,15 +480,15 @@ main( int argc, char **argv )
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
goto lcop;
} else if (!strcmp( opt, "no-expunge" ))
ops[F] |= XOP_HAVE_EXPUNGE;
ops[M] |= XOP_HAVE_EXPUNGE;
else if (!strcmp( opt, "no-create" ))
ops[F] |= XOP_HAVE_CREATE;
ops[M] |= XOP_HAVE_CREATE;
else if (!strcmp( opt, "no-remove" ))
ops[F] |= XOP_HAVE_REMOVE;
ops[M] |= XOP_HAVE_REMOVE;
else if (!strcmp( opt, "full" ))
ops[F] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH;
ops[M] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH;
else if (!strcmp( opt, "noop" ))
ops[F] |= XOP_HAVE_TYPE;
ops[M] |= XOP_HAVE_TYPE;
else if (starts_with( opt, -1, "pull", 4 )) {
op = XOP_PULL;
lcac:
@ -573,11 +520,11 @@ main( int argc, char **argv )
return 1;
}
switch (op & XOP_MASK_DIR) {
case XOP_PULL: ops[N] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[F] |= op & OP_MASK_TYPE; break;
case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
default: cops |= op; break;
}
ops[F] |= XOP_HAVE_TYPE;
ops[M] |= XOP_HAVE_TYPE;
}
continue;
}
@ -595,6 +542,10 @@ main( int argc, char **argv )
mvars->list = 1;
break;
case 'c':
if (*ochar == 'T') {
ochar++;
pseudo = 1;
}
if (oind >= argc) {
error( "-c requires an argument.\n" );
return 1;
@ -604,19 +555,15 @@ main( int argc, char **argv )
case 'C':
op = OP_CREATE|XOP_HAVE_CREATE;
cop:
if (*ochar == 'f')
ops[F] |= op, ochar++;
else if (*ochar == 'm') // Pre-1.4 legacy
ops[F] |= op, ms_warn = 1, ochar++;
else if (*ochar == 'n')
ops[N] |= op, ochar++;
else if (*ochar == 's') // Pre-1.4 legacy
ops[N] |= op, ms_warn = 1, ochar++;
if (*ochar == 'm')
ops[M] |= op, ochar++;
else if (*ochar == 's')
ops[S] |= op, ochar++;
else if (*ochar == '-')
ochar++;
else
cops |= op;
ops[F] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
break;
case 'R':
op = OP_REMOVE|XOP_HAVE_REMOVE;
@ -626,9 +573,9 @@ main( int argc, char **argv )
goto cop;
case 'F':
cops |= XOP_PULL|XOP_PUSH;
FALLTHROUGH
/* fallthrough */
case '0':
ops[F] |= XOP_HAVE_TYPE;
ops[M] |= XOP_HAVE_TYPE;
break;
case 'n':
case 'd':
@ -651,13 +598,13 @@ main( int argc, char **argv )
}
if (op & OP_MASK_TYPE)
switch (op & XOP_MASK_DIR) {
case XOP_PULL: ops[N] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[F] |= op & OP_MASK_TYPE; break;
case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
default: cops |= op; break;
}
else
cops |= op;
ops[F] |= XOP_HAVE_TYPE;
ops[M] |= XOP_HAVE_TYPE;
break;
case 'L':
op = XOP_PULL;
@ -680,12 +627,6 @@ main( int argc, char **argv )
case 'C':
op |= DEBUG_CRASH;
break;
case 'd':
op |= DEBUG_DRV | VERBOSE;
break;
case 'D':
op |= DEBUG_DRV | DEBUG_DRV_ALL | VERBOSE;
break;
case 'm':
op |= DEBUG_MAILDIR | VERBOSE;
break;
@ -710,24 +651,11 @@ main( int argc, char **argv )
op = DEBUG_ALL | VERBOSE;
DFlags |= op;
break;
case 'T':
for (; *ochar; ) {
switch (*ochar++) {
case 'a':
DFlags |= FORCEASYNC;
break;
case 'j':
DFlags |= KEEPJOURNAL;
JLimit = strtol( ochar, &ochar, 10 );
break;
case 'z':
DFlags |= ZERODELAY;
break;
default:
error( "Unknown -T flag '%c'\n", *(ochar - 1) );
return 1;
}
}
case 'J':
DFlags |= KEEPJOURNAL;
break;
case 'Z':
DFlags |= ZERODELAY;
break;
case 'v':
version();
@ -738,8 +666,6 @@ main( int argc, char **argv )
return 1;
}
}
if (ms_warn)
warn( "Notice: -master/-slave/m/s suffixes are deprecated; use -far/-near/f/n instead.\n" );
if (!(DFlags & (QUIET | DEBUG_ALL)) && isatty( 1 ))
DFlags |= PROGRESS;
@ -755,7 +681,7 @@ main( int argc, char **argv )
if (merge_ops( cops, ops ))
return 1;
if (load_config( config ))
if (load_config( config, pseudo ))
return 1;
if (!channels) {
@ -801,34 +727,11 @@ main( int argc, char **argv )
}
#define ST_FRESH 0
#define ST_CONNECTED 1
#define ST_OPEN 2
#define ST_CANCELING 3
#define ST_CLOSED 4
#define ST_OPEN 1
#define ST_CLOSED 2
static void
cancel_prep_done( void *aux )
{
MVARS(aux)
mvars->drv[t]->free_store( mvars->ctx[t] );
mvars->state[t] = ST_CLOSED;
sync_chans( mvars, E_OPEN );
}
static void
store_bad( void *aux )
{
MVARS(aux)
mvars->drv[t]->cancel_store( mvars->ctx[t] );
mvars->state[t] = ST_CLOSED;
mvars->ret = mvars->skip = 1;
sync_chans( mvars, E_OPEN );
}
static void store_connected( int sts, void *aux );
static void store_listed( int sts, string_list_t *boxes, void *aux );
static void store_opened( store_t *ctx, void *aux );
static void store_listed( int sts, void *aux );
static int sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox );
static void done_sync_2_dyn( int sts, void *aux );
static void done_sync( int sts, void *aux );
@ -854,83 +757,73 @@ sync_chans( main_vars_t *mvars, int ent )
info( "Channel %s\n", mvars->chan->name );
mvars->skip = mvars->cben = 0;
for (t = 0; t < 2; t++) {
int st = mvars->chan->stores[t]->driver->get_fail_state( mvars->chan->stores[t] );
int st = mvars->chan->stores[t]->driver->fail_state( mvars->chan->stores[t] );
if (st != FAIL_TEMP) {
info( "Skipping due to %sfailed %s store %s.\n",
(st == FAIL_WAIT) ? "temporarily " : "", str_fn[t], mvars->chan->stores[t]->name );
(st == FAIL_WAIT) ? "temporarily " : "", str_ms[t], mvars->chan->stores[t]->name );
mvars->skip = 1;
}
}
if (mvars->skip)
goto next2;
mvars->state[F] = mvars->state[N] = ST_FRESH;
uint dcaps[2];
for (t = 0; t < 2; t++) {
mvars->drv[t] = mvars->chan->stores[t]->driver;
dcaps[t] = mvars->drv[t]->get_caps( NULL );
}
if ((DFlags & DEBUG_DRV) || (dcaps[F] & dcaps[N] & DRV_VERBOSE))
labels[F] = "F: ", labels[N] = "N: ";
mvars->state[M] = mvars->state[S] = ST_FRESH;
if (mvars->chan->stores[M]->driver->flags & mvars->chan->stores[S]->driver->flags & DRV_VERBOSE)
labels[M] = "M: ", labels[S] = "S: ";
else
labels[F] = labels[N] = "";
for (t = 0; t < 2; t++) {
store_t *ctx = mvars->drv[t]->alloc_store( mvars->chan->stores[t], labels[t] );
if ((DFlags & DEBUG_DRV) || ((DFlags & FORCEASYNC) && !(dcaps[t] & DRV_ASYNC))) {
mvars->drv[t] = &proxy_driver;
ctx = proxy_alloc_store( ctx, labels[t] );
}
mvars->ctx[t] = ctx;
mvars->drv[t]->set_bad_callback( ctx, store_bad, AUX );
}
labels[M] = labels[S] = "";
for (t = 0; ; t++) {
info( "Opening %s store %s...\n", str_fn[t], mvars->chan->stores[t]->name );
mvars->drv[t]->connect_store( mvars->ctx[t], store_connected, AUX );
if (t || mvars->skip)
info( "Opening %s store %s...\n", str_ms[t], mvars->chan->stores[t]->name );
mvars->drv[t] = mvars->chan->stores[t]->driver;
mvars->drv[t]->open_store( mvars->chan->stores[t], labels[t], store_opened, AUX );
if (t)
break;
if (mvars->skip) {
mvars->state[1] = ST_CLOSED;
break;
}
}
mvars->cben = 1;
opened:
if (mvars->skip)
goto next;
if (mvars->state[F] != ST_OPEN || mvars->state[N] != ST_OPEN)
if (mvars->state[M] != ST_OPEN || mvars->state[S] != ST_OPEN)
return;
if (!mvars->chanptr->boxlist && mvars->chan->patterns) {
mvars->chanptr->boxlist = 2;
boxes[F] = filter_boxes( mvars->boxes[F], mvars->chan->boxes[F], mvars->chan->patterns );
boxes[N] = filter_boxes( mvars->boxes[N], mvars->chan->boxes[N], mvars->chan->patterns );
boxes[M] = filter_boxes( mvars->ctx[M]->boxes, mvars->chan->boxes[M], mvars->chan->patterns );
boxes[S] = filter_boxes( mvars->ctx[S]->boxes, mvars->chan->boxes[S], mvars->chan->patterns );
mboxapp = &mvars->chanptr->boxes;
for (mb = sb = 0; ; ) {
char *mname = boxes[F] ? boxes[F][mb] : NULL;
char *sname = boxes[N] ? boxes[N][sb] : NULL;
char *mname = boxes[M] ? boxes[M][mb] : 0;
char *sname = boxes[S] ? boxes[S][sb] : 0;
if (!mname && !sname)
break;
mbox = nfmalloc( sizeof(*mbox) );
if (!(cmp = !mname - !sname) && !(cmp = cmp_box_names( &mname, &sname ))) {
mbox->name = mname;
free( sname );
mbox->present[F] = mbox->present[N] = BOX_PRESENT;
mbox->present[M] = mbox->present[S] = BOX_PRESENT;
mb++;
sb++;
} else if (cmp < 0) {
mbox->name = mname;
mbox->present[F] = BOX_PRESENT;
mbox->present[N] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[M] = BOX_PRESENT;
mbox->present[S] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mb++;
} else {
mbox->name = sname;
mbox->present[F] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[N] = BOX_PRESENT;
mbox->present[M] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[S] = BOX_PRESENT;
sb++;
}
mbox->next = NULL;
mbox->next = 0;
*mboxapp = mbox;
mboxapp = &mbox->next;
boxes_total++;
}
free( boxes[F] );
free( boxes[N] );
free( boxes[M] );
free( boxes[S] );
if (!mvars->list)
stats();
}
@ -959,35 +852,26 @@ sync_chans( main_vars_t *mvars, int ent )
if (!mvars->skip)
goto syncml;
} else
printf( "%s <=> %s\n", nz( mvars->chan->boxes[F], "INBOX" ), nz( mvars->chan->boxes[N], "INBOX" ) );
printf( "%s <=> %s\n", nz( mvars->chan->boxes[M], "INBOX" ), nz( mvars->chan->boxes[S], "INBOX" ) );
}
next:
mvars->cben = 0;
for (t = 0; t < 2; t++) {
free_string_list( mvars->boxes[t] );
mvars->boxes[t] = NULL;
if (mvars->state[t] == ST_FRESH) {
/* An unconnected store may be only cancelled. */
for (t = 0; t < 2; t++)
if (mvars->state[t] == ST_OPEN) {
mvars->drv[t]->disown_store( mvars->ctx[t] );
mvars->state[t] = ST_CLOSED;
mvars->drv[t]->cancel_store( mvars->ctx[t] );
} else if (mvars->state[t] == ST_CONNECTED || mvars->state[t] == ST_OPEN) {
mvars->state[t] = ST_CANCELING;
mvars->drv[t]->cancel_cmds( mvars->ctx[t], cancel_prep_done, AUX );
}
}
mvars->cben = 1;
if (mvars->state[F] != ST_CLOSED || mvars->state[N] != ST_CLOSED) {
mvars->skip = 1;
if (mvars->state[M] != ST_CLOSED || mvars->state[S] != ST_CLOSED) {
mvars->skip = mvars->cben = 1;
return;
}
if (mvars->chanptr->boxlist) {
if (mvars->chanptr->boxlist == 2) {
for (nmbox = mvars->chanptr->boxes; (mbox = nmbox); ) {
nmbox = mbox->next;
free( mbox->name );
free( mbox );
}
mvars->chanptr->boxes = NULL;
mvars->chanptr->boxes = 0;
mvars->chanptr->boxlist = 0;
}
next2:
@ -995,101 +879,110 @@ sync_chans( main_vars_t *mvars, int ent )
chans_done++;
stats();
}
chan_ent_t *nchan = mvars->chanptr->next;
free( mvars->chanptr );
mvars->chanptr = nchan;
} while (mvars->chanptr);
} while ((mvars->chanptr = mvars->chanptr->next));
for (t = 0; t < N_DRIVERS; t++)
drivers[t]->cleanup();
}
static void
store_connected( int sts, void *aux )
store_bad( void *aux )
{
MVARS(aux)
string_list_t *cpat;
int cflags;
switch (sts) {
case DRV_CANCELED:
return;
case DRV_OK:
if (!mvars->skip && !mvars->chanptr->boxlist && mvars->chan->patterns) {
for (cflags = 0, cpat = mvars->chan->patterns; cpat; cpat = cpat->next) {
const char *pat = cpat->string;
if (*pat != '!') {
char buf[8];
int bufl = snprintf( buf, sizeof(buf), "%s%s", nz( mvars->chan->boxes[t], "" ), pat );
int flags = 0;
/* Partial matches like "INB*" or even "*" are not considered,
* except implicity when the INBOX lives under Path. */
if (starts_with( buf, bufl, "INBOX", 5 )) {
char c = buf[5];
if (!c) {
/* User really wants the INBOX. */
flags |= LIST_INBOX;
} else if (c == '/') {
/* Flattened sub-folders of INBOX actually end up in Path. */
if (mvars->ctx[t]->conf->flat_delim[0])
flags |= LIST_PATH;
else
flags |= LIST_INBOX;
} else if (c == '*' || c == '%') {
/* It can be both INBOX and Path, but don't require Path to be configured. */
flags |= LIST_INBOX | LIST_PATH_MAYBE;
} else {
/* It's definitely not the INBOX. */
flags |= LIST_PATH;
}
} else {
flags |= LIST_PATH;
}
debug( "pattern '%s' (effective '%s'): %sPath, %sINBOX\n",
pat, buf, (flags & LIST_PATH) ? "" : "no ", (flags & LIST_INBOX) ? "" : "no ");
cflags |= flags;
}
}
mvars->state[t] = ST_CONNECTED;
mvars->drv[t]->list_store( mvars->ctx[t], cflags, store_listed, AUX );
return;
}
mvars->state[t] = ST_OPEN;
break;
default:
mvars->ret = mvars->skip = 1;
mvars->state[t] = ST_OPEN;
break;
}
mvars->drv[t]->cancel_store( mvars->ctx[t] );
mvars->ret = mvars->skip = 1;
mvars->state[t] = ST_CLOSED;
sync_chans( mvars, E_OPEN );
}
static void
store_listed( int sts, string_list_t *boxes, void *aux )
store_opened( store_t *ctx, void *aux )
{
MVARS(aux)
string_list_t *box;
string_list_t *cpat;
int flags;
if (!ctx) {
mvars->ret = mvars->skip = 1;
mvars->state[t] = ST_CLOSED;
sync_chans( mvars, E_OPEN );
return;
}
mvars->ctx[t] = ctx;
if (!mvars->skip && !mvars->chanptr->boxlist && mvars->chan->patterns && !ctx->listed) {
for (flags = 0, cpat = mvars->chan->patterns; cpat; cpat = cpat->next) {
const char *pat = cpat->string;
if (*pat != '!') {
char buf[8];
int bufl = snprintf( buf, sizeof(buf), "%s%s", nz( mvars->chan->boxes[t], "" ), pat );
/* Partial matches like "INB*" or even "*" are not considered,
* except implicity when the INBOX lives under Path. */
if (starts_with( buf, bufl, "INBOX", 5 )) {
char c = buf[5];
if (!c) {
/* User really wants the INBOX. */
flags |= LIST_INBOX;
} else if (c == '/') {
/* Flattened sub-folders of INBOX actually end up in Path. */
if (ctx->conf->flat_delim)
flags |= LIST_PATH;
else
flags |= LIST_INBOX;
} else {
/* User may not want the INBOX after all ... */
flags |= LIST_PATH;
/* ... but maybe he does.
* The flattened sub-folder case is implicitly covered by the previous line. */
if (c == '*' || c == '%')
flags |= LIST_INBOX;
}
} else {
flags |= LIST_PATH;
}
debug( "pattern '%s' (effective '%s'): %sPath, %sINBOX\n",
pat, buf, (flags & LIST_PATH) ? "" : "no ", (flags & LIST_INBOX) ? "" : "no ");
}
}
set_bad_callback( ctx, store_bad, AUX );
mvars->drv[t]->list_store( ctx, flags, store_listed, AUX );
} else {
mvars->state[t] = ST_OPEN;
sync_chans( mvars, E_OPEN );
}
}
static void
store_listed( int sts, void *aux )
{
MVARS(aux)
string_list_t **box, *bx;
switch (sts) {
case DRV_CANCELED:
return;
case DRV_OK:
for (box = boxes; box; box = box->next) {
if (mvars->ctx[t]->conf->flat_delim[0]) {
mvars->ctx[t]->listed = 1;
if (DFlags & DEBUG_MAIN) {
debug( "got mailbox list from %s:\n", str_ms[t] );
for (bx = mvars->ctx[t]->boxes; bx; bx = bx->next)
debug( " %s\n", bx->string );
}
if (mvars->ctx[t]->conf->flat_delim) {
for (box = &mvars->ctx[t]->boxes; *box; box = &(*box)->next) {
string_list_t *nbox;
if (map_name( box->string, (char **)&nbox, offsetof(string_list_t, string), mvars->ctx[t]->conf->flat_delim, "/" ) < 0) {
error( "Error: flattened mailbox name '%s' contains canonical hierarchy delimiter\n", box->string );
if (map_name( (*box)->string, (char **)&nbox, offsetof(string_list_t, string), mvars->ctx[t]->conf->flat_delim, "/" ) < 0) {
error( "Error: flattened mailbox name '%s' contains canonical hierarchy delimiter\n", (*box)->string );
mvars->ret = mvars->skip = 1;
} else {
nbox->next = mvars->boxes[t];
mvars->boxes[t] = nbox;
nbox->next = (*box)->next;
free( *box );
*box = nbox;
}
} else {
add_string_list( &mvars->boxes[t], box->string );
}
}
if (mvars->ctx[t]->conf->map_inbox) {
debug( "adding mapped inbox to %s store: %s\n", str_fn[t], mvars->ctx[t]->conf->map_inbox );
add_string_list( &mvars->boxes[t], mvars->ctx[t]->conf->map_inbox );
debug( "adding mapped inbox to %s: %s\n", str_ms[t], mvars->ctx[t]->conf->map_inbox );
add_string_list( &mvars->ctx[t]->boxes, mvars->ctx[t]->conf->map_inbox );
}
break;
default:
@ -1103,20 +996,20 @@ store_listed( int sts, string_list_t *boxes, void *aux )
static int
sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox )
{
if (mvars->chan->boxes[F] || mvars->chan->boxes[N]) {
const char *mpfx = nz( mvars->chan->boxes[F], "" );
const char *spfx = nz( mvars->chan->boxes[N], "" );
if (mvars->chan->boxes[M] || mvars->chan->boxes[S]) {
const char *mpfx = nz( mvars->chan->boxes[M], "" );
const char *spfx = nz( mvars->chan->boxes[S], "" );
if (!mvars->list) {
nfasprintf( &mvars->names[F], "%s%s", mpfx, mbox->name );
nfasprintf( &mvars->names[N], "%s%s", spfx, mbox->name );
sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync_2_dyn, mvars );
nfasprintf( &mvars->names[M], "%s%s", mpfx, mbox->name );
nfasprintf( &mvars->names[S], "%s%s", spfx, mbox->name );
sync_boxes( mvars->ctx, (const char **)mvars->names, mbox->present, mvars->chan, done_sync_2_dyn, mvars );
return 1;
}
printf( "%s%s <=> %s%s\n", mpfx, mbox->name, spfx, mbox->name );
} else {
if (!mvars->list) {
mvars->names[F] = mvars->names[N] = mbox->name;
sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync, mvars );
mvars->names[M] = mvars->names[S] = mbox->name;
sync_boxes( mvars->ctx, (const char **)mvars->names, mbox->present, mvars->chan, done_sync, mvars );
return 1;
}
puts( mbox->name );
@ -1129,8 +1022,8 @@ done_sync_2_dyn( int sts, void *aux )
{
main_vars_t *mvars = (main_vars_t *)aux;
free( mvars->names[F] );
free( mvars->names[N] );
free( mvars->names[M] );
free( mvars->names[S] );
done_sync( sts, aux );
}
@ -1144,11 +1037,13 @@ done_sync( int sts, void *aux )
stats();
if (sts) {
mvars->ret = 1;
if (sts & (SYNC_BAD(F) | SYNC_BAD(N))) {
if (sts & SYNC_BAD(F))
mvars->state[F] = ST_CLOSED;
if (sts & SYNC_BAD(N))
mvars->state[N] = ST_CLOSED;
if (sts & (SYNC_BAD(M) | SYNC_BAD(S))) {
if (sts & SYNC_BAD(M))
mvars->state[M] = ST_CLOSED;
if (sts & SYNC_BAD(S))
mvars->state[S] = ST_CLOSED;
mvars->skip = 1;
} else if (sts & SYNC_FAIL_ALL) {
mvars->skip = 1;
}
}

View file

@ -1,32 +1,33 @@
.\" mbsync - mailbox synchronizer
.\" Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
.\" Copyright (C) 2002-2004,2011-2015 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, see <http://www.gnu.org/licenses/>.
.\"
.\" As a special exception, mbsync may be linked with the OpenSSL library,
.\" despite that library's more restrictive license.
.
.ig
\" mbsync - mailbox synchronizer
\" Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
\" Copyright (C) 2002-2004,2011-2015 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, see <http://www.gnu.org/licenses/>.
\"
\" As a special exception, mbsync may be linked with the OpenSSL library,
\" despite that library's more restrictive license.
..
.TH mbsync 1 "2015 Mar 22"
.
..
.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.
@ -35,14 +36,14 @@ 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 (unlike with some other mail synchronizers).
OTOH, \fBmbsync\fR is susceptible to UID validity changes (but will recover
just fine if the change is unfounded).
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;
these files are protected against concurrent \fBmbsync\fR processes.
Mailboxes can be safely modified while \fBmbsync\fR operates
(see \fBINHERENT PROBLEMS\fR below for a minor exception).
Multiple replicas of each mailbox can be maintained.
.
..
.SH OPTIONS
.TP
\fB-c\fR, \fB--config\fR \fIfile\fR
@ -57,13 +58,13 @@ line are ignored.
Don't synchronize anything, but list all mailboxes in the selected channels
and exit.
.TP
\fB-C\fR[\fBf\fR][\fBn\fR], \fB--create\fR[\fB-far\fR|\fB-near\fR]
\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-R\fR[\fBf\fR][\fBn\fR], \fB--remove\fR[\fB-far\fR|\fB-near\fR]
\fB-R\fR[\fBm\fR][\fBs\fR], \fB--remove\fR[\fB-master\fR|\fB-slave\fR]
Override any \fBRemove\fR options from the config file. See below.
.TP
\fB-X\fR[\fBf\fR][\fBn\fR], \fB--expunge\fR[\fB-far\fR|\fB-near\fR]
\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},\
@ -82,16 +83,12 @@ Display version information.
\fB-V\fR, \fB--verbose\fR
Enable \fIverbose\fR mode, which displays what is currently happening.
.TP
\fB-D\fR[\fBC\fR][\fBd\fR|\fBD\fR][\fBm\fR][\fBM\fR][\fBn\fR|\fBN\fR][\fBs\fR]\fR]\fR,\
\fB--debug\fR[\fB-crash\fR|\fB-driver\fR|\fB-driver-all\fR|\fB-maildir\fR|\fB-main\fR|\fB-net\fR|\fB-net-all\fR|\fB-sync\fR]
\fB-D\fR[\fBC\fR][\fBm\fR][\fBM\fR][\fBn\fR|\fBN\fR][\fBs\fR]\fR]\fR,\
\fB--debug\fR[\fB-crash\fR|\fB-maildir\fR|\fB-main\fR|\fB-net\fR|\fB-net-all\fR|\fB-sync\fR]
Enable debugging categories:
.in +4
\fBC\fR, \fBcrash\fR - use built-in crash handler
.br
\fBd\fR, \fBdriver\fR - print driver calls (metadata only)
.br
\fBD\fR, \fBdriver-all\fR - print driver calls (including messages)
.br
\fBm\fR, \fBmaildir\fR - print maildir debug info
.br
\fBM\fR, \fBmain\fR - print main debug info
@ -109,7 +106,7 @@ Without category specification, all categories except net-all are enabled.
Suppress progress counters (this is implicit if stdout is no TTY,
or any debugging categories are enabled) and notices.
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.
@ -119,9 +116,9 @@ and literal double quotes and backslashes (\fB\\\fR) must be backslash-escaped.
All keywords (including those used as arguments) are case-insensitive.
Bash-like home directory expansion using the tilde (\fB~\fR) is supported
in all options which represent local paths.
There are a few global options, the others apply to particular sections.
Sections begin with a section-starting keyword and are terminated by an empty
line or end of file.
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 an identifier unique within that
object class.
.P
@ -130,8 +127,8 @@ 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 network Stores, so server configurations can
be shared between multiple Stores. A Group aggregates multiple Channels to
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.
.P
File system locations (in particular, \fBPath\fR and \fBInbox\fR) use the
@ -140,7 +137,7 @@ even combinations thereof.
.br
Mailbox names, OTOH, always use canonical path separators, which are
Unix-like forward slashes.
.
..
.SS All Stores
These options can be used in all supported Store types.
.br
@ -149,43 +146,35 @@ 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
and \fBCreate\fR in the Channels section.
in the Channels section.
Note that you \fBmust\fR append a slash if you want to specify an entire
directory.
(Default: none)
.
..
.TP
\fBMaxSize\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR]
Messages larger than \fIsize\fR will have only a small placeholder message
propagated into this Store. To propagate the full message, it must be
flagged in either Store; that can be done retroactively, in which case
the \fBReNew\fR operation needs to be executed instead of \fBNew\fR.
This is useful for avoiding downloading messages with large attachments
unless they are actually needed.
Caveat: Setting a size limit on a Store you never read directly (which is
typically the case for servers) is not recommended, as you may never
notice that affected messages were not propagated to it.
.br
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 superfluous.
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 aliases
the \fBINBOX\fR. Makes sense in conjunction with \fBPatterns\fR in the
Channels section, though with a Maildir near side, you probably want to
Channels section, though with a Maildir slave, you probably want to
place \fBInbox\fR under \fBPath\fR instead.
This virtual mailbox does not support subfolders.
.
..
.TP
\fBFlatten\fR \fIdelim\fR
Flatten the hierarchy within this Store by substituting the canonical
@ -197,27 +186,27 @@ A common choice for the delimiter is \fB.\fR.
.br
Note that flattened sub-folders of the \fBINBOX\fR always end up
under \fBPath\fR, including the "INBOX\fIdelim\fR" prefix.
.
..
.TP
\fBTrash\fR \fImailbox\fR
Specifies a mailbox (relative to \fBPath\fR) to copy deleted messages to
prior to expunging.
See \fBRECOMMENDATIONS\fR and \fBINHERENT PROBLEMS\fR below.
(Default: none)
.
..
.TP
\fBTrashNewOnly\fR \fByes\fR|\fBno\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 \fBno\fR).
(Default: \fBno\fR)
.
..
.TP
\fBTrashRemoteNew\fR \fByes\fR|\fBno\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: \fBno\fR)
.
..
.SS Maildir Stores
The reference point for relative \fBPath\fRs is the current working directory.
.P
@ -245,58 +234,36 @@ message deletion and a new message, resulting in unnecessary traffic.
\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 \fByes\fR|\fBno\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.
See \fBRECOMMENDATIONS\fR below.
(Default: \fBno\fR)
.
..
.TP
\fBInbox\fR \fIpath\fR
The location of the \fBINBOX\fR. This is \fInot\fR relative to \fBPath\fR,
but it is allowed to place the \fBINBOX\fR inside the \fBPath\fR.
(Default: \fI~/Maildir\fR)
.
..
.TP
\fBInfoDelimiter\fR \fIdelim\fR
The character used to delimit the info field from a message's basename.
The Maildir standard defines this to be the colon, but this is incompatible
with DOS/Windows file systems.
(Default: the value of \fBFieldDelimiter\fR)
.
.TP
\fBSubFolders\fR \fBVerbatim\fR|\fBMaildir++\fR|\fBLegacy\fR
The on-disk folder naming style used for hierarchical mailboxes.
This option has no effect when \fBFlatten\fR is used.
.br
Suppose mailboxes with the canonical paths \fBtop/sub/subsub\fR and
\fBINBOX/sub/subsub\fR, the styles will yield the following on-disk paths:
.br
\fBVerbatim\fR - \fIPath\fB/top/sub/subsub\fR and \fIInbox\fB/sub/subsub\fR
(this is the style you probably want to use)
.br
\fBMaildir++\fR - \fIInbox\fB/.top.sub.subsub\fR and \fIInbox\fB/..sub.subsub\fR
(this style is compatible with Courier and Dovecot - but note that
the mailbox metadata format is \fInot\fR compatible).
Note that attempts to set \fBPath\fR are rejected in this mode.
.br
\fBLegacy\fR - \fIPath\fB/top/.sub/.subsub\fR and \fIInbox\fB/.sub/.subsub\fR
(this is \fBmbsync\fR's historical style)
.br
(Default: unset; will error out when sub-folders are encountered)
.
..
.SS IMAP4 Accounts
.TP
\fBIMAPAccount\fR \fIname\fR
Define the IMAP4 Account \fIname\fR, opening a section for its parameters.
.
..
.TP
\fBHost\fR \fIhost\fR
Specify the DNS name or IP address of the IMAP server.
@ -304,78 +271,41 @@ Specify the DNS name or IP address of the IMAP server.
If \fBTunnel\fR is used, this setting is needed only if \fBSSLType\fR is
not \fBNone\fR and \fBCertificateFile\fR is not used,
in which case the host name is used for certificate subject verification.
.
..
.TP
\fBPort\fR \fIport\fR
Specify the TCP port number of the IMAP server. (Default: 143 for IMAP,
993 for IMAPS)
.br
If \fBTunnel\fR is used, this setting is ignored.
.
.TP
\fBTimeout\fR \fItimeout\fR
Specify the connect and data timeout for the IMAP server in seconds.
Zero means unlimited.
(Default: \fI20\fR)
.
..
.TP
\fBUser\fR \fIusername\fR
Specify the login name on the IMAP server.
.
.TP
\fBUserCmd\fR [\fB+\fR]\fIcommand\fR
Specify a shell command to obtain a user rather than specifying a
user directly. This allows you to script retrieving user names.
.br
The command must produce exactly one line on stdout; the trailing newline
is optional.
Prepend \fB+\fR to the command to indicate that it produces TTY output
(e.g., a prompt); failure to do so will merely produce messier output.
Remember to backslash-escape double quotes and backslashes embedded into
the command.
.
..
.TP
\fBPass\fR \fIpassword\fR
Specify the password for \fIusername\fR on the IMAP server.
Note that this option is \fInot\fR required.
If neither a password nor a password command is specified in the
configuration file, \fBmbsync\fR will prompt you for a password.
.
..
.TP
\fBPassCmd\fR [\fB+\fR]\fIcommand\fR
Specify a shell command to obtain a password rather than specifying a
password directly. This allows you to use password files and agents.
.br
See \fBUserCmd\fR above for details.
.
.TP
\fBUseKeychain\fR \fByes\fR|\fBno\fR
Whether to use the macOS Keychain to obtain the password.
(Default: \fBno\fR)
.IP
The neccessary keychain item can be created this way:
.RS
.IP
.nh
.B security add-internet-password \-r imap \-s
.I Host
.B \-a
.I User
.B \-w
.I password
[
.B \-T
.I /path/to/mbsync
]
.hy
.RE
.
The command must produce exactly one line on stdout; the trailing newline is
optional.
Prepend \fB+\fR to the command to indicate that it produces TTY output
(e.g., a decryption password prompt); failure to do so will merely produce
messier output.
..
.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.
.
..
.TP
\fBAuthMechs\fR \fItype\fR ...
The list of acceptable authentication mechanisms.
@ -386,7 +316,7 @@ enough for the current \fBSSLType\fR setting.
The actually used mechanism is the most secure choice from the intersection
of this list, the list supplied by the server, and the installed SASL modules.
(Default: \fB*\fR)
.
..
.TP
\fBSSLType\fR {\fBNone\fR|\fBSTARTTLS\fR|\fBIMAPS\fR}
Select the connection security/encryption method:
@ -400,81 +330,37 @@ so it is the default (unless a tunnel is used).
.br
\fBIMAPS\fR - security is established by starting SSL/TLS negotiation
right after connecting the secure IMAP port 993.
.
..
.TP
\fBSSLVersions\fR [\fBSSLv3\fR] [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR] [\fBTLSv1.3\fR]
\fBSSLVersions\fR [\fBSSLv2\fR] [\fBSSLv3\fR] [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR]
Select the acceptable SSL/TLS versions.
Use old versions only when the server has problems with newer ones.
(Default: [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR] [\fBTLSv1.3\fR]).
.
Use of SSLv2 is strongly discouraged for security reasons, but might be the
only option on some very old servers.
Generally, the newest TLS version is recommended, but as this confuses some
servers, \fBTLSv1\fR is the default.
..
.TP
\fBSystemCertificates\fR \fByes\fR|\fBno\fR
Whether the system's default CA (certificate authority) certificate
store should be used to verify certificate trust chains. Disable this
if you want to trust only hand-picked certificates.
Whether the system's default root cerificate store should be loaded.
(Default: \fByes\fR)
.
..
.TP
\fBCertificateFile\fR \fIpath\fR
File containing additional X.509 certificates used to verify server
identities.
It may contain two types of certificates:
.RS
.IP Host
These certificates are matched only against the received server certificate
itself.
They are always trusted, regardless of validity.
A typical use case would be forcing acceptance of an expired certificate.
identities. Directly matched peer certificates are always trusted,
regardless of validity.
.br
These certificates may be obtained using the \fBmbsync-get-cert\fR tool;
make sure to verify their fingerprints before trusting them, or transfer
them securely from the server's network (if it can be trusted beyond the
server itself).
.IP CA
These certificates are used as trust anchors when building the certificate
chain for the received server certificate.
They are used to supplant or supersede the system's trust store, depending
on the \fBSystemCertificates\fR setting;
it is not necessary and not recommended to specify the system's trust store
itself here.
The trust chains are fully validated.
.RE
.
.TP
\fBClientCertificate\fR \fIpath\fR
File containing a client certificate to send to the server.
\fBClientKey\fR should also be specified.
.br
Note that client certificate verification is usually not required,
so it is unlikely that you need this option.
.
.TP
\fBClientKey\fR \fIpath\fR
File containing the private key corresponding to \fBClientCertificate\fR.
.
.TP
\fBCipherString\fR \fIstring\fR
Specify OpenSSL cipher string for connections secured with TLS up to
version 1.2 (but not 1.3 and above).
The format is described in \fBciphers\fR\|(1).
(Default: empty, which implies system wide policy).
.
Note that the system's default certificate store is always used
(unless \fBSystemCertificates\fR is disabled)
and should not be specified here.
..
.TP
\fBPipelineDepth\fR \fIdepth\fR
Maximum number of IMAP commands which can be simultaneously in flight.
Setting this to \fI1\fR disables pipelining.
This is mostly a debugging option, but may also be used to limit average
bandwidth consumption (GMail may require this if you have a very fast
connection), or to spare flaky servers like M$ Exchange.
This is mostly a debugging only option.
(Default: \fIunlimited\fR)
.
.TP
\fBDisableExtension\fR[\fBs\fR] \fIextension\fR ...
Disable the use of specific IMAP extensions.
This can be used to work around bugs in servers
(and possibly \fBmbsync\fR itself).
(Default: empty)
.
..
.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
@ -482,21 +368,21 @@ 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 \fByes\fR|\fBno\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: \fByes\fR)
.
..
.TP
\fBPathDelimiter\fR \fIdelim\fR
Specify the server's hierarchy delimiter.
@ -504,33 +390,25 @@ Specify the server's hierarchy delimiter.
.br
Do \fInot\fR abuse this to re-interpret the hierarchy.
Use \fBFlatten\fR instead.
.
.TP
\fBSubscribedOnly\fR \fByes\fR|\fBno\fR
Selects whether to synchronize only mailboxes that are subscribed to on the
IMAP server. In technical terms, if this option is set, \fBmbsync\fR will use
the IMAP command LSUB instead of LIST to look for mailboxes in this Store.
This option make sense only in conjunction with \fBPatterns\fR.
(Default: \fBno\fR)
.
..
.SS Channels
.TP
\fBChannel\fR \fIname\fR
Define the Channel \fIname\fR, opening a section for its parameters.
.
..
.TP
{\fBFar\fR|\fBNear\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
Specify the far resp. near side Store to be connected by this Channel.
{\fBMaster\fR|\fBSlave\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
Specify the Master resp. Slave Store to be connected by this Channel.
If \fBPatterns\fR are specified, \fImailbox\fR is interpreted as a
prefix which is not matched against the patterns, and which is not
affected by mailbox list overrides.
Otherwise, if \fImailbox\fR is omitted, \fBINBOX\fR is assumed.
.
..
.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 the far
and near side. Patterns are IMAP4 patterns, i.e., \fB*\fR matches anything
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
@ -543,16 +421,16 @@ The mailbox list selected by \fBPatterns\fR can be overridden by a mailbox
list in a channel reference (a \fBGroup\fR specification or the command line).
.br
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 Far and Near. Note that this actually modifies the Stores, so take care
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 near side mailbox.
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
@ -561,7 +439,7 @@ Messages that are flagged (marked as important) and (by default) unread
messages will not be automatically deleted.
If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR
(Default: \fI0\fR).
.
..
.TP
\fBExpireUnread\fR \fByes\fR|\fBno\fR
Selects whether unread messages should be affected by \fBMaxMessages\fR.
@ -571,19 +449,20 @@ However, if your archive contains large amounts of unread messages by design,
treating them as important would practically defeat \fBMaxMessages\fR. In this
case you need to enable this option.
(Default: \fBno\fR).
.
..
.TP
\fBSync\fR {\fBNone\fR|[\fBPull\fR] [\fBPush\fR] [\fBNew\fR] [\fBReNew\fR] [\fBDelete\fR] [\fBFlags\fR]|\fBAll\fR}
Select the synchronization operation(s) to perform:
.br
\fBPull\fR - propagate changes from far to near side.
\fBPull\fR - propagate changes from Master to Slave.
.br
\fBPush\fR - propagate changes from near to far side.
\fBPush\fR - propagate changes from Slave to Master.
.br
\fBNew\fR - propagate newly appeared messages.
.br
\fBReNew\fR - upgrade placeholders to full messages. Useful only with
a configured \fBMaxSize\fR.
\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
@ -609,10 +488,10 @@ 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 far side to the near side,
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 near side to the far side.
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
@ -620,22 +499,22 @@ 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 far side to the near side and any
changes from the near side to the far side.
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 {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Automatically create missing mailboxes [on the far/near side].
\fBCreate\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Automatically create missing mailboxes [on the Master/Slave].
Otherwise print an error message and skip that mailbox pair if a mailbox
and the corresponding sync state does not exist.
(Global default: \fBNone\fR)
.
..
.TP
\fBRemove\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Propagate mailbox deletions [to the far/near side].
\fBRemove\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Propagate mailbox deletions [to the Master/Slave].
Otherwise print an error message and skip that mailbox pair if a mailbox
does not exist but the corresponding sync state does.
.br
@ -645,13 +524,13 @@ mark them as deleted. This ensures compatibility with \fBSyncState *\fR.
Note that for safety, non-empty mailboxes are never deleted.
.br
(Global default: \fBNone\fR)
.
..
.TP
\fBExpunge\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Permanently remove all messages [on the far/near side] marked for deletion.
\fBExpunge\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Permanently remove all messages [on the Master/Slave] marked for deletion.
See \fBRECOMMENDATIONS\fR below.
(Global default: \fBNone\fR)
.
..
.TP
\fBCopyArrivalDate\fR {\fByes\fR|\fBno\fR}
Selects whether their arrival time should be propagated together with
@ -661,31 +540,30 @@ sorting intact.
Note that IMAP does not guarantee that the time stamp (termed \fBinternal
date\fR) is actually the arrival time, but it is usually close enough.
(Default: \fBno\fR)
.
..
.P
\fBSync\fR, \fBCreate\fR, \fBRemove\fR, \fBExpunge\fR,
\fBMaxMessages\fR, and \fBCopyArrivalDate\fR
can be used before 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 near side mailbox itself; this has the advantage that you do not need
to handle the state file separately if you delete the mailbox, but it works
only with Maildir mailboxes, obviously.
Otherwise this is interpreted as a string to prepend to the near side mailbox
name to make up a complete path.
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:\fIfar-store\fB:\fIfar-box\fB_:\fInear-store\fB:\fInear-box\fR
\fB:\fImaster\fB:\fImaster-box\fB_:\fIslave\fB:\fIslave-box\fR
(see also \fBFieldDelimiter\fR below).
.br
(Global default: \fI~/.mbsync/\fR).
.
..
.SS Groups
.TP
\fBGroup\fR \fIname\fR [\fIchannel\fR[\fB:\fIbox\fR[\fB,\fR...]]] ...
@ -699,12 +577,12 @@ If you supply one or more \fIbox\fRes to a \fIchannel\fR, they will be used
instead of what is specified in the Channel's Patterns.
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.
.
..
.SS Global Options
.TP
\fBFSync\fR \fByes\fR|\fBno\fR
@ -717,7 +595,7 @@ Enabling it is a wise choice for file systems mounted with data=writeback,
in particular modern systems like ext4, btrfs and xfs. The performance impact
on older file systems may be disproportionate.
(Default: \fByes\fR)
.
..
.TP
\fBFieldDelimiter\fR \fIdelim\fR
The character to use to delimit fields in the string appended to a global
@ -727,7 +605,7 @@ DOS/Windows file systems.
This option is meaningless for \fBSyncState\fR if the latter is \fB*\fR,
obviously. However, it also determines the default of \fBInfoDelimiter\fR.
(Global default: \fI;\fR on Windows, \fI:\fR everywhere else)
.
..
.TP
\fBBufferLimit\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR]
The per-Channel, per-direction instantaneous memory usage above which
@ -735,22 +613,22 @@ The per-Channel, per-direction instantaneous memory usage above which
absolute limit, as even a single message can consume more memory than
this.
(Default: \fI10M\fR)
.
..
.SH CONSOLE OUTPUT
If \fBmbsync\fR's output is connected to a console, it will print progress
counters by default. The output will look like this:
.P
.in +4
C: 1/2 B: 3/4 F: +13/13 *23/42 #0/0 N: +0/7 *0/0 #0/0
C: 1/2 B: 3/4 M: +13/13 *23/42 #0/0 S: +0/7 *0/0 #0/0
.in -4
.P
This represents the cumulative progress over channels, boxes, and messages
affected on the far and near side, respectively.
affected on master and slave, respectively.
The message counts represent added messages, messages with updated flags,
and trashed messages, respectively.
No attempt is made to calculate the totals in advance, so they grow over
time as more information is gathered.
.
..
.SH RECOMMENDATIONS
Make sure your IMAP server does not auto-expunge deleted messages - it is
slow, and semantically somewhat questionable. Specifically, Gmail needs to
@ -778,18 +656,7 @@ If your server supports auto-trashing (as Gmail does), it is probably a
good idea to rely on that instead of \fBmbsync\fR's trash functionality.
If you do that, and intend to synchronize the trash like other mailboxes,
you should not use \fBmbsync\fR's \fBTrash\fR option at all.
.P
Use of the \fBTrash\fR option with M$ Exchange 2013 requires the use of
\fBDisableExtension MOVE\fR due to a server bug.
.P
When using the more efficient default UID mapping scheme, it is important
that the MUA renames files when moving them between Maildir folders.
Mutt always does that, while mu4e needs to be configured to do it:
.br
.in +4
(setq mu4e-change-filenames-when-moving t)
.in -4
.
..
.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.
@ -800,7 +667,7 @@ 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 accessed by only one client
(including \fBmbsync\fR) at a time.
.
..
.SH FILES
.TP
.B ~/.mbsyncrc
@ -808,15 +675,15 @@ Default configuration file
.TP
.B ~/.mbsync/
Directory containing synchronization state files
.
..
.SH SEE ALSO
mdconvert(1), mutt(1), maildir(5)
mdconvert(1), isync(1), mutt(1), maildir(5)
.P
Up to date information on \fBmbsync\fR can be found at http://isync.sf.net/
.P
SASL mechanisms are listed at
http://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml
.
..
.SH AUTHORS
Originally written by Michael R. Elkins,
rewritten and currently maintained by Oswald Buddenhagen,

View file

@ -21,11 +21,16 @@ Pass xxxxxxxx
#PassCmd "gpg --quiet --for-your-eyes-only --decrypt $HOME/imappassword.gpg"
# Fetch password from pwmd (http://pwmd.sourceforge.net/):
#PassCmd "echo -ne 'GET myIsp\\tpassword' | pwmc datafile"
# On Mac OS X, run "KeyChain Access" -- File->New Password Item. Fill out form using
# "Keychain Item Name" http://IMAPSERVER (note: the "http://" is a hack)
# "Account Name" USERNAME
# "Password" PASSWORD
#PassCmd "/usr/bin/security find-internet-password -w -a USERNAME -s IMAPSERVER ~/Library/Keychains/login.keychain"
Channel work
Far :work:
Near :local:work
Expunge Near
Master :work:
Slave :local:work
Expunge Slave
Sync PullNew Push
@ -35,8 +40,8 @@ Port 6789
RequireSSL no
Channel personal
Far :personal:
Near :local:personal
Master :personal:
Slave :local:personal
Expunge Both
MaxMessages 150
MaxSize 200k
@ -45,8 +50,8 @@ IMAPStore remote
Tunnel "ssh -q host.remote.com /usr/sbin/imapd"
Channel remote
Far :remote:
Near :local:remote
Master :remote:
Slave :local:remote
Group boxes
@ -65,8 +70,8 @@ RequireSSL no
UseTLSv1 no
Channel rst
Far :st1:somebox
Near :st2:
Master :st1:somebox
Slave :st2:
IMAPAccount server
@ -81,17 +86,16 @@ TrashRemoteNew yes
MaildirStore mirror
Path ~/Maildir/
SubFolders Verbatim
Channel o2o
Far :server:
Near :mirror:
Master :server:
Slave :mirror:
Patterns %
Group partial o2o:inbox,sent-mail,foobar
# INBOX => server, INBOX.foo => server.foo, etc.
Channel inbox
Far :server:INBOX
Near :mirror:server
Master :server:INBOX
Slave :mirror:server
Patterns *

View file

@ -117,11 +117,11 @@ convert( const char *box, int altmap )
sys_error( "Cannot create %s", tdpath );
goto sbork;
}
if (db_create( &db, NULL, 0 )) {
if (db_create( &db, 0, 0 )) {
fputs( "Error: db_create() failed\n", stderr );
goto tbork;
}
if ((ret = (db->open)( db, NULL, dbpath, NULL, DB_HASH, altmap ? DB_CREATE|DB_TRUNCATE : 0, 0 ))) {
if ((ret = (db->open)( db, 0, dbpath, 0, DB_HASH, altmap ? DB_CREATE|DB_TRUNCATE : 0, 0 ))) {
db->err( db, ret, "Error: db->open(%s)", dbpath );
dbork:
db->close( db, 0 );
@ -143,12 +143,12 @@ convert( const char *box, int altmap )
}
value.data = uv;
value.size = sizeof(uv);
if ((ret = db->put( db, NULL, &key, &value, 0 ))) {
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, NULL, &key, &value, 0 ))) {
if ((ret = db->get( db, 0, &key, &value, 0 ))) {
db->err( db, ret, "Error: cannot read UIDVALIDITY of '%s'", box );
goto dbork;
}
@ -188,7 +188,7 @@ convert( const char *box, int altmap )
uid = atoi( p + 3 );
value.data = &uid;
value.size = sizeof(uid);
if ((ret = db->put( db, NULL, &key, &value, 0 ))) {
if ((ret = db->put( db, 0, &key, &value, 0 ))) {
db->err( db, ret, "Error: cannot write UID for '%s'", box );
goto ebork;
}
@ -197,7 +197,7 @@ convert( const char *box, int altmap )
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, NULL, &key, &value, 0 ))) {
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;

File diff suppressed because it is too large Load diff

View file

@ -39,12 +39,8 @@
#ifdef HAVE_LIBSSL
# include <openssl/ssl.h>
# include <openssl/err.h>
# include <openssl/hmac.h>
# include <openssl/x509v3.h>
# if OPENSSL_VERSION_NUMBER < 0x10100000L \
|| (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070100fL)
# define X509_OBJECT_get0_X509(o) ((o)->data.x509)
# define X509_STORE_get0_objects(o) ((o)->objs)
# endif
#endif
enum {
@ -63,34 +59,6 @@ socket_fail( conn_t *conn )
}
#ifdef HAVE_LIBSSL
static void ATTR_PRINTFLIKE(1, 2)
print_ssl_errors( const char *fmt, ... )
{
char *action;
va_list va;
ulong err;
va_start( va, fmt );
nfvasprintf( &action, fmt, va );
va_end( va );
while ((err = ERR_get_error()))
error( "Error while %s: %s\n", action, ERR_error_string( err, NULL ) );
free( action );
}
static int
print_ssl_socket_errors( const char *func, conn_t *conn )
{
ulong err;
int num = 0;
while ((err = ERR_get_error())) {
error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, NULL ) );
num++;
}
return num;
}
static int
ssl_return( const char *func, conn_t *conn, int ret )
{
@ -101,23 +69,23 @@ ssl_return( const char *func, conn_t *conn, int ret )
return ret;
case SSL_ERROR_WANT_WRITE:
conf_notifier( &conn->notify, POLLIN, POLLOUT );
FALLTHROUGH
/* fallthrough */
case SSL_ERROR_WANT_READ:
return 0;
case SSL_ERROR_SSL:
print_ssl_socket_errors( func, conn );
break;
case SSL_ERROR_SYSCALL:
if (print_ssl_socket_errors( func, conn ))
break;
if (ret == 0) {
case SSL_ERROR_SSL:
if (!(err = ERR_get_error())) {
if (ret == 0) {
case SSL_ERROR_ZERO_RETURN:
/* Callers take the short path out, so signal higher layers from here. */
conn->state = SCK_EOF;
conn->read_callback( conn->callback_aux );
return -1;
/* Callers take the short path out, so signal higher layers from here. */
conn->state = SCK_EOF;
conn->read_callback( conn->callback_aux );
return -1;
}
sys_error( "Socket error: secure %s %s", func, conn->name );
} else {
error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, 0 ) );
}
sys_error( "Socket error: secure %s %s", func, conn->name );
break;
default:
error( "Socket error: secure %s %s: unhandled SSL error %d\n", func, conn->name, err );
@ -194,6 +162,7 @@ verify_cert_host( const server_conf_t *conf, conn_t *sock )
int i;
long err;
X509 *cert;
STACK_OF(X509_OBJECT) *trusted;
cert = SSL_get_peer_certificate( sock->ssl );
if (!cert) {
@ -201,54 +170,41 @@ verify_cert_host( const server_conf_t *conf, conn_t *sock )
return -1;
}
for (i = 0; i < sk_X509_num( sock->conf->trusted_certs ); i++) {
if (!X509_cmp( cert, sk_X509_value( sock->conf->trusted_certs, i ) )) {
X509_free( cert );
trusted = (STACK_OF(X509_OBJECT) *)sock->conf->trusted_certs;
for (i = 0; i < sk_X509_OBJECT_num( trusted ); i++) {
if (!X509_cmp( cert, sk_X509_OBJECT_value( trusted, i )->data.x509 ))
return 0;
}
}
err = SSL_get_verify_result( sock->ssl );
if (err != X509_V_OK) {
error( "SSL error connecting %s: %s\n", sock->name, X509_verify_cert_error_string( err ) );
X509_free( cert );
error( "SSL error connecting %s: %s\n", sock->name, ERR_error_string( err, NULL ) );
return -1;
}
if (!conf->host) {
error( "SSL error connecting %s: Neither host nor matching certificate specified\n", sock->name );
X509_free( cert );
return -1;
}
int ret = verify_hostname( cert, conf->host );
X509_free( cert );
return ret;
return verify_hostname( cert, conf->host );
}
static int
init_ssl_ctx( const server_conf_t *conf )
{
DIAG_PUSH
DIAG_DISABLE("-Wcast-qual") // C has no 'mutable' or const_cast<> ...
server_conf_t *mconf = (server_conf_t *)conf;
DIAG_POP
int options = 0;
if (conf->SSLContext)
return conf->ssl_ctx_valid;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
const SSL_METHOD *method = TLS_client_method();
#else
const SSL_METHOD *method = SSLv23_client_method();
#endif
if (!(mconf->SSLContext = SSL_CTX_new( method ))) {
print_ssl_errors( "initializing SSL context" );
return 0;
}
mconf->SSLContext = SSL_CTX_new( SSLv23_client_method() );
uint options = SSL_OP_NO_SSLv3;
if (!(conf->ssl_versions & SSLv2))
options |= SSL_OP_NO_SSLv2;
if (!(conf->ssl_versions & SSLv3))
options |= SSL_OP_NO_SSLv3;
if (!(conf->ssl_versions & TLSv1))
options |= SSL_OP_NO_TLSv1;
#ifdef SSL_OP_NO_TLSv1_1
@ -259,62 +215,21 @@ DIAG_POP
if (!(conf->ssl_versions & TLSv1_2))
options |= SSL_OP_NO_TLSv1_2;
#endif
#ifdef SSL_OP_NO_TLSv1_3
if (!(conf->ssl_versions & TLSv1_3))
options |= SSL_OP_NO_TLSv1_3;
#endif
SSL_CTX_set_options( mconf->SSLContext, options );
if (conf->cipher_string && !SSL_CTX_set_cipher_list( mconf->SSLContext, conf->cipher_string )) {
print_ssl_errors( "setting cipher string '%s'", conf->cipher_string );
if (conf->cert_file && !SSL_CTX_load_verify_locations( mconf->SSLContext, conf->cert_file, 0 )) {
error( "Error while loading certificate file '%s': %s\n",
conf->cert_file, ERR_error_string( ERR_get_error(), 0 ) );
return 0;
}
if (!(mconf->trusted_certs = sk_X509_new_null()))
oom();
if (conf->cert_file) {
X509_STORE *store;
if (!(store = X509_STORE_new()))
oom();
if (!X509_STORE_load_locations( store, conf->cert_file, NULL )) {
print_ssl_errors( "loading certificate file '%s'", conf->cert_file );
return 0;
}
STACK_OF(X509_OBJECT) *objs = X509_STORE_get0_objects( store );
for (int i = 0; i < sk_X509_OBJECT_num( objs ); i++) {
X509 *cert = X509_OBJECT_get0_X509( sk_X509_OBJECT_value( objs, i ) );
if (cert) {
if (X509_check_ca( cert )) {
if (!X509_STORE_add_cert( SSL_CTX_get_cert_store( mconf->SSLContext ), cert ))
oom();
} else {
X509_up_ref( cert ); // Locking failure assumed impossible
if (!sk_X509_push( mconf->trusted_certs, cert ))
oom();
}
}
}
X509_STORE_free( store );
}
if (mconf->system_certs && !SSL_CTX_set_default_verify_paths( mconf->SSLContext )) {
ulong err;
while ((err = ERR_get_error()))
warn( "Warning: Unable to load default certificate files: %s\n", ERR_error_string( err, NULL ) );
}
mconf->trusted_certs = (_STACK *)sk_X509_OBJECT_dup( SSL_CTX_get_cert_store( mconf->SSLContext )->objs );
if (mconf->system_certs && !SSL_CTX_set_default_verify_paths( mconf->SSLContext ))
warn( "Warning: Unable to load default certificate files: %s\n",
ERR_error_string( ERR_get_error(), 0 ) );
SSL_CTX_set_verify( mconf->SSLContext, SSL_VERIFY_NONE, NULL );
if (conf->client_certfile && !SSL_CTX_use_certificate_chain_file( mconf->SSLContext, conf->client_certfile)) {
print_ssl_errors( "loading client certificate file '%s'", conf->client_certfile );
return 0;
}
if (conf->client_keyfile && !SSL_CTX_use_PrivateKey_file( mconf->SSLContext, conf->client_keyfile, SSL_FILETYPE_PEM)) {
print_ssl_errors( "loading client private key '%s'", conf->client_keyfile );
return 0;
}
mconf->ssl_ctx_valid = 1;
return 1;
}
@ -329,7 +244,6 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) )
static int ssl_inited;
conn->callbacks.starttls = cb;
conn->state = SCK_STARTTLS;
if (!ssl_inited) {
SSL_library_init();
@ -343,23 +257,10 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) )
}
init_wakeup( &conn->ssl_fake, ssl_fake_cb, conn );
if (!(conn->ssl = SSL_new( ((server_conf_t const *)conn->conf)->SSLContext ))) {
print_ssl_errors( "initializing SSL connection" );
start_tls_p3( conn, 0 );
return;
}
if (!SSL_set_tlsext_host_name( conn->ssl, conn->conf->host )) {
print_ssl_errors( "setting SSL server host name" );
start_tls_p3( conn, 0 );
return;
}
if (!SSL_set_fd( conn->ssl, conn->fd )) {
print_ssl_errors( "setting SSL socket fd" );
start_tls_p3( conn, 0 );
return;
}
conn->ssl = SSL_new( ((server_conf_t *)conn->conf)->SSLContext );
SSL_set_fd( conn->ssl, conn->fd );
SSL_set_mode( conn->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER );
socket_expect_activity( conn, 1 );
conn->state = SCK_STARTTLS;
start_tls_p2( conn );
}
@ -378,7 +279,6 @@ start_tls_p2( conn_t *conn )
static void start_tls_p3( conn_t *conn, int ok )
{
socket_expect_activity( conn, 0 );
conn->state = SCK_READY;
conn->callbacks.starttls( ok, conn->callback_aux );
}
@ -389,14 +289,6 @@ static void start_tls_p3( conn_t *conn, int ok )
static void z_fake_cb( void * );
static const char *
z_err_msg( int code, z_streamp strm )
{
/* zlib's consistency in populating z_stream->msg is somewhat
* less than stellar. zError() is undocumented. */
return strm->msg ? strm->msg : zError( code );
}
void
socket_start_deflate( conn_t *conn )
{
@ -408,7 +300,7 @@ socket_start_deflate( conn_t *conn )
-15 /* Use raw deflate */
);
if (result != Z_OK) {
error( "Fatal: Cannot initialize decompression: %s\n", z_err_msg( result, conn->in_z ) );
error( "Fatal: Cannot initialize decompression: %s\n", conn->in_z->msg );
abort();
}
@ -422,7 +314,7 @@ socket_start_deflate( conn_t *conn )
Z_DEFAULT_STRATEGY /* Don't try to do anything fancy */
);
if (result != Z_OK) {
error( "Fatal: Cannot initialize compression: %s\n", z_err_msg( result, conn->out_z ) );
error( "Fatal: Cannot initialize compression: %s\n", conn->out_z->msg );
abort();
}
@ -432,10 +324,8 @@ socket_start_deflate( conn_t *conn )
static void socket_fd_cb( int, void * );
static void socket_fake_cb( void * );
static void socket_timeout_cb( void * );
static void socket_connect_one( conn_t * );
static void socket_connect_next( conn_t * );
static void socket_connect_failed( conn_t * );
static void socket_connected( conn_t * );
static void socket_connect_bail( conn_t * );
@ -447,7 +337,6 @@ socket_open_internal( conn_t *sock, int fd )
fcntl( fd, F_SETFL, O_NONBLOCK );
init_notifier( &sock->notify, fd, socket_fd_cb, sock );
init_wakeup( &sock->fd_fake, socket_fake_cb, sock );
init_wakeup( &sock->fd_timeout, socket_timeout_cb, sock );
}
static void
@ -455,37 +344,10 @@ socket_close_internal( conn_t *sock )
{
wipe_notifier( &sock->notify );
wipe_wakeup( &sock->fd_fake );
wipe_wakeup( &sock->fd_timeout );
close( sock->fd );
sock->fd = -1;
}
#ifndef HAVE_IPV6
struct addr_info {
struct addr_info *ai_next;
struct sockaddr_in ai_addr[1];
};
#define freeaddrinfo(ai) free( ai )
static struct addr_info *
init_addrinfo( struct hostent *he )
{
uint naddr = 0;
for (char **addr = he->h_addr_list; *addr; addr++)
naddr++;
struct addr_info *caddr = nfcalloc( naddr * sizeof(struct addrinfo) );
struct addr_info *ret, **caddrp = &ret;
for (char **addr = he->h_addr_list; *addr; addr++, caddr++) {
caddr->ai_addr->sin_family = AF_INET;
memcpy( &caddr->ai_addr->sin_addr.s_addr, *addr, sizeof(struct in_addr) );
*caddrp = caddr;
caddrp = &caddr->ai_next;
}
return ret;
}
#endif
void
socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
{
@ -535,6 +397,8 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
return;
}
info( "\vok\n" );
sock->curr_addr = sock->addrs;
#else
struct hostent *he;
@ -547,9 +411,8 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
}
info( "\vok\n" );
sock->addrs = init_addrinfo( he );
sock->curr_addr = he->h_addr_list;
#endif
sock->curr_addr = sock->addrs;
socket_connect_one( sock );
}
}
@ -561,10 +424,16 @@ socket_connect_one( conn_t *sock )
#ifdef HAVE_IPV6
struct addrinfo *ai;
#else
struct addr_info *ai;
struct {
struct sockaddr_in ai_addr[1];
} ai[1];
#endif
#ifdef HAVE_IPV6
if (!(ai = sock->curr_addr)) {
#else
if (!*sock->curr_addr) {
#endif
error( "No working address found for %s\n", sock->conf->host );
socket_connect_bail( sock );
return;
@ -581,6 +450,11 @@ socket_connect_one( conn_t *sock )
#endif
{
struct sockaddr_in *in = ((struct sockaddr_in *)ai->ai_addr);
#ifndef HAVE_IPV6
memset( in, 0, sizeof(*in) );
in->sin_family = AF_INET;
in->sin_addr.s_addr = *((int *)*sock->curr_addr);
#endif
in->sin_port = htons( sock->conf->port );
nfasprintf( &sock->name, "%s (%s:%hu)",
sock->conf->host, inet_ntoa( in->sin_addr ), sock->conf->port );
@ -592,8 +466,8 @@ socket_connect_one( conn_t *sock )
s = socket( PF_INET, SOCK_STREAM, 0 );
#endif
if (s < 0) {
socket_connect_next( sock );
return;
perror( "socket" );
exit( 1 );
}
socket_open_internal( sock, s );
@ -608,7 +482,6 @@ socket_connect_one( conn_t *sock )
return;
}
conf_notifier( &sock->notify, 0, POLLOUT );
socket_expect_activity( sock, 1 );
sock->state = SCK_CONNECTING;
info( "\v\n" );
return;
@ -617,51 +490,43 @@ socket_connect_one( conn_t *sock )
socket_connected( sock );
}
static void
socket_connect_next( conn_t *conn )
{
sys_error( "Cannot connect to %s", conn->name );
free( conn->name );
conn->name = NULL;
conn->curr_addr = conn->curr_addr->ai_next;
socket_connect_one( conn );
}
static void
socket_connect_failed( conn_t *conn )
{
sys_error( "Cannot connect to %s", conn->name );
socket_close_internal( conn );
socket_connect_next( conn );
free( conn->name );
conn->name = 0;
#ifdef HAVE_IPV6
conn->curr_addr = conn->curr_addr->ai_next;
#else
conn->curr_addr++;
#endif
socket_connect_one( conn );
}
static void
socket_connected( conn_t *conn )
{
if (conn->addrs) {
freeaddrinfo( conn->addrs );
conn->addrs = NULL;
}
#ifdef HAVE_IPV6
freeaddrinfo( conn->addrs );
#endif
conf_notifier( &conn->notify, 0, POLLIN );
socket_expect_activity( conn, 0 );
conn->state = SCK_READY;
conn->callbacks.connect( 1, conn->callback_aux );
}
static void
socket_cleanup_names( conn_t *conn )
{
if (conn->addrs) {
freeaddrinfo( conn->addrs );
conn->addrs = NULL;
}
free( conn->name );
conn->name = NULL;
}
static void
socket_connect_bail( conn_t *conn )
{
socket_cleanup_names( conn );
#ifdef HAVE_IPV6
if (conn->addrs) {
freeaddrinfo( conn->addrs );
conn->addrs = 0;
}
#endif
free( conn->name );
conn->name = 0;
conn->callbacks.connect( 0, conn->callback_aux );
}
@ -672,11 +537,12 @@ socket_close( conn_t *sock )
{
if (sock->fd >= 0)
socket_close_internal( sock );
socket_cleanup_names( sock );
free( sock->name );
sock->name = 0;
#ifdef HAVE_LIBSSL
if (sock->ssl) {
SSL_free( sock->ssl );
sock->ssl = NULL;
sock->ssl = 0;
wipe_wakeup( &sock->ssl_fake );
}
#endif
@ -684,23 +550,23 @@ socket_close( conn_t *sock )
if (sock->in_z) {
inflateEnd( sock->in_z );
free( sock->in_z );
sock->in_z = NULL;
sock->in_z = 0;
deflateEnd( sock->out_z );
free( sock->out_z );
sock->out_z = NULL;
sock->out_z = 0;
wipe_wakeup( &sock->z_fake );
}
#endif
while (sock->write_buf)
dispose_chunk( sock );
free( sock->append_buf );
sock->append_buf = NULL;
sock->append_buf = 0;
}
static int
prepare_read( conn_t *sock, char **buf, uint *len )
prepare_read( conn_t *sock, char **buf, int *len )
{
uint n = sock->offset + sock->bytes;
int n = sock->offset + sock->bytes;
if (!(*len = sizeof(sock->buf) - n)) {
error( "Socket error: receive buffer full. Probably protocol error.\n" );
socket_fail( sock );
@ -711,17 +577,17 @@ prepare_read( conn_t *sock, char **buf, uint *len )
}
static int
do_read( conn_t *sock, char *buf, uint len )
do_read( conn_t *sock, char *buf, int len )
{
int n;
assert( sock->fd >= 0 );
#ifdef HAVE_LIBSSL
if (sock->ssl) {
if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, (int)len ) )) <= 0)
if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, len ) )) <= 0)
return n;
if (n == (int)len && SSL_pending( sock->ssl ))
if (n == len && SSL_pending( sock->ssl ))
conf_wakeup( &sock->ssl_fake, 0 );
} else
#endif
@ -744,8 +610,7 @@ static void
socket_fill_z( conn_t *sock )
{
char *buf;
uint len;
int ret;
int len, ret;
if (prepare_read( sock, &buf, &len ) < 0)
return;
@ -754,10 +619,8 @@ socket_fill_z( conn_t *sock )
sock->in_z->next_out = (unsigned char *)buf;
ret = inflate( sock->in_z, Z_SYNC_FLUSH );
/* Z_BUF_ERROR happens here when the previous call both consumed
* all input and exactly filled up the output buffer. */
if (ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_STREAM_END) {
error( "Error decompressing data from %s: %s\n", sock->name, z_err_msg( ret, sock->in_z ) );
if (ret != Z_OK && ret != Z_STREAM_END) {
error( "Error decompressing data from %s: %s\n", sock->name, sock->in_z->msg );
socket_fail( sock );
return;
}
@ -765,7 +628,7 @@ socket_fill_z( conn_t *sock )
if (!sock->in_z->avail_out)
conf_wakeup( &sock->z_fake, 0 );
if ((len = (uint)((char *)sock->in_z->next_out - buf))) {
if ((len = (char *)sock->in_z->next_out - buf)) {
sock->bytes += len;
sock->read_callback( sock->callback_aux );
}
@ -783,37 +646,29 @@ socket_fill( conn_t *sock )
sock->in_z->next_in = (uchar *)sock->z_buf;
if ((ret = do_read( sock, sock->z_buf, sizeof(sock->z_buf) )) <= 0)
return;
sock->in_z->avail_in = (uint)ret;
sock->in_z->avail_in = ret;
socket_fill_z( sock );
} else
#endif
{
char *buf;
uint len;
int len;
if (prepare_read( sock, &buf, &len ) < 0)
return;
int n;
if ((n = do_read( sock, buf, len )) <= 0)
if ((len = do_read( sock, buf, len )) <= 0)
return;
sock->bytes += (uint)n;
sock->bytes += len;
sock->read_callback( sock->callback_aux );
}
}
void
socket_expect_activity( conn_t *conn, int expect )
{
if (conn->conf->timeout > 0 && expect != pending_wakeup( &conn->fd_timeout ))
conf_wakeup( &conn->fd_timeout, expect ? conn->conf->timeout : -1 );
}
int
socket_read( conn_t *conn, char *buf, uint len )
socket_read( conn_t *conn, char *buf, int len )
{
uint n = conn->bytes;
int n = conn->bytes;
if (!n && conn->state == SCK_EOF)
return -1;
if (n > len)
@ -823,14 +678,14 @@ socket_read( conn_t *conn, char *buf, uint len )
conn->offset = 0;
else
conn->offset += n;
return (int)n;
return n;
}
char *
socket_read_line( conn_t *b )
{
char *p, *s;
uint n;
int n;
s = b->buf + b->offset;
p = memchr( s + b->scanoff, '\n', b->bytes - b->scanoff );
@ -842,9 +697,9 @@ socket_read_line( conn_t *b )
}
if (b->state == SCK_EOF)
return (void *)~0;
return NULL;
return 0;
}
n = (uint)(p + 1 - s);
n = p + 1 - s;
b->offset += n;
b->bytes -= n;
b->scanoff = 0;
@ -855,14 +710,14 @@ socket_read_line( conn_t *b )
}
static int
do_write( conn_t *sock, char *buf, uint len )
do_write( conn_t *sock, char *buf, int len )
{
int n;
assert( sock->fd >= 0 );
#ifdef HAVE_LIBSSL
if (sock->ssl)
return ssl_return( "write to", sock, SSL_write( sock->ssl, buf, (int)len ) );
return ssl_return( "write to", sock, SSL_write( sock->ssl, buf, len ) );
#endif
n = write( sock->fd, buf, len );
if (n < 0) {
@ -873,7 +728,7 @@ do_write( conn_t *sock, char *buf, uint len )
n = 0;
conf_notifier( &sock->notify, POLLIN, POLLOUT );
}
} else if (n != (int)len) {
} else if (n != len) {
conf_notifier( &sock->notify, POLLIN, POLLOUT );
}
return n;
@ -898,12 +753,12 @@ do_queued_write( conn_t *conn )
return 0;
while ((bc = conn->write_buf)) {
int n;
uint len = bc->len - conn->write_offset;
int n, len = bc->len - conn->write_offset;
if ((n = do_write( conn, bc->data + conn->write_offset, len )) < 0)
return -1;
if (n != (int)len) {
conn->write_offset += (uint)n;
if (n != len) {
conn->write_offset += n;
conn->writing = 1;
return 0;
}
conn->write_offset = 0;
@ -913,6 +768,7 @@ do_queued_write( conn_t *conn )
if (conn->ssl && SSL_pending( conn->ssl ))
conf_wakeup( &conn->ssl_fake, 0 );
#endif
conn->writing = 0;
conn->write_callback( conn->callback_aux );
return -1;
}
@ -920,7 +776,7 @@ do_queued_write( conn_t *conn )
static void
do_append( conn_t *conn, buff_chunk_t *bc )
{
bc->next = NULL;
bc->next = 0;
conn->buffer_mem += bc->len;
*conn->write_buf_append = bc;
conn->write_buf_append = &bc->next;
@ -936,11 +792,10 @@ do_flush( conn_t *conn )
buff_chunk_t *bc = conn->append_buf;
#ifdef HAVE_LIBZ
if (conn->out_z) {
uint buf_avail = conn->append_avail;
int buf_avail = conn->append_avail;
if (!conn->z_written)
return;
do {
int ret;
if (!bc) {
buf_avail = WRITE_CHUNK_SIZE;
bc = nfmalloc( offsetof(buff_chunk_t, data) + buf_avail );
@ -950,17 +805,14 @@ do_flush( conn_t *conn )
conn->out_z->avail_in = 0;
conn->out_z->next_out = (uchar *)bc->data + bc->len;
conn->out_z->avail_out = buf_avail;
/* Z_BUF_ERROR cannot happen here, as zlib suppresses the error
* both upon increasing the flush level (1st iteration) and upon
* a no-op after the output buffer was full (later iterations). */
if ((ret = deflate( conn->out_z, Z_PARTIAL_FLUSH )) != Z_OK) {
error( "Fatal: Compression error: %s\n", z_err_msg( ret, conn->out_z ) );
if (deflate( conn->out_z, Z_PARTIAL_FLUSH ) != Z_OK) {
error( "Fatal: Compression error: %s\n", conn->out_z->msg );
abort();
}
bc->len = (uint)((char *)conn->out_z->next_out - bc->data);
bc->len = (char *)conn->out_z->next_out - bc->data;
if (bc->len) {
do_append( conn, bc );
bc = NULL;
bc = 0;
buf_avail = 0;
} else {
buf_avail = conn->out_z->avail_out;
@ -973,7 +825,7 @@ do_flush( conn_t *conn )
#endif
if (bc) {
do_append( conn, bc );
conn->append_buf = NULL;
conn->append_buf = 0;
#ifdef HAVE_LIBZ
conn->append_avail = 0;
#endif
@ -983,8 +835,7 @@ do_flush( conn_t *conn )
void
socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
{
int i;
uint buf_avail, len, offset = 0, total = 0;
int i, buf_avail, len, offset = 0, total = 0;
buff_chunk_t *bc;
for (i = 0; i < iovcnt; i++)
@ -1017,18 +868,15 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
len = iov->len - offset;
#ifdef HAVE_LIBZ
if (conn->out_z) {
int ret;
conn->out_z->next_in = (uchar *)iov->buf + offset;
conn->out_z->avail_in = len;
conn->out_z->next_out = (uchar *)bc->data + bc->len;
conn->out_z->avail_out = buf_avail;
/* Z_BUF_ERROR is impossible here, as the input buffer always has data,
* and the output buffer always has space. */
if ((ret = deflate( conn->out_z, Z_NO_FLUSH )) != Z_OK) {
error( "Fatal: Compression error: %s\n", z_err_msg( ret, conn->out_z ) );
if (deflate( conn->out_z, Z_NO_FLUSH ) != Z_OK) {
error( "Fatal: Compression error: %s\n", conn->out_z->msg );
abort();
}
bc->len = (uint)((char *)conn->out_z->next_out - bc->data);
bc->len = (char *)conn->out_z->next_out - bc->data;
buf_avail = conn->out_z->avail_out;
len -= conn->out_z->avail_in;
conn->z_written = 1;
@ -1051,7 +899,7 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
}
if (!buf_avail) {
do_append( conn, bc );
bc = NULL;
bc = 0;
break;
}
}
@ -1091,15 +939,12 @@ socket_fd_cb( int events, void *aux )
if (events & POLLOUT)
conf_notifier( &conn->notify, POLLIN, 0 );
if (pending_wakeup( &conn->fd_timeout ))
conf_wakeup( &conn->fd_timeout, conn->conf->timeout );
#ifdef HAVE_LIBSSL
if (conn->state == SCK_STARTTLS) {
start_tls_p2( conn );
return;
}
if (conn->ssl) {
if (conn->state == SCK_STARTTLS) {
start_tls_p2( conn );
return;
}
if (do_queued_write( conn ) < 0)
return;
socket_fill( conn );
@ -1121,24 +966,10 @@ socket_fake_cb( void *aux )
/* Ensure that a pending write gets queued. */
do_flush( conn );
/* If no writes are ongoing, start writing now. */
if (!(notifier_config( &conn->notify ) & POLLOUT))
if (!conn->writing)
do_queued_write( conn );
}
static void
socket_timeout_cb( void *aux )
{
conn_t *conn = (conn_t *)aux;
if (conn->state == SCK_CONNECTING) {
errno = ETIMEDOUT;
socket_connect_failed( conn );
} else {
error( "Socket error on %s: timeout.\n", conn->name );
socket_fail( conn );
}
}
#ifdef HAVE_LIBZ
static void
z_fake_cb( void *aux )

View file

@ -30,40 +30,38 @@
#endif
#ifdef HAVE_LIBSSL
# include <openssl/ssl.h>
# include <openssl/x509.h>
typedef struct ssl_st SSL;
typedef struct ssl_ctx_st SSL_CTX;
typedef struct stack_st _STACK;
enum {
SSLv2 = 1,
SSLv3 = 2,
TLSv1 = 4,
TLSv1_1 = 8,
TLSv1_2 = 16,
TLSv1_3 = 32
TLSv1_2 = 16
};
#endif
typedef struct {
typedef struct server_conf {
char *tunnel;
char *host;
ushort port;
int timeout;
int port;
#ifdef HAVE_LIBSSL
char *cert_file;
char *client_certfile;
char *client_keyfile;
char *cipher_string;
char system_certs;
char ssl_versions;
/* these are actually variables and are leaked at the end */
char ssl_ctx_valid;
STACK_OF(X509) *trusted_certs;
_STACK *trusted_certs;
SSL_CTX *SSLContext;
#endif
} server_conf_t;
typedef struct buff_chunk {
struct buff_chunk *next;
uint len;
int len;
char data[1];
} buff_chunk_t;
@ -75,7 +73,7 @@ typedef struct {
#ifdef HAVE_IPV6
struct addrinfo *addrs, *curr_addr; /* needed during connect */
#else
struct addr_info *addrs, *curr_addr; /* needed during connect */
char **curr_addr; /* needed during connect */
#endif
char *name;
#ifdef HAVE_LIBSSL
@ -99,21 +97,21 @@ typedef struct {
notifier_t notify;
wakeup_t fd_fake;
wakeup_t fd_timeout;
/* writing */
buff_chunk_t *append_buf; /* accumulating buffer */
buff_chunk_t *write_buf, **write_buf_append; /* buffer head & tail */
int writing;
#ifdef HAVE_LIBZ
uint append_avail; /* space left in accumulating buffer */
int append_avail; /* space left in accumulating buffer */
#endif
uint write_offset; /* offset into buffer head */
uint buffer_mem; /* memory currently occupied by buffers in the queue */
int write_offset; /* offset into buffer head */
int buffer_mem; /* memory currently occupied by buffers in the queue */
/* reading */
uint offset; /* start of filled bytes in buffer */
uint bytes; /* number of filled bytes in buffer */
uint scanoff; /* offset to continue scanning for newline at, relative to 'offset' */
int offset; /* start of filled bytes in buffer */
int bytes; /* number of filled bytes in buffer */
int scanoff; /* offset to continue scanning for newline at, relative to 'offset' */
char buf[100000];
#ifdef HAVE_LIBZ
char z_buf[100000];
@ -134,20 +132,19 @@ static INLINE void socket_init( conn_t *conn,
conn->write_callback = write_callback;
conn->callback_aux = aux;
conn->fd = -1;
conn->name = NULL;
conn->name = 0;
conn->write_buf_append = &conn->write_buf;
}
void socket_connect( conn_t *conn, void (*cb)( int ok, void *aux ) );
void socket_start_tls(conn_t *conn, void (*cb)( int ok, void *aux ) );
void socket_start_deflate( conn_t *conn );
void socket_close( conn_t *sock );
void socket_expect_activity( conn_t *sock, int expect );
int socket_read( conn_t *sock, char *buf, uint len ); /* never waits */
int socket_read( conn_t *sock, char *buf, int len ); /* never waits */
char *socket_read_line( conn_t *sock ); /* don't free return value; never waits */
typedef enum { KeepOwn = 0, GiveOwn } ownership_t;
typedef struct {
typedef struct conn_iovec {
char *buf;
uint len;
int len;
ownership_t takeOwn;
} conn_iovec_t;
void socket_write( conn_t *sock, conn_iovec_t *iov, int iovcnt );

1783
src/sync.c

File diff suppressed because it is too large Load diff

View file

@ -25,8 +25,8 @@
#include "driver.h"
#define F 0 // far side
#define N 1 // near side
#define M 0 /* master */
#define S 1 /* slave */
#define OP_NEW (1<<0)
#define OP_RENEW (1<<1)
@ -40,7 +40,6 @@
#define XOP_PULL (1<<9)
#define XOP_MASK_DIR (XOP_PUSH|XOP_PULL)
#define XOP_HAVE_TYPE (1<<10)
// The following must all have the same bit shift from the corresponding OP_* flags.
#define XOP_HAVE_EXPUNGE (1<<11)
#define XOP_HAVE_CREATE (1<<12)
#define XOP_HAVE_REMOVE (1<<13)
@ -53,7 +52,7 @@ typedef struct channel_conf {
char *sync_state;
string_list_t *patterns;
int ops[2];
int max_messages; // For near side only.
uint max_messages; /* for slave only */
signed char expire_unread;
char use_internal_date;
} channel_conf_t;
@ -68,11 +67,12 @@ extern channel_conf_t global_conf;
extern channel_conf_t *channels;
extern group_conf_t *groups;
extern const char *str_fn[2], *str_hl[2];
extern const char *str_ms[2], *str_hl[2];
#define SYNC_OK 0 /* assumed to be 0 */
#define SYNC_FAIL 1
#define SYNC_BAD(fn) (4<<(fn))
#define SYNC_FAIL_ALL 2
#define SYNC_BAD(ms) (4<<(ms))
#define SYNC_NOGOOD 16 /* internal */
#define SYNC_CANCELED 32 /* internal */
@ -81,7 +81,7 @@ extern const char *str_fn[2], *str_hl[2];
#define BOX_PRESENT 1
/* All passed pointers must stay alive until cb is called. */
void sync_boxes( store_t *ctx[], const char * const names[], int present[], channel_conf_t *chan,
void sync_boxes( store_t *ctx[], const char *names[], int present[], channel_conf_t *chan,
void (*cb)( int sts, void *aux ), void *aux );
#endif

View file

@ -29,16 +29,16 @@
int DFlags;
const char *Home;
typedef struct {
struct tst {
int id;
int first, other, morph_at, morph_to;
time_t start;
wakeup_t timer;
wakeup_t morph_timer;
} tst_t;
};
static void
timer_start( tst_t *timer, int to )
timer_start( struct tst *timer, int to )
{
printf( "starting timer %d, should expire after %d\n", timer->id, to );
time( &timer->start );
@ -48,7 +48,7 @@ timer_start( tst_t *timer, int to )
static void
timed_out( void *aux )
{
tst_t *timer = (tst_t *)aux;
struct tst *timer = (struct tst *)aux;
printf( "timer %d expired after %d, repeat %d\n",
timer->id, (int)(time( 0 ) - timer->start), timer->other );
@ -64,7 +64,7 @@ timed_out( void *aux )
static void
morph_timed_out( void *aux )
{
tst_t *timer = (tst_t *)aux;
struct tst *timer = (struct tst *)aux;
printf( "morphing timer %d after %d\n",
timer->id, (int)(time( 0 ) - timer->start) );
@ -80,7 +80,7 @@ main( int argc, char **argv )
for (i = 1; i < argc; i++) {
char *val = argv[i];
tst_t *timer = nfmalloc( sizeof(*timer) );
struct tst *timer = nfmalloc( sizeof(*timer) );
init_wakeup( &timer->timer, timed_out, timer );
init_wakeup( &timer->morph_timer, morph_timed_out, timer );
timer->id = ++nextid;

View file

@ -23,11 +23,9 @@
#include "common.h"
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <pwd.h>
@ -44,7 +42,7 @@ flushn( void )
}
}
static void ATTR_PRINTFLIKE(1, 0)
static void
printn( const char *msg, va_list va )
{
if (*msg == '\v')
@ -151,34 +149,25 @@ error( const char *msg, ... )
}
void
vsys_error( const char *msg, va_list va )
sys_error( const char *msg, ... )
{
va_list va;
char buf[1024];
int errno_bak = errno;
flushn();
va_start( va, msg );
if ((uint)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
oob();
errno = errno_bak;
va_end( va );
perror( buf );
}
void
sys_error( const char *msg, ... )
{
va_list va;
va_start( va, msg );
vsys_error( msg, va );
va_end( va );
}
void
add_string_list_n( string_list_t **list, const char *str, uint len )
add_string_list_n( string_list_t **list, const char *str, int len )
{
string_list_t *elem;
elem = nfmalloc( offsetof(string_list_t, string) + len + 1 );
elem = nfmalloc( sizeof(*elem) + len );
elem->next = *list;
*list = elem;
memcpy( elem->string, str, len );
@ -214,7 +203,7 @@ vasprintf( char **strp, const char *fmt, va_list ap )
if (len >= (int)sizeof(tmp))
vsprintf( *strp, fmt, ap );
else
memcpy( *strp, tmp, (size_t)len + 1 );
memcpy( *strp, tmp, len + 1 );
return len;
}
#endif
@ -233,42 +222,47 @@ memrchr( const void *s, int c, size_t n )
#endif
#ifndef HAVE_STRNLEN
size_t
int
strnlen( const char *str, size_t maxlen )
{
const char *estr = memchr( str, 0, maxlen );
return estr ? (size_t)(estr - str) : maxlen;
size_t len;
/* It's tempting to use memchr(), but it's allowed to read past the end of the actual string. */
for (len = 0; len < maxlen && str[len]; len++) {}
return len;
}
#endif
int
starts_with( const char *str, int strl, const char *cmp, uint cmpl )
starts_with( const char *str, int strl, const char *cmp, int cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return ((uint)strl >= cmpl) && !memcmp( str, cmp, cmpl );
return (strl >= cmpl) && !memcmp( str, cmp, cmpl );
}
int
starts_with_upper( const char *str, int strl, const char *cmp, uint cmpl )
starts_with_upper( const char *str, int strl, const char *cmp, int cmpl )
{
int i;
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
if ((uint)strl < cmpl)
if (strl < cmpl)
return 0;
for (uint i = 0; i < cmpl; i++)
for (i = 0; i < cmpl; i++)
if (str[i] != cmp[i] && toupper( str[i] ) != cmp[i])
return 0;
return 1;
}
int
equals( const char *str, int strl, const char *cmp, uint cmpl )
equals( const char *str, int strl, const char *cmp, int cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return ((uint)strl == cmpl) && !memcmp( str, cmp, cmpl );
return (strl == cmpl) && !memcmp( str, cmp, cmpl );
}
#ifndef HAVE_TIMEGM
@ -350,13 +344,13 @@ nfsnprintf( char *buf, int blen, const char *fmt, ... )
va_list va;
va_start( va, fmt );
if (blen <= 0 || (uint)(ret = vsnprintf( buf, (size_t)blen, fmt, va )) >= (uint)blen)
if (blen <= 0 || (uint)(ret = vsnprintf( buf, blen, fmt, va )) >= (uint)blen)
oob();
va_end( va );
return ret;
}
void
static void ATTR_NORETURN
oom( void )
{
fputs( "Fatal: Out of memory\n", stderr );
@ -459,20 +453,20 @@ expand_strdup( const char *s )
if (*s == '~') {
s++;
if (!*s) {
p = NULL;
p = 0;
q = Home;
} else if (*s == '/') {
p = s;
q = Home;
} else {
if ((p = strchr( s, '/' ))) {
r = nfstrndup( s, (size_t)(p - s) );
r = nfstrndup( s, (int)(p - s) );
pw = getpwnam( r );
free( r );
} else
pw = getpwnam( s );
if (!pw)
return NULL;
return 0;
q = pw->pw_dir;
}
nfasprintf( &r, "%s%s", q, p ? p : "" );
@ -483,25 +477,24 @@ expand_strdup( const char *s )
/* Return value: 0 = ok, -1 = out found in arg, -2 = in found in arg but no out specified */
int
map_name( const char *arg, char **result, uint reserve, const char *in, const char *out )
map_name( const char *arg, char **result, int reserve, const char *in, const char *out )
{
char *p;
uint i, l, ll, num, inl, outl;
int i, l, ll, num, inl, outl;
assert( arg );
l = strlen( arg );
assert( in );
inl = strlen( in );
if (!inl) {
if (!in) {
copy:
*result = nfmalloc( reserve + l + 1 );
memcpy( *result + reserve, arg, l + 1 );
return 0;
}
assert( out );
outl = strlen( out );
if (equals( in, (int)inl, out, outl ))
goto copy;
inl = strlen( in );
if (out) {
outl = strlen( out );
if (inl == outl && !memcmp( in, out, inl ))
goto copy;
}
for (num = 0, i = 0; i < l; ) {
for (ll = 0; ll < inl; ll++)
if (arg[i + ll] != in[ll])
@ -510,7 +503,7 @@ map_name( const char *arg, char **result, uint reserve, const char *in, const ch
i += inl;
continue;
fout:
if (outl) {
if (out) {
for (ll = 0; ll < outl; ll++)
if (arg[i + ll] != out[ll])
goto fnexti;
@ -521,7 +514,7 @@ map_name( const char *arg, char **result, uint reserve, const char *in, const ch
}
if (!num)
goto copy;
if (!outl)
if (!out)
return -2;
*result = nfmalloc( reserve + l + num * (outl - inl) + 1 );
p = *result + reserve;
@ -541,35 +534,15 @@ map_name( const char *arg, char **result, uint reserve, const char *in, const ch
}
static int
compare_uints( const void *l, const void *r )
compare_ints( const void *l, const void *r )
{
uint li = *(const uint *)l, ri = *(const uint *)r;
if (li != ri) // Can't subtract, the result might not fit into signed int.
return li > ri ? 1 : -1;
return 0;
return *(int *)l - *(int *)r;
}
void
sort_uint_array( uint_array_t array )
sort_ints( int *arr, int len )
{
qsort( array.data, array.size, sizeof(uint), compare_uints );
}
int
find_uint_array( uint_array_t array, uint value )
{
uint bot = 0, top = array.size;
while (bot < top) {
uint i = (bot + top) / 2;
uint elt = array.data[i];
if (elt == value)
return 1;
if (elt < value)
bot = i + 1;
else
top = i;
}
return 0;
qsort( arr, len, sizeof(int), compare_ints );
}
@ -594,7 +567,7 @@ arc4_init( void )
close( fd );
for (i = 0; i < 256; i++)
rs.s[i] = (uchar)i;
rs.s[i] = i;
for (i = j = 0; i < 256; i++) {
si = rs.s[i];
j += si + dat[i & 127];
@ -626,13 +599,13 @@ static const uchar prime_deltas[] = {
1, 29, 3, 21, 7, 17, 15, 9, 43, 35, 15, 0, 0, 0, 0, 0
};
uint
bucketsForSize( uint size )
int
bucketsForSize( int size )
{
uint base = 4, bits = 2;
int base = 4, bits = 2;
for (;;) {
uint prime = base + prime_deltas[bits];
int prime = base + prime_deltas[bits];
if (prime >= size)
return prime;
base <<= 1;
@ -660,14 +633,14 @@ list_unlink( list_head_t *head )
assert( head->prev->next == head);
head->next->prev = head->prev;
head->prev->next = head->next;
head->next = head->prev = NULL;
head->next = head->prev = 0;
}
static notifier_t *notifiers;
static int changed; /* Iterator may be invalid now. */
#ifdef HAVE_POLL_H
#ifdef HAVE_SYS_POLL_H
static struct pollfd *pollfds;
static uint npolls, rpolls;
static int npolls, rpolls;
#else
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
@ -677,8 +650,8 @@ static uint npolls, rpolls;
void
init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux )
{
#ifdef HAVE_POLL_H
uint idx = npolls++;
#ifdef HAVE_SYS_POLL_H
int idx = npolls++;
if (rpolls < npolls) {
rpolls = npolls;
pollfds = nfrealloc( pollfds, npolls * sizeof(*pollfds) );
@ -697,41 +670,31 @@ init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux )
}
void
conf_notifier( notifier_t *sn, short and_events, short or_events )
conf_notifier( notifier_t *sn, int and_events, int or_events )
{
#ifdef HAVE_POLL_H
uint idx = sn->index;
#ifdef HAVE_SYS_POLL_H
int idx = sn->index;
pollfds[idx].events = (pollfds[idx].events & and_events) | or_events;
#else
sn->events = (sn->events & and_events) | or_events;
#endif
}
short
notifier_config( notifier_t *sn )
{
#ifdef HAVE_POLL_H
return pollfds[sn->index].events;
#else
return sn->events;
#endif
}
void
wipe_notifier( notifier_t *sn )
{
notifier_t **snp;
#ifdef HAVE_POLL_H
uint idx;
#ifdef HAVE_SYS_POLL_H
int idx;
#endif
for (snp = &notifiers; *snp != sn; snp = &(*snp)->next)
assert( *snp );
*snp = sn->next;
sn->next = NULL;
sn->next = 0;
changed = 1;
#ifdef HAVE_POLL_H
#ifdef HAVE_SYS_POLL_H
idx = sn->index;
memmove( pollfds + idx, pollfds + idx + 1, (--npolls - idx) * sizeof(*pollfds) );
for (sn = notifiers; sn; sn = sn->next) {
@ -741,10 +704,17 @@ wipe_notifier( notifier_t *sn )
#endif
}
static int nowvalid;
static time_t now;
static time_t
get_now( void )
{
return time( NULL );
if (!nowvalid) {
nowvalid = 1;
return time( &now );
}
return now;
}
static list_head_t timers = { &timers, &timers };
@ -754,7 +724,7 @@ init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux )
{
tmr->cb = cb;
tmr->aux = aux;
tmr->links.next = tmr->links.prev = NULL;
tmr->links.next = tmr->links.prev = 0;
}
void
@ -773,12 +743,12 @@ conf_wakeup( wakeup_t *tmr, int to )
if (tmr->links.next)
list_unlink( &tmr->links );
} else {
time_t timeout = to;
time_t timeout = get_now() + to;
tmr->timeout = timeout;
if (!to) {
/* We always prepend null timers, to cluster related events. */
succ = timers.next;
} else {
timeout += get_now();
/* We start at the end in the expectation that the newest timer is likely to fire last
* (which will be true only if all timeouts are equal, but it's an as good guess as any). */
for (succ = &timers; (head = succ->prev) != &timers; succ = head) {
@ -787,7 +757,6 @@ conf_wakeup( wakeup_t *tmr, int to )
}
assert( head != &tmr->links );
}
tmr->timeout = timeout;
if (succ != &tmr->links) {
if (tmr->links.next)
list_unlink( &tmr->links );
@ -796,6 +765,11 @@ conf_wakeup( wakeup_t *tmr, int to )
}
}
#define shifted_bit(in, from, to) \
(((uint)(in) & from) \
/ (from > to ? from / to : 1) \
* (to > from ? to / from : 1))
static void
event_wait( void )
{
@ -803,17 +777,18 @@ event_wait( void )
notifier_t *sn;
int m;
#ifdef HAVE_POLL_H
#ifdef HAVE_SYS_POLL_H
int timeout = -1;
nowvalid = 0;
if ((head = timers.next) != &timers) {
wakeup_t *tmr = (wakeup_t *)head;
time_t delta = tmr->timeout;
if (!delta || (delta -= get_now()) <= 0) {
int delta = tmr->timeout - get_now();
if (delta <= 0) {
list_unlink( head );
tmr->cb( tmr->aux );
return;
}
timeout = (int)delta * 1000;
timeout = delta * 1000;
}
switch (poll( pollfds, npolls, timeout )) {
case 0:
@ -825,7 +800,7 @@ event_wait( void )
break;
}
for (sn = notifiers; sn; sn = sn->next) {
uint n = sn->index;
int n = sn->index;
if ((m = pollfds[n].revents)) {
assert( !(m & POLLNVAL) );
sn->cb( m | shifted_bit( m, POLLHUP, POLLIN ), sn->aux );
@ -841,10 +816,11 @@ event_wait( void )
fd_set rfds, wfds, efds;
int fd;
nowvalid = 0;
if ((head = timers.next) != &timers) {
wakeup_t *tmr = (wakeup_t *)head;
time_t delta = tmr->timeout;
if (!delta || (delta -= get_now()) <= 0) {
int delta = tmr->timeout - get_now();
if (delta <= 0) {
list_unlink( head );
tmr->cb( tmr->aux );
return;