Compare commits
No commits in common. "filter" and "v1.2.2" have entirely different histories.
53 changed files with 5224 additions and 6194 deletions
58
.gitignore
vendored
58
.gitignore
vendored
|
@ -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
|
||||
*~
|
||||
|
|
41
Dockerfile
41
Dockerfile
|
@ -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
|
21
Makefile.am
21
Makefile.am
|
@ -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
34
NEWS
|
@ -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
21
README
|
@ -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
46
TODO
|
@ -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
3
build
|
@ -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
|
132
configure.ac
132
configure.ac
|
@ -1,69 +1,19 @@
|
|||
AC_INIT([isync], [1.4.4])
|
||||
AC_INIT([isync], [1.2.2])
|
||||
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
12
debian/.gitignore
vendored
|
@ -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
13
debian/README.Debian
vendored
|
@ -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
94
debian/changelog
vendored
|
@ -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
2
debian/compat
vendored
|
@ -1 +1 @@
|
|||
9
|
||||
4
|
||||
|
|
42
debian/control
vendored
42
debian/control
vendored
|
@ -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
59
debian/copyright
vendored
|
@ -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
1
debian/dirs
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
usr/bin
|
3
debian/docs
vendored
Normal file
3
debian/docs
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
NEWS
|
||||
README
|
||||
TODO
|
39
debian/generate-deb
vendored
Executable file
39
debian/generate-deb
vendored
Executable 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
57
debian/rules
vendored
|
@ -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
|
||||
|
|
1
debian/source/format
vendored
1
debian/source/format
vendored
|
@ -1 +0,0 @@
|
|||
3.0 (quilt)
|
2
debian/watch
vendored
2
debian/watch
vendored
|
@ -1,2 +1,2 @@
|
|||
version=3
|
||||
http://sf.net/isync/ isync-(.*)\.tar\.gz
|
||||
http://sf.net/isync/ isync-(.*)\.tar\.gz debian uupdate
|
||||
|
|
|
@ -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
13
src/.gitignore
vendored
|
@ -1,8 +1,7 @@
|
|||
/drv_proxy.inc
|
||||
/mbsync
|
||||
/mdconvert
|
||||
/tst_timers
|
||||
/tmp/
|
||||
|
||||
.deps/
|
||||
.deps
|
||||
Makefile
|
||||
Makefile.in
|
||||
mbsync
|
||||
mdconvert
|
||||
tmp
|
||||
*.o
|
||||
|
|
|
@ -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)
|
||||
|
|
118
src/common.h
118
src/common.h
|
@ -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
5
src/compat/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
.deps
|
||||
Makefile
|
||||
Makefile.in
|
||||
isync
|
||||
*.o
|
12
src/compat/Makefile.am
Normal file
12
src/compat/Makefile.am
Normal 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
554
src/compat/config.c
Normal 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
265
src/compat/convert.c
Normal 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
328
src/compat/isync.1
Normal 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
111
src/compat/isync.h
Normal 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
55
src/compat/isyncrc.sample
Normal 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
439
src/compat/main.c
Normal 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
208
src/compat/util.c
Normal 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;
|
||||
}
|
179
src/config.c
179
src/config.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
14
src/driver.c
14
src/driver.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
216
src/driver.h
216
src/driver.h
|
@ -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;
|
||||
int 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; */
|
||||
int 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
|
||||
|
|
2423
src/drv_imap.c
2423
src/drv_imap.c
File diff suppressed because it is too large
Load diff
1058
src/drv_maildir.c
1058
src/drv_maildir.c
File diff suppressed because it is too large
Load diff
447
src/drv_proxy.c
447
src/drv_proxy.c
|
@ -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"
|
|
@ -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;
|
529
src/main.c
529
src/main.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
414
src/mbsync.1
414
src/mbsync.1
|
@ -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,11 +234,11 @@ 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.
|
||||
|
@ -257,46 +246,25 @@ 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 +272,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 +317,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 +331,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 +369,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 +391,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 +422,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 +440,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 +450,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 +489,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 +500,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 +525,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 +541,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 +578,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 +596,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 +606,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 +614,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
|
||||
|
@ -779,9 +658,6 @@ 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:
|
||||
|
@ -789,7 +665,7 @@ Mutt always does that, while mu4e needs to be configured to do it:
|
|||
.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 +676,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 +684,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,
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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;
|
||||
|
|
661
src/run-tests.pl
661
src/run-tests.pl
|
@ -18,38 +18,22 @@
|
|||
|
||||
use warnings;
|
||||
use strict;
|
||||
use Cwd;
|
||||
use File::Path;
|
||||
use File::Temp 'tempdir';
|
||||
|
||||
my $use_vg = $ENV{USE_VALGRIND};
|
||||
my $mbsync = getcwd()."/mbsync";
|
||||
|
||||
if (!-d "tmp") {
|
||||
unlink "tmp";
|
||||
my $tdir = tempdir();
|
||||
symlink $tdir, "tmp" or die "Cannot symlink temp directory: $!\n";
|
||||
}
|
||||
-d "tmp" or mkdir "tmp";
|
||||
chdir "tmp" or die "Cannot enter temp direcory.\n";
|
||||
|
||||
sub show($$$);
|
||||
sub test($$$$);
|
||||
sub test($$$@);
|
||||
|
||||
################################################################################
|
||||
|
||||
# Format of the test defs: [ far, near, state ]
|
||||
# far/near: [ maxuid, { seq, uid, flags }... ]
|
||||
# state: [ MaxPulledUid, MaxExpiredFarUid, MaxPushedUid, { muid, suid, flags }... ]
|
||||
|
||||
use enum qw(:=1 A..Z);
|
||||
sub mn($) { chr(64 + shift) }
|
||||
|
||||
# generic syncing tests
|
||||
my @x01 = (
|
||||
[ 9,
|
||||
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
|
||||
[ 9,
|
||||
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "", J, 9, "" ],
|
||||
[ 8,
|
||||
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 0, "" ],
|
||||
[ 8,
|
||||
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "", 10, 0, "" ],
|
||||
[ 8, 0, 0,
|
||||
1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 8, 8, "" ],
|
||||
);
|
||||
|
@ -58,184 +42,138 @@ my @O01 = ("", "", "");
|
|||
#show("01", "01", "01");
|
||||
my @X01 = (
|
||||
[ 10,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "FT", G, 7, "FT", I, 9, "", J, 10, "" ],
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "FT", 7, 7, "FT", 9, 9, "", 10, 10, "" ],
|
||||
[ 10,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", G, 7, "FT", H, 8, "T", J, 9, "", I, 10, "" ],
|
||||
[ 10, 0, 10,
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 7, 7, "FT", 8, 8, "T", 9, 10, "", 10, 9, "" ],
|
||||
[ 9, 0, 9,
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 0, "", 7, 7, "FT", 0, 8, "", 10, 9, "", 9, 10, "" ],
|
||||
);
|
||||
test("full", \@x01, \@X01, \@O01);
|
||||
test("full", \@x01, \@X01, @O01);
|
||||
|
||||
my @O02 = ("", "", "Expunge Both\n");
|
||||
#show("01", "02", "02");
|
||||
my @X02 = (
|
||||
[ 10,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", I, 9, "", J, 10, "" ],
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 9, 9, "", 10, 10, "" ],
|
||||
[ 10,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", J, 9, "", I, 10, "" ],
|
||||
[ 10, 0, 10,
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 9, 10, "", 10, 9, "" ],
|
||||
[ 9, 0, 9,
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 10, 9, "", 9, 10, "" ],
|
||||
);
|
||||
test("full + expunge both", \@x01, \@X02, \@O02);
|
||||
test("full + expunge both", \@x01, \@X02, @O02);
|
||||
|
||||
my @O03 = ("", "", "Expunge Near\n");
|
||||
my @O03 = ("", "", "Expunge Slave\n");
|
||||
#show("01", "03", "03");
|
||||
my @X03 = (
|
||||
[ 10,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "FT", G, 7, "FT", I, 9, "", J, 10, "" ],
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "FT", 7, 7, "FT", 9, 9, "", 10, 10, "" ],
|
||||
[ 10,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", J, 9, "", I, 10, "" ],
|
||||
[ 10, 0, 10,
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 9, 10, "", 10, 9, "" ],
|
||||
[ 9, 0, 9,
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 0, "T", 6, 0, "", 7, 0, "T", 10, 9, "", 9, 10, "" ],
|
||||
);
|
||||
test("full + expunge near side", \@x01, \@X03, \@O03);
|
||||
test("full + expunge slave", \@x01, \@X03, @O03);
|
||||
|
||||
my @O04 = ("", "", "Sync Pull\n");
|
||||
#show("01", "04", "04");
|
||||
my @X04 = (
|
||||
[ 9,
|
||||
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
|
||||
[ 10,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", G, 7, "FT", H, 8, "T", J, 9, "", I, 10, "" ],
|
||||
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "" ],
|
||||
[ 9,
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 7, 7, "FT", 8, 8, "T", 9, 9, "", 10, 0, "" ],
|
||||
[ 9, 0, 0,
|
||||
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "", 7, 7, "FT", 0, 8, "", 9, 10, "" ],
|
||||
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "", 7, 7, "FT", 0, 8, "", 9, 9, "" ],
|
||||
);
|
||||
test("pull", \@x01, \@X04, \@O04);
|
||||
test("pull", \@x01, \@X04, @O04);
|
||||
|
||||
my @O05 = ("", "", "Sync Flags\n");
|
||||
#show("01", "05", "05");
|
||||
my @X05 = (
|
||||
[ 9,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
|
||||
[ 9,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", G, 7, "FT", H, 8, "", J, 9, "" ],
|
||||
[ 8,
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 0, "" ],
|
||||
[ 8,
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 7, 7, "FT", 8, 8, "", 10, 0, "" ],
|
||||
[ 8, 0, 0,
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "", 7, 7, "FT", 8, 8, "" ],
|
||||
);
|
||||
test("flags", \@x01, \@X05, \@O05);
|
||||
test("flags", \@x01, \@X05, @O05);
|
||||
|
||||
my @O06 = ("", "", "Sync Delete\n");
|
||||
#show("01", "06", "06");
|
||||
my @X06 = (
|
||||
[ 9,
|
||||
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "FT", G, 7, "FT", I, 9, "" ],
|
||||
[ 9,
|
||||
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "T", J, 9, "" ],
|
||||
[ 8,
|
||||
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "FT", 7, 7, "FT", 9, 0, "" ],
|
||||
[ 8,
|
||||
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "T", 10, 0, "" ],
|
||||
[ 8, 0, 0,
|
||||
1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 0, "", 7, 7, "", 0, 8, "" ],
|
||||
);
|
||||
test("deletions", \@x01, \@X06, \@O06);
|
||||
test("deletions", \@x01, \@X06, @O06);
|
||||
|
||||
my @O07 = ("", "", "Sync New\n");
|
||||
#show("01", "07", "07");
|
||||
my @X07 = (
|
||||
[ 10,
|
||||
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "", J, 10, "" ],
|
||||
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "", 10, 10, "" ],
|
||||
[ 10,
|
||||
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "", J, 9, "", I, 10, "" ],
|
||||
[ 10, 0, 10,
|
||||
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "", 9, 10, "", 10, 9, "" ],
|
||||
[ 9, 0, 9,
|
||||
1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 8, 8, "", 10, 9, "", 9, 10, "" ],
|
||||
);
|
||||
test("new", \@x01, \@X07, \@O07);
|
||||
test("new", \@x01, \@X07, @O07);
|
||||
|
||||
my @O08 = ("", "", "Sync PushFlags PullDelete\n");
|
||||
#show("01", "08", "08");
|
||||
my @X08 = (
|
||||
[ 9,
|
||||
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
|
||||
[ 9,
|
||||
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "T", J, 9, "" ],
|
||||
[ 8,
|
||||
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 0, "" ],
|
||||
[ 8,
|
||||
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "T", 10, 0, "" ],
|
||||
[ 8, 0, 0,
|
||||
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 0, 8, "" ],
|
||||
);
|
||||
test("push flags + pull deletions", \@x01, \@X08, \@O08);
|
||||
test("push flags + pull deletions", \@x01, \@X08, @O08);
|
||||
|
||||
# size restriction tests
|
||||
|
||||
my @x10 = (
|
||||
[ 2,
|
||||
A, 1, "", B, 2, "*" ],
|
||||
[ 1,
|
||||
C, 1, "*" ],
|
||||
[ 0,
|
||||
1, 0, "", 2, 0, "*" ],
|
||||
[ 0,
|
||||
3, 0, "*" ],
|
||||
[ 0, 0, 0,
|
||||
],
|
||||
);
|
||||
|
||||
my @O11 = ("MaxSize 1k\n", "MaxSize 1k\n", "Expunge Near");
|
||||
my @O11 = ("MaxSize 1k\n", "MaxSize 1k\n", "");
|
||||
#show("10", "11", "11");
|
||||
my @X11 = (
|
||||
[ 3,
|
||||
A, 1, "", B, 2, "*", C, 3, "?" ],
|
||||
[ 3,
|
||||
C, 1, "*", A, 2, "", B, 3, "?" ],
|
||||
[ 3, 0, 3,
|
||||
3, 1, "<", 1, 2, "", 2, 3, ">" ],
|
||||
);
|
||||
test("max size", \@x10, \@X11, \@O11);
|
||||
|
||||
my @x22 = (
|
||||
[ 3,
|
||||
A, 1, "", B, 2, "*", C, 3, "?" ],
|
||||
[ 3,
|
||||
C, 1, "F*", A, 2, "", B, 3, "F?" ],
|
||||
[ 3, 0, 3,
|
||||
3, 1, "<", 1, 2, "", 2, 3, ">" ],
|
||||
);
|
||||
|
||||
#show("22", "22", "11");
|
||||
my @X22 = (
|
||||
[ 4,
|
||||
A, 1, "", B, 2, "*", C, 3, "T?", C, 4, "F*" ],
|
||||
[ 4,
|
||||
C, 1, "F*", A, 2, "", B, 4, "*" ],
|
||||
[ 4, 0, 4,
|
||||
4, 1, "F", 3, 0, "T", 1, 2, "", 2, 4, "" ],
|
||||
);
|
||||
test("max size + flagging", \@x22, \@X22, \@O11);
|
||||
|
||||
my @x23 = (
|
||||
[ 2,
|
||||
A, 1, "", B, 2, "F*" ],
|
||||
[ 1,
|
||||
C, 1, "F*" ],
|
||||
[ 0, 0, 0,
|
||||
],
|
||||
1, 1, "", 2, 2, "*" ],
|
||||
[ 2,
|
||||
3, 1, "*", 1, 2, "" ],
|
||||
[ 2, 0, 1,
|
||||
-1, 1, "", 1, 2, "", 2, -1, "" ],
|
||||
);
|
||||
test("max size", \@x10, \@X11, @O11);
|
||||
|
||||
my @X23 = (
|
||||
my @O22 = ("", "MaxSize 1k\n", "");
|
||||
#show("11", "22", "22");
|
||||
my @X22 = (
|
||||
[ 3,
|
||||
A, 1, "", B, 2, "F*", C, 3, "F*" ],
|
||||
[ 3,
|
||||
C, 1, "F*", A, 2, "", B, 3, "F*" ],
|
||||
[ 3, 0, 3,
|
||||
3, 1, "F", 1, 2, "", 2, 3, "F" ]
|
||||
1, 1, "", 2, 2, "*", 3, 3, "*" ],
|
||||
[ 2,
|
||||
3, 1, "*", 1, 2, "" ],
|
||||
[ 2, 0, 1,
|
||||
3, 1, "", 1, 2, "", 2, -1, "" ],
|
||||
);
|
||||
test("max size + initial flagging", \@x23, \@X23, \@O11);
|
||||
|
||||
my @x24 = (
|
||||
[ 3,
|
||||
A, 1, "", B, 2, "*", C, 3, "F*" ],
|
||||
[ 1,
|
||||
A, 1, "" ],
|
||||
[ 3, 0, 1,
|
||||
1, 1, "", 2, 0, "^", 3, 0, "^" ],
|
||||
);
|
||||
|
||||
my @X24 = (
|
||||
[ 3,
|
||||
A, 1, "", B, 2, "*", C, 3, "F*" ],
|
||||
[ 3,
|
||||
A, 1, "", B, 2, "?", C, 3, "F*" ],
|
||||
[ 3, 0, 3,
|
||||
1, 1, "", 2, 2, ">", 3, 3, "F" ],
|
||||
);
|
||||
test("max size (pre-1.4 legacy)", \@x24, \@X24, \@O11);
|
||||
test("slave max size", \@X11, \@X22, @O22);
|
||||
|
||||
# expiration tests
|
||||
|
||||
my @x30 = (
|
||||
[ 6,
|
||||
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
|
||||
[ 0,
|
||||
1, 0, "F", 2, 0, "", 3, 0, "S", 4, 0, "", 5, 0, "S", 6, 0, "" ],
|
||||
[ 0,
|
||||
],
|
||||
[ 0, 0, 0,
|
||||
|
@ -246,50 +184,52 @@ my @O31 = ("", "", "MaxMessages 3\n");
|
|||
#show("30", "31", "31");
|
||||
my @X31 = (
|
||||
[ 6,
|
||||
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
|
||||
1, 1, "F", 2, 2, "", 3, 3, "S", 4, 4, "", 5, 5, "S", 6, 6, "" ],
|
||||
[ 5,
|
||||
A, 1, "F", B, 2, "", D, 3, "", E, 4, "S", F, 5, "" ],
|
||||
[ 6, 3, 5,
|
||||
1, 1, "F", 2, 2, "", 4, 3, "", 5, 4, "S", 6, 5, "" ],
|
||||
[ 6, 2, 0,
|
||||
1, 1, "F", 2, 2, "", 4, 3, "", 5, 4, "S", 6, 5, "" ],
|
||||
);
|
||||
test("max messages", \@x30, \@X31, \@O31);
|
||||
test("max messages", \@x30, \@X31, @O31);
|
||||
|
||||
my @O32 = ("", "", "MaxMessages 3\nExpireUnread yes\n");
|
||||
#show("30", "32", "32");
|
||||
my @X32 = (
|
||||
[ 6,
|
||||
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
|
||||
1, 1, "F", 2, 2, "", 3, 3, "S", 4, 4, "", 5, 5, "S", 6, 6, "" ],
|
||||
[ 4,
|
||||
A, 1, "F", D, 2, "", E, 3, "S", F, 4, "" ],
|
||||
[ 6, 3, 4,
|
||||
1, 1, "F", 4, 2, "", 5, 3, "S", 6, 4, "" ],
|
||||
[ 6, 1, 0,
|
||||
1, 1, "F", 4, 2, "", 5, 3, "S", 6, 4, "" ],
|
||||
);
|
||||
test("max messages vs. unread", \@x30, \@X32, \@O32);
|
||||
test("max messages vs. unread", \@x30, \@X32, @O32);
|
||||
|
||||
my @x50 = (
|
||||
[ 6,
|
||||
A, 1, "FS", B, 2, "FS", C, 3, "S", D, 4, "", E, 5, "", F, 6, "" ],
|
||||
1, 1, "FS", 2, 2, "FS", 3, 3, "S", 4, 4, "", 5, 5, "", 6, 6, "" ],
|
||||
[ 6,
|
||||
A, 1, "S", B, 2, "ST", D, 4, "", E, 5, "", F, 6, "" ],
|
||||
1, 1, "S", 2, 2, "ST", 4, 4, "", 5, 5, "", 6, 6, "" ],
|
||||
[ 6, 3, 0,
|
||||
1, 1, "FS", 2, 2, "~S", 3, 3, "~S", 4, 4, "", 5, 5, "", 6, 6, "" ],
|
||||
1, 1, "FS", 2, 2, "XS", 3, 3, "XS", 4, 4, "", 5, 5, "", 6, 6, "" ],
|
||||
);
|
||||
|
||||
my @O51 = ("", "", "MaxMessages 3\nExpunge Both\n");
|
||||
#show("50", "51", "51");
|
||||
my @X51 = (
|
||||
[ 6,
|
||||
A, 1, "S", B, 2, "FS", C, 3, "S", D, 4, "", E, 5, "", F, 6, "" ],
|
||||
1, 1, "S", 2, 2, "FS", 3, 3, "S", 4, 4, "", 5, 5, "", 6, 6, "" ],
|
||||
[ 6,
|
||||
B, 2, "FS", D, 4, "", E, 5, "", F, 6, "" ],
|
||||
[ 6, 3, 6,
|
||||
2, 2, "FS", 4, 4, "", 5, 5, "", 6, 6, "" ],
|
||||
[ 6, 3, 0,
|
||||
2, 2, "FS", 4, 4, "", 5, 5, "", 6, 6, "" ],
|
||||
);
|
||||
test("max messages + expunge", \@x50, \@X51, \@O51);
|
||||
test("max messages + expunge", \@x50, \@X51, @O51);
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
chdir "..";
|
||||
rmdir "tmp";
|
||||
print "OK.\n";
|
||||
exit 0;
|
||||
|
||||
|
@ -304,66 +244,47 @@ sub qm($)
|
|||
return $_;
|
||||
}
|
||||
|
||||
# [ $far, $near, $channel ]
|
||||
sub writecfg($)
|
||||
# $master, $slave, $channel
|
||||
sub writecfg($$$)
|
||||
{
|
||||
my ($sfx) = @_;
|
||||
|
||||
open(FILE, ">", ".mbsyncrc") or
|
||||
die "Cannot open .mbsyncrc.\n";
|
||||
print FILE
|
||||
"FSync no
|
||||
|
||||
MaildirStore far
|
||||
MaildirStore master
|
||||
Path ./
|
||||
Inbox ./far
|
||||
".$$sfx[0]."
|
||||
MaildirStore near
|
||||
Inbox ./master
|
||||
".shift()."
|
||||
MaildirStore slave
|
||||
Path ./
|
||||
Inbox ./near
|
||||
".$$sfx[1]."
|
||||
Inbox ./slave
|
||||
".shift()."
|
||||
Channel test
|
||||
Far :far:
|
||||
Near :near:
|
||||
Master :master:
|
||||
Slave :slave:
|
||||
SyncState *
|
||||
".$$sfx[2];
|
||||
".shift();
|
||||
close FILE;
|
||||
}
|
||||
|
||||
sub killcfg()
|
||||
{
|
||||
unlink $_ for (glob("*.log"));
|
||||
unlink ".mbsyncrc";
|
||||
}
|
||||
|
||||
# $run_async, $mbsync_options, $log_file
|
||||
# Return: $exit_code, \@mbsync_output
|
||||
sub runsync($$$)
|
||||
# $options
|
||||
sub runsync($)
|
||||
{
|
||||
my ($async, $flags, $file) = @_;
|
||||
|
||||
my $cmd;
|
||||
if ($use_vg) {
|
||||
$cmd = "valgrind -q --error-exitcode=1 ";
|
||||
} else {
|
||||
$flags .= " -D";
|
||||
}
|
||||
$flags .= " -Ta" if ($async);
|
||||
$cmd .= "$mbsync -Tz $flags -c .mbsyncrc test";
|
||||
open FILE, "$cmd 2>&1 |";
|
||||
# open FILE, "valgrind -q --log-fd=3 ../mbsync ".shift()." -c .mbsyncrc test 3>&2 2>&1 |";
|
||||
open FILE, "../mbsync -D -Z ".shift()." -c .mbsyncrc test 2>&1 |";
|
||||
my @out = <FILE>;
|
||||
close FILE or push(@out, $! ? "*** error closing mbsync: $!\n" : "*** mbsync exited with signal ".($?&127).", code ".($?>>8)."\n");
|
||||
if ($file) {
|
||||
open FILE, ">$file" or die("Cannot create $file: $!\n");
|
||||
print FILE @out;
|
||||
close FILE;
|
||||
}
|
||||
return $?, \@out;
|
||||
return $?, @out;
|
||||
}
|
||||
|
||||
|
||||
# $path
|
||||
# Return: $max_uid, { uid => [ seq, flags ] }
|
||||
sub readbox($)
|
||||
{
|
||||
my $bn = shift;
|
||||
|
@ -380,9 +301,11 @@ sub readbox($)
|
|||
for my $d ("cur", "new") {
|
||||
opendir(DIR, $bn."/".$d) or next;
|
||||
for my $f (grep(!/^\.\.?$/, readdir(DIR))) {
|
||||
my ($uid, $flg, $ph, $num);
|
||||
my ($uid, $flg, $num);
|
||||
if ($f =~ /^\d+\.\d+_\d+\.[-[:alnum:]]+,U=(\d+):2,(.*)$/) {
|
||||
($uid, $flg) = ($1, $2);
|
||||
} elsif ($f =~ /^\d+\.\d+_(\d+)\.[-[:alnum:]]+:2,(.*)$/) {
|
||||
($uid, $flg) = (0, $2);
|
||||
} else {
|
||||
print STDERR "unrecognided file name '$f' in '$bn'.\n";
|
||||
exit 1;
|
||||
|
@ -390,7 +313,7 @@ sub readbox($)
|
|||
open(FILE, "<", $bn."/".$d."/".$f) or die "Cannot read message '$f' in '$bn'.\n";
|
||||
my $sz = 0;
|
||||
while (<FILE>) {
|
||||
/^Subject: (\[placeholder\] )?(\d+)$/ && ($ph = defined($1), $num = $2);
|
||||
/^Subject: (\d+)$/ && ($num = $1);
|
||||
$sz += length($_);
|
||||
}
|
||||
close FILE;
|
||||
|
@ -398,10 +321,10 @@ sub readbox($)
|
|||
print STDERR "message '$f' in '$bn' has no identifier.\n";
|
||||
exit 1;
|
||||
}
|
||||
@{ $ms{$uid} } = ($num, $flg.($sz>1000?"*":"").($ph?"?":""));
|
||||
@{ $ms{$num} } = ($uid, $flg.($sz>1000?"*":""));
|
||||
}
|
||||
}
|
||||
return $mu, \%ms;
|
||||
return ($mu, %ms);
|
||||
}
|
||||
|
||||
# $boxname
|
||||
|
@ -412,18 +335,24 @@ sub showbox($)
|
|||
{
|
||||
my ($bn) = @_;
|
||||
|
||||
my ($mu, $ms) = readbox($bn);
|
||||
my @bc = ($mu);
|
||||
for my $uid (sort { $a <=> $b } keys %$ms) {
|
||||
push @bc, $$ms{$uid}[0], $uid, $$ms{$uid}[1];
|
||||
my ($mu, %ms) = readbox($bn);
|
||||
print " [ $mu,\n ";
|
||||
my $frst = 1;
|
||||
for my $num (sort { $a <=> $b } keys %ms) {
|
||||
if ($frst) {
|
||||
$frst = 0;
|
||||
} else {
|
||||
print ", ";
|
||||
}
|
||||
print "$num, $ms{$num}[0], \"$ms{$num}[1]\"";
|
||||
}
|
||||
printbox(\@bc);
|
||||
print " ],\n";
|
||||
}
|
||||
|
||||
# $filename
|
||||
# Output:
|
||||
# [ maxuid[F], maxxfuid, maxuid[N],
|
||||
# uid[F], uid[N], "flags", ... ],
|
||||
# [ maxuid[M], smaxxuid, maxuid[S],
|
||||
# uid[M], uid[S], "flags", ... ],
|
||||
sub showstate($)
|
||||
{
|
||||
my ($fn) = @_;
|
||||
|
@ -450,14 +379,22 @@ sub showstate($)
|
|||
close FILE;
|
||||
return;
|
||||
}
|
||||
my @T = ($hdr{'MaxPulledUid'} // "missing",
|
||||
$hdr{'MaxExpiredFarUid'} // "0",
|
||||
$hdr{'MaxPushedUid'} // "missing");
|
||||
print " [ ".($hdr{'MaxPulledUid'} // "missing").", ".
|
||||
($hdr{'MaxExpiredSlaveUid'} // "0").", ".($hdr{'MaxPushedUid'} // "missing").",\n ";
|
||||
my $frst = 1;
|
||||
for (@ls) {
|
||||
/^(\d+) (\d+) (.*)$/;
|
||||
push @T, $1, $2, $3;
|
||||
if ($frst) {
|
||||
$frst = 0;
|
||||
} else {
|
||||
print ", ";
|
||||
}
|
||||
if (!/^(-?\d+) (-?\d+) (.*)$/) {
|
||||
print "??, ??, \"??\"";
|
||||
} else {
|
||||
print "$1, $2, \"$3\"";
|
||||
}
|
||||
}
|
||||
printstate(\@T);
|
||||
print " ],\n";
|
||||
}
|
||||
|
||||
# $filename
|
||||
|
@ -465,8 +402,8 @@ sub showchan($)
|
|||
{
|
||||
my ($fn) = @_;
|
||||
|
||||
showbox("far");
|
||||
showbox("near");
|
||||
showbox("master");
|
||||
showbox("slave");
|
||||
showstate($fn);
|
||||
}
|
||||
|
||||
|
@ -474,105 +411,109 @@ sub showchan($)
|
|||
sub show($$$)
|
||||
{
|
||||
my ($sx, $tx, $sfxn) = @_;
|
||||
my ($sp, $sfx);
|
||||
eval "\$sp = \\\@x$sx";
|
||||
eval "\$sfx = \\\@O$sfxn";
|
||||
mkchan($$sp[0], $$sp[1], $$sp[2]);
|
||||
my (@sp, @sfx);
|
||||
eval "\@sp = \@x$sx";
|
||||
eval "\@sfx = \@O$sfxn";
|
||||
mkchan($sp[0], $sp[1], @{ $sp[2] });
|
||||
print "my \@x$sx = (\n";
|
||||
showchan("near/.mbsyncstate");
|
||||
showchan("slave/.mbsyncstate");
|
||||
print ");\n";
|
||||
writecfg($sfx);
|
||||
runsync(0, "", "");
|
||||
&writecfg(@sfx);
|
||||
runsync("");
|
||||
killcfg();
|
||||
print "my \@X$tx = (\n";
|
||||
showchan("near/.mbsyncstate");
|
||||
showchan("slave/.mbsyncstate");
|
||||
print ");\n";
|
||||
print "test(\"\", \\\@x$sx, \\\@X$tx, \\\@O$sfxn);\n\n";
|
||||
rmtree "near";
|
||||
rmtree "far";
|
||||
print "test(\"\", \\\@x$sx, \\\@X$tx, \@O$sfxn);\n\n";
|
||||
rmtree "slave";
|
||||
rmtree "master";
|
||||
}
|
||||
|
||||
# $box_name, \@box_state
|
||||
sub mkbox($$)
|
||||
# $boxname, $maxuid, @msgs
|
||||
sub mkbox($$@)
|
||||
{
|
||||
my ($bn, $bs) = @_;
|
||||
my ($bn, $mu, @ms) = @_;
|
||||
|
||||
rmtree($bn);
|
||||
(mkdir($bn) and mkdir($bn."/tmp") and mkdir($bn."/new") and mkdir($bn."/cur")) or
|
||||
die "Cannot create mailbox $bn.\n";
|
||||
open(FILE, ">", $bn."/.uidvalidity") or die "Cannot create UID validity for mailbox $bn.\n";
|
||||
print FILE "1\n$$bs[0]\n";
|
||||
print FILE "1\n$mu\n";
|
||||
close FILE;
|
||||
for (my $i = 1; $i < @$bs; $i += 3) {
|
||||
my ($num, $uid, $flg) = ($$bs[$i], $$bs[$i + 1], $$bs[$i + 2]);
|
||||
while (@ms) {
|
||||
my ($num, $uid, $flg) = (shift @ms, shift @ms, shift @ms);
|
||||
if ($uid) {
|
||||
$uid = ",U=".$uid;
|
||||
} else {
|
||||
$uid = "";
|
||||
}
|
||||
my $big = $flg =~ s/\*//;
|
||||
my $ph = $flg =~ s/\?//;
|
||||
open(FILE, ">", $bn."/".($flg =~ /S/ ? "cur" : "new")."/0.1_".$num.".local,U=".$uid.":2,".$flg) or
|
||||
die "Cannot create message ".mn($num)." in mailbox $bn.\n";
|
||||
print FILE "From: foo\nTo: bar\nDate: Thu, 1 Jan 1970 00:00:00 +0000\nSubject: ".($ph?"[placeholder] ":"").$num."\n\n".(("A"x50)."\n")x($big*30);
|
||||
open(FILE, ">", $bn."/".($flg =~ /S/ ? "cur" : "new")."/0.1_".$num.".local".$uid.":2,".$flg) or
|
||||
die "Cannot create message $num in mailbox $bn.\n";
|
||||
print FILE "From: foo\nTo: bar\nDate: Thu, 1 Jan 1970 00:00:00 +0000\nSubject: $num\n\n".(("A"x50)."\n")x($big*30);
|
||||
close FILE;
|
||||
}
|
||||
}
|
||||
|
||||
# \@far_state, \@near_state, \@sync_state
|
||||
sub mkchan($$$)
|
||||
# \@master, \@slave, @syncstate
|
||||
sub mkchan($$@)
|
||||
{
|
||||
my ($f, $n, $t) = @_;
|
||||
mkbox("far", $f);
|
||||
mkbox("near", $n);
|
||||
open(FILE, ">", "near/.mbsyncstate") or
|
||||
my ($m, $s, @t) = @_;
|
||||
&mkbox("master", @{ $m });
|
||||
&mkbox("slave", @{ $s });
|
||||
open(FILE, ">", "slave/.mbsyncstate") or
|
||||
die "Cannot create sync state.\n";
|
||||
print FILE "FarUidValidity 1\nMaxPulledUid ".$$t[0]."\n".
|
||||
"NearUidValidity 1\nMaxExpiredFarUid ".$$t[1]."\nMaxPushedUid ".$$t[2]."\n\n";
|
||||
for (my $i = 3; $i < @$t; $i += 3) {
|
||||
print FILE $$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2]."\n";
|
||||
print FILE "MasterUidValidity 1\nMaxPulledUid ".shift(@t)."\n".
|
||||
"SlaveUidValidity 1\nMaxExpiredSlaveUid ".shift(@t)."\nMaxPushedUid ".shift(@t)."\n\n";
|
||||
while (@t) {
|
||||
print FILE shift(@t)." ".shift(@t)." ".shift(@t)."\n";
|
||||
}
|
||||
close FILE;
|
||||
}
|
||||
|
||||
# $box_name, \@box_state
|
||||
sub ckbox($$)
|
||||
# $config, $boxname, $maxuid, @msgs
|
||||
sub ckbox($$$@)
|
||||
{
|
||||
my ($bn, $bs) = @_;
|
||||
my ($bn, $MU, @MS) = @_;
|
||||
|
||||
my ($mu, $ms) = readbox($bn);
|
||||
if ($mu != $$bs[0]) {
|
||||
print STDERR "MAXUID mismatch for '$bn' (got $mu, wanted $$bs[0]).\n";
|
||||
my ($mu, %ms) = readbox($bn);
|
||||
if ($mu != $MU) {
|
||||
print STDERR "MAXUID mismatch for '$bn' (got $mu, wanted $MU).\n";
|
||||
return 1;
|
||||
}
|
||||
for (my $i = 1; $i < @$bs; $i += 3) {
|
||||
my ($num, $uid, $flg) = ($$bs[$i], $$bs[$i + 1], $$bs[$i + 2]);
|
||||
my $m = delete $$ms{$uid};
|
||||
if (!defined $m) {
|
||||
print STDERR "No message $bn:$uid.\n";
|
||||
while (@MS) {
|
||||
my ($num, $uid, $flg) = (shift @MS, shift @MS, shift @MS);
|
||||
if (!defined $ms{$num}) {
|
||||
print STDERR "No message $bn:$num.\n";
|
||||
return 1;
|
||||
}
|
||||
if ($$m[0] ne $num) {
|
||||
print STDERR "Subject mismatch for $bn:$uid.\n";
|
||||
if ($ms{$num}[0] ne $uid) {
|
||||
print STDERR "UID mismatch for $bn:$num.\n";
|
||||
return 1;
|
||||
}
|
||||
if ($$m[1] ne $flg) {
|
||||
print STDERR "Flag mismatch for $bn:$uid.\n";
|
||||
if ($ms{$num}[1] ne $flg) {
|
||||
print STDERR "Flag mismatch for $bn:$num.\n";
|
||||
return 1;
|
||||
}
|
||||
delete $ms{$num};
|
||||
}
|
||||
if (%$ms) {
|
||||
print STDERR "Excess messages in '$bn': ".join(", ", sort({ $a <=> $b } keys(%$ms))).".\n";
|
||||
if (%ms) {
|
||||
print STDERR "Excess messages in '$bn': ".join(", ", sort({$a <=> $b } keys(%ms))).".\n";
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# $state_file, \@sync_state
|
||||
sub ckstate($$)
|
||||
# $filename, @syncstate
|
||||
sub ckstate($@)
|
||||
{
|
||||
my ($fn, $t) = @_;
|
||||
my ($fn, $mmaxuid, $smaxxuid, $smaxuid, @T) = @_;
|
||||
my %hdr;
|
||||
$hdr{'FarUidValidity'} = "1";
|
||||
$hdr{'NearUidValidity'} = "1";
|
||||
$hdr{'MaxPulledUid'} = $$t[0];
|
||||
$hdr{'MaxPushedUid'} = $$t[2];
|
||||
$hdr{'MaxExpiredFarUid'} = $$t[1] if ($$t[1] ne 0);
|
||||
$hdr{'MasterUidValidity'} = "1";
|
||||
$hdr{'SlaveUidValidity'} = "1";
|
||||
$hdr{'MaxPulledUid'} = $mmaxuid;
|
||||
$hdr{'MaxPushedUid'} = $smaxuid;
|
||||
$hdr{'MaxExpiredSlaveUid'} = $smaxxuid if ($smaxxuid ne 0);
|
||||
open(FILE, "<", $fn) or die "Cannot read sync state $fn.\n";
|
||||
chomp(my @ls = <FILE>);
|
||||
close FILE;
|
||||
|
@ -602,68 +543,65 @@ sub ckstate($$)
|
|||
print STDERR "Keys missing from sync state header: @ky\n";
|
||||
return 1;
|
||||
}
|
||||
my $i = 3;
|
||||
for my $l (@ls) {
|
||||
if ($i == @$t) {
|
||||
if (!@T) {
|
||||
print STDERR "Excess sync state entry: '$l'.\n";
|
||||
return 1;
|
||||
}
|
||||
my $xl = $$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2];
|
||||
my $xl = shift(@T)." ".shift(@T)." ".shift(@T);
|
||||
if ($l ne $xl) {
|
||||
print STDERR "Sync state entry mismatch: '$l' instead of '$xl'.\n";
|
||||
return 1;
|
||||
}
|
||||
$i += 3;
|
||||
}
|
||||
if ($i < @$t) {
|
||||
print STDERR "Missing sync state entry: '".$$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2]."'.\n";
|
||||
if (@T) {
|
||||
print STDERR "Missing sync state entry: '".shift(@T)." ".shift(@T)." ".shift(@T)."'.\n";
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# $state_file, \@chan_state
|
||||
# $statefile, \@chan_state
|
||||
sub ckchan($$)
|
||||
{
|
||||
my ($fn, $cs) = @_;
|
||||
my $rslt = ckstate($fn, $$cs[2]);
|
||||
$rslt |= ckbox("far", $$cs[0]);
|
||||
$rslt |= ckbox("near", $$cs[1]);
|
||||
my ($F, $cs) = @_;
|
||||
my $rslt = ckstate($F, @{ $$cs[2] });
|
||||
$rslt |= &ckbox("master", @{ $$cs[0] });
|
||||
$rslt |= &ckbox("slave", @{ $$cs[1] });
|
||||
return $rslt;
|
||||
}
|
||||
|
||||
# \@box_state
|
||||
sub printbox($)
|
||||
sub printbox($$@)
|
||||
{
|
||||
my ($bs) = @_;
|
||||
my ($bn, $mu, @ms) = @_;
|
||||
|
||||
print " [ $$bs[0],\n ";
|
||||
print " [ $mu,\n ";
|
||||
my $frst = 1;
|
||||
for (my $i = 1; $i < @$bs; $i += 3) {
|
||||
while (@ms) {
|
||||
if ($frst) {
|
||||
$frst = 0;
|
||||
} else {
|
||||
print ", ";
|
||||
}
|
||||
print mn($$bs[$i]).", ".$$bs[$i + 1].", \"".$$bs[$i + 2]."\"";
|
||||
print shift(@ms).", ".shift(@ms).", \"".shift(@ms)."\"";
|
||||
}
|
||||
print " ],\n";
|
||||
}
|
||||
|
||||
# \@sync_state
|
||||
sub printstate($)
|
||||
# @syncstate
|
||||
sub printstate(@)
|
||||
{
|
||||
my ($t) = @_;
|
||||
my (@t) = @_;
|
||||
|
||||
print " [ ".$$t[0].", ".$$t[1].", ".$$t[2].",\n ";
|
||||
print " [ ".shift(@t).", ".shift(@t).", ".shift(@t).",\n ";
|
||||
my $frst = 1;
|
||||
for (my $i = 3; $i < @$t; $i += 3) {
|
||||
while (@t) {
|
||||
if ($frst) {
|
||||
$frst = 0;
|
||||
} else {
|
||||
print ", ";
|
||||
}
|
||||
print(($$t[$i] // "??").", ".($$t[$i + 1] // "??").", \"".($$t[$i + 2] // "??")."\"");
|
||||
print shift(@t).", ".shift(@t).", \"".shift(@t)."\"";
|
||||
}
|
||||
print " ],\n";
|
||||
}
|
||||
|
@ -673,134 +611,103 @@ sub printchan($)
|
|||
{
|
||||
my ($cs) = @_;
|
||||
|
||||
printbox($$cs[0]);
|
||||
printbox($$cs[1]);
|
||||
printstate($$cs[2]);
|
||||
&printbox("master", @{ $$cs[0] });
|
||||
&printbox("slave", @{ $$cs[1] });
|
||||
printstate(@{ $$cs[2] });
|
||||
}
|
||||
|
||||
sub readfile($)
|
||||
# $title, \@source_state, \@target_state, @channel_configs
|
||||
sub test($$$@)
|
||||
{
|
||||
my ($file) = @_;
|
||||
my ($ttl, $sx, $tx, @sfx) = @_;
|
||||
|
||||
open(FILE, $file) or return;
|
||||
my @nj = <FILE>;
|
||||
close FILE;
|
||||
return \@nj;
|
||||
}
|
||||
return 0 if (scalar(@ARGV) && !grep { $_ eq $ttl } @ARGV);
|
||||
print "Testing: ".$ttl." ...\n";
|
||||
mkchan($$sx[0], $$sx[1], @{ $$sx[2] });
|
||||
&writecfg(@sfx);
|
||||
|
||||
# $run_async, \@source_state, \@target_state, \@channel_configs
|
||||
sub test_impl($$$$)
|
||||
{
|
||||
my ($async, $sx, $tx, $sfx) = @_;
|
||||
|
||||
mkchan($$sx[0], $$sx[1], $$sx[2]);
|
||||
|
||||
my ($xc, $ret) = runsync($async, "-Tj", "1-initial.log");
|
||||
if ($xc || ckchan("near/.mbsyncstate.new", $tx)) {
|
||||
my ($xc, @ret) = runsync("-J");
|
||||
if ($xc) {
|
||||
print "Input:\n";
|
||||
printchan($sx);
|
||||
print "Options:\n";
|
||||
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n";
|
||||
if (!$xc) {
|
||||
print "Expected result:\n";
|
||||
printchan($tx);
|
||||
print "Actual result:\n";
|
||||
showchan("near/.mbsyncstate.new");
|
||||
}
|
||||
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
|
||||
print "Expected result:\n";
|
||||
printchan($tx);
|
||||
print "Debug output:\n";
|
||||
print @$ret;
|
||||
print @ret;
|
||||
exit 1;
|
||||
}
|
||||
if (ckchan("slave/.mbsyncstate.new", $tx)) {
|
||||
print "Input:\n";
|
||||
printchan($sx);
|
||||
print "Options:\n";
|
||||
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
|
||||
print "Expected result:\n";
|
||||
printchan($tx);
|
||||
print "Actual result:\n";
|
||||
showchan("slave/.mbsyncstate.new");
|
||||
print "Debug output:\n";
|
||||
print @ret;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $nj = readfile("near/.mbsyncstate.journal");
|
||||
my ($jxc, $jret) = runsync($async, "-0 --no-expunge", "2-replay.log");
|
||||
if ($jxc || ckstate("near/.mbsyncstate", $$tx[2])) {
|
||||
open(FILE, "<", "slave/.mbsyncstate.journal") or
|
||||
die "Cannot read journal.\n";
|
||||
my @nj = <FILE>;
|
||||
close FILE;
|
||||
($xc, @ret) = runsync("-0 --no-expunge");
|
||||
if ($xc) {
|
||||
print "Journal replay failed.\n";
|
||||
print "Input == Expected result:\n";
|
||||
printchan($tx);
|
||||
print "Options:\n";
|
||||
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ], [ \"-0\", \"--no-expunge\" ]\n";
|
||||
print "Debug output:\n";
|
||||
print @ret;
|
||||
exit 1;
|
||||
}
|
||||
if (ckstate("slave/.mbsyncstate", @{ $$tx[2] })) {
|
||||
print "Journal replay failed.\n";
|
||||
print "Options:\n";
|
||||
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ], [ \"-0\", \"--no-expunge\" ]\n";
|
||||
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
|
||||
print "Old State:\n";
|
||||
printstate($$sx[2]);
|
||||
print "Journal:\n".join("", @$nj)."\n";
|
||||
if (!$jxc) {
|
||||
print "Expected New State:\n";
|
||||
printstate($$tx[2]);
|
||||
print "New State:\n";
|
||||
showstate("near/.mbsyncstate");
|
||||
}
|
||||
printstate(@{ $$sx[2] });
|
||||
print "Journal:\n".join("", @nj)."\n";
|
||||
print "Expected New State:\n";
|
||||
printstate(@{ $$tx[2] });
|
||||
print "New State:\n";
|
||||
showstate("slave/.mbsyncstate");
|
||||
print "Debug output:\n";
|
||||
print @$jret;
|
||||
print @ret;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my ($ixc, $iret) = runsync($async, "", "3-verify.log");
|
||||
if ($ixc || ckchan("near/.mbsyncstate", $tx)) {
|
||||
($xc, @ret) = runsync("");
|
||||
if ($xc) {
|
||||
print "Idempotence verification run failed.\n";
|
||||
print "Input == Expected result:\n";
|
||||
printchan($tx);
|
||||
print "Options:\n";
|
||||
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n";
|
||||
if (!$ixc) {
|
||||
print "Actual result:\n";
|
||||
showchan("near/.mbsyncstate");
|
||||
}
|
||||
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
|
||||
print "Debug output:\n";
|
||||
print @$iret;
|
||||
print @ret;
|
||||
exit 1;
|
||||
}
|
||||
if (ckchan("slave/.mbsyncstate", $tx)) {
|
||||
print "Idempotence verification run failed.\n";
|
||||
print "Input == Expected result:\n";
|
||||
printchan($tx);
|
||||
print "Options:\n";
|
||||
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
|
||||
print "Actual result:\n";
|
||||
showchan("slave/.mbsyncstate");
|
||||
print "Debug output:\n";
|
||||
print @ret;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
rmtree "near";
|
||||
rmtree "far";
|
||||
|
||||
my $njl = (@$nj - 1) * 2;
|
||||
for (my $l = 1; $l <= $njl; $l++) {
|
||||
mkchan($$sx[0], $$sx[1], $$sx[2]);
|
||||
|
||||
my ($nxc, $nret) = runsync($async, "-Tj$l", "4-interrupt.log");
|
||||
if ($nxc != (100 + ($l & 1)) << 8) {
|
||||
print "Interrupting at step $l/$njl failed.\n";
|
||||
print "Debug output:\n";
|
||||
print @$nret;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
($nxc, $nret) = runsync($async, "-Tj", "5-resume.log");
|
||||
if ($nxc || ckchan("near/.mbsyncstate.new", $tx)) {
|
||||
print "Resuming from step $l/$njl failed.\n";
|
||||
print "Input:\n";
|
||||
printchan($sx);
|
||||
print "Options:\n";
|
||||
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n";
|
||||
my $nnj = readfile("near/.mbsyncstate.journal");
|
||||
my $ln = int($l / 2);
|
||||
print "Journal:\n".join("", @$nnj[0..$ln])."-------\n".join("", @$nnj[($ln + 1)..$#$nnj])."\n";
|
||||
print "Full journal:\n".join("", @$nj)."\n";
|
||||
if (!$nxc) {
|
||||
print "Expected result:\n";
|
||||
printchan($tx);
|
||||
print "Actual result:\n";
|
||||
showchan("near/.mbsyncstate.new");
|
||||
}
|
||||
print "Debug output:\n";
|
||||
print @$nret;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
rmtree "near";
|
||||
rmtree "far";
|
||||
}
|
||||
}
|
||||
|
||||
# $title, \@source_state, \@target_state, \@channel_configs
|
||||
sub test($$$$)
|
||||
{
|
||||
my ($ttl, $sx, $tx, $sfx) = @_;
|
||||
|
||||
return 0 if (scalar(@ARGV) && !grep { $_ eq $ttl } @ARGV);
|
||||
print "Testing: ".$ttl." ...\n";
|
||||
writecfg($sfx);
|
||||
|
||||
test_impl(0, $sx, $tx, $sfx);
|
||||
test_impl(1, $sx, $tx, $sfx);
|
||||
|
||||
killcfg();
|
||||
rmtree "slave";
|
||||
rmtree "master";
|
||||
}
|
||||
|
|
360
src/socket.c
360
src/socket.c
|
@ -40,8 +40,7 @@
|
|||
# include <openssl/ssl.h>
|
||||
# include <openssl/err.h>
|
||||
# include <openssl/x509v3.h>
|
||||
# if OPENSSL_VERSION_NUMBER < 0x10100000L \
|
||||
|| (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070100fL)
|
||||
# if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
# define X509_OBJECT_get0_X509(o) ((o)->data.x509)
|
||||
# define X509_STORE_get0_objects(o) ((o)->objs)
|
||||
# endif
|
||||
|
@ -63,34 +62,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 +72,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 +165,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 +173,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, X509_OBJECT_get0_X509( sk_X509_OBJECT_value( trusted, i ) ) ))
|
||||
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 );
|
||||
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 +218,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( X509_STORE_get0_objects( SSL_CTX_get_cert_store( mconf->SSLContext ) ) );
|
||||
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 +247,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 +260,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 +282,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 );
|
||||
}
|
||||
|
@ -432,7 +335,6 @@ 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 * );
|
||||
|
@ -447,7 +349,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 +356,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 +409,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 +423,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 +436,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 +462,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 );
|
||||
|
@ -608,7 +494,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;
|
||||
|
@ -622,8 +507,12 @@ socket_connect_next( conn_t *conn )
|
|||
{
|
||||
sys_error( "Cannot connect to %s", conn->name );
|
||||
free( conn->name );
|
||||
conn->name = NULL;
|
||||
conn->name = 0;
|
||||
#ifdef HAVE_IPV6
|
||||
conn->curr_addr = conn->curr_addr->ai_next;
|
||||
#else
|
||||
conn->curr_addr++;
|
||||
#endif
|
||||
socket_connect_one( conn );
|
||||
}
|
||||
|
||||
|
@ -637,31 +526,25 @@ socket_connect_failed( conn_t *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 +555,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 +568,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 +595,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 +628,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;
|
||||
|
@ -765,7 +648,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 +666,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 +698,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 +717,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 +730,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 +748,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 +773,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 +788,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 +796,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,7 +812,7 @@ 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 {
|
||||
|
@ -957,10 +833,10 @@ do_flush( conn_t *conn )
|
|||
error( "Fatal: Compression error: %s\n", z_err_msg( ret, conn->out_z ) );
|
||||
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 +849,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 +859,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++)
|
||||
|
@ -1028,7 +903,7 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
|
|||
error( "Fatal: Compression error: %s\n", z_err_msg( ret, conn->out_z ) );
|
||||
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 +926,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 +966,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 +993,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 )
|
||||
|
|
47
src/socket.h
47
src/socket.h
|
@ -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
1783
src/sync.c
File diff suppressed because it is too large
Load diff
14
src/sync.h
14
src/sync.h
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
202
src/util.c
202
src/util.c
|
@ -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;
|
||||
|
@ -529,7 +522,15 @@ map_name( const char *arg, char **result, uint reserve, const char *in, const ch
|
|||
for (ll = 0; ll < inl; ll++)
|
||||
if (arg[i + ll] != in[ll])
|
||||
goto rnexti;
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic push
|
||||
/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=42145 */
|
||||
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
memcpy( p, out, outl );
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
p += outl;
|
||||
i += inl;
|
||||
continue;
|
||||
|
@ -541,35 +542,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 +575,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 +607,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 +641,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 +658,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 +678,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 = ¬ifiers; *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 +712,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 +732,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 +751,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 +765,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 +773,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 +785,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 +808,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 +824,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;
|
||||
|
|
Loading…
Add table
Reference in a new issue