Compare commits

..

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

64 changed files with 7510 additions and 14084 deletions

22
.cvsignore Normal file
View file

@ -0,0 +1,22 @@
.autoconf_trace
Makefile
Makefile.in
autom4te.cache
aclocal.m4
build-stamp
config.h
config.h.in
config.cache
config.guess
config.log
config.status
config.sub
configure
configure.lineno
configure-stamp
isync.spec
isync-*.tar.gz
patch-stamp
stamp-h
stamp-h.in
stamp-h1

32
.gitignore vendored
View file

@ -1,32 +0,0 @@
/.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
Makefile
Makefile.in

View file

@ -1,3 +1,6 @@
Michael Elkins <me@mutt.org>
* Author
Oswald Buddenhagen <ossi@users.sf.net>
* Contributor, current maintainer
@ -7,9 +10,6 @@ Theodore Ts'o <tytso@mit.edu>
Nicolas Boullis <nboullis@debian.org>
* Debian package maintainer and minor upstream contributions
Michael Elkins <me@mutt.org>
* Original author
Send questions and bug reports to the isync-devel@lists.sourceforge.net
mailing list.

1445
ChangeLog Normal file

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,80 +1,28 @@
SUBDIRS = src
bin_SCRIPTS = mbsync-get-cert
bin_SCRIPTS = get-cert
EXTRA_DIST = debian isync.spec $(bin_SCRIPTS)
LOG_PL = \
use POSIX qw(strftime); \
use Date::Parse; \
use Text::Wrap; \
$$Text::Wrap::columns = 72; \
while (defined($$_ = <>)) { \
/^commit / or die "commit missing: $$_"; \
<> =~ /^log size (\d+)$$/ or die "wrong size"; \
$$len = $$1; \
read(STDIN, $$log, $$len) == $$len or die "unexpected EOF"; \
$$log =~ s/^Author: ([^>]+>)\nDate: (\d{4}-\d\d-\d\d \d\d:\d\d:\d\d [-+]\d{4})\n(.*)$$/$$3/s or die "unexpected log format"; \
$$author = $$1; $$date = str2time($$2); \
scalar(<>); \
@files = (); \
$$pfx = ""; \
while (defined($$l = <>) and $$l ne "\n") { \
chomp $$l; \
next if ($$l =~ m,^(ChangeLog$$|NEWS$$|TODO$$|debian/),); \
if (!@files) { \
$$pfx = $$l; \
$$pfx =~ s,/?[^/]+$$,,; \
} else { \
while (length($$pfx)) { \
$$l =~ m,^\Q$$pfx/\E, and last; \
$$pfx =~ s,/?[^/]+$$,,; \
} \
} \
push @files, $$l; \
} \
next if (!@files); \
print strftime("%F %H:%M", gmtime($$date))." ".$$author."\n\n"; \
if (@files > 1 and ($$len = length($$pfx))) { \
@efiles = (); \
for $$f (@files) { push @efiles, substr($$f, $$len + 1); } \
$$fstr = $$pfx."/: "; \
} else { \
@efiles = @files; \
$$fstr = ""; \
} \
print wrap("\t* ", "\t ", $$fstr.join(", ", @efiles).":")."\n"; \
$$log =~ s, +$$,,gm; \
$$log =~ s,^ ,\t,gm; \
print $$log."\n"; \
}
$(srcdir)/.git/index:
$(srcdir)/ChangeLog: $(srcdir)/.git/index
$(MAKE) log
log:
@test -z "$(srcdir)" || cd $(srcdir) && \
( ! test -d .git || \
git log --pretty=medium --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
@perl -p -e "s/^(\\S+)\\s+(\\S.+\\S)\\s+(\\S+)\\s*\$$/\$$1:'\$$2 <\$$3>'\\n/" < ../CVSROOT/accounts > .usermap
cvs2cl -U .usermap --no-wrap --separate-header -I ChangeLog -I NEWS -I TODO -I debian/
@rm -f .usermap ChangeLog.bak
deb:
CFLAGS= INSTALL= dpkg-buildpackage -b --no-sign
CFLAGS="-O2 -mcpu=i686" fakeroot debian/rules binary
deb-clean:
dh_clean -Xsrc/
fakeroot debian/rules unpatch
distdir distclean: deb-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
find $(distdir)/debian \( -name CVS -o -name .cvsignore -o -name .#\*# -o -type l \) -print0 | xargs -0r rm -rf
dist-sign: dist
gpg -b -a $(PACKAGE)-$(VERSION).tar.gz
rpm: dist
CFLAGS="-O2 -mtune=core2" rpmbuild --clean -ta $(PACKAGE)-$(VERSION).tar.gz
rpm-ia32: dist
CFLAGS="-O2 -m32 -march=i686" rpmbuild --target i686-unknown-linux --clean -ta $(PACKAGE)-$(VERSION).tar.gz
rpm:
make dist
cp $(PACKAGE)-$(VERSION).tar.gz /usr/src/rpm/SOURCES
CFLAGS="-O2 -mcpu=i686" rpm -ba --clean isync.spec
docdir = $(datadir)/doc/isync
doc_DATA = README TODO NEWS ChangeLog AUTHORS

78
NEWS
View file

@ -1,81 +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.
An IMAP Path/NAMESPACE rooted in INBOX won't be handled specially any more.
This means that some Patterns may need adjustment.
The default output is a lot less verbose now.
The meanings of the -V and -D options changed significantly.
The SSL/TLS configuration has been re-designed.
SSL is now explicitly enabled or disabled - "use SSL if available" is gone.
Notice: Tunnels are assumed to be secure and thus default to no SSL.
Support for SASL (flexible authentication) has been added.
Support for Windows file systems has been added.
Support for compressed data transfer has been added.
Folder deletions can be propagated now.
[1.1.0]
Support for hierarchical mailboxes in Patterns.
Full support for IMAP pipelining (streaming, parallelization) added.
This is considerably faster especially with high-latency networks.
Faster and hopefully more reliable support for IMAP servers without the
UIDPLUS extension (e.g., M$ Exchange).
More automatic handling of SSL certificates.
IPv6 support.
IMAP password query can be scripted.
Message arrival dates can be propagated.
Data safety in case of system crashes was improved.
MaxMessages was made vastly more useful.
[1.0.0]
Essentially a rewrite. Synchronization state storage concept, configuration

48
README
View file

@ -14,16 +14,15 @@ currently Maildir and IMAP4 mailboxes are supported. New messages, message
deletions and flag changes can be propagated both ways.
``mbsync'' is suitable for use in IMAP-disconnected mode.
Synchronization is based on unique message identifiers (UIDs), so
no identification conflicts can occur (unlike with some other mail
Synchronization is based on unique message identifiers (UIDs), so no
identification conflicts can occur (as opposed to some other mail
synchronizers).
Synchronization state is kept in one local text file per mailbox pair;
these files are protected against concurrent ``mbsync'' processes.
Mailboxes can be safely modified while ``mbsync'' operates.
Multiple replicas of each mailbox can be maintained.
multiple replicas of a 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
@ -32,46 +31,41 @@ change was necessary because of massive changes in the user interface.
* Partial mirrors possible: keep only the latest messages locally
* Trash functionality: backup messages before removing them
* IMAP features:
* Supports TLS/SSL via imaps: (port 993) and STARTTLS
* Supports SASL for authentication
* Pipelining for maximum speed
* Supports TLS/SSL via imaps: (port 993) and STARTTLS (RFC2595)
* Supports CRAM-MD5 (RFC2195) for authentication
* Supports NAMESPACE (RFC2342) for simplified configuration
* Pipelining for maximum speed (currently only partially implemented)
* Compatibility
isync should work fairly well with any IMAP4 compliant server;
servers that support the UIDPLUS and LITERAL+ extensions are most
efficient.
particularily efficient with those that support the UIDPLUS and LITERAL+
extensions.
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.
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
At some point, ``isync'' has successfully run on:
Linux, Solaris 2.7, OpenBSD 2.8, FreeBSD 4.3.
Linux, Solaris 2.7, OpenBSD 2.8, FreeBSD 4.3, Cygwin
* 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)
./configure
make
sudo make install
make install
* Help

107
TODO
View file

@ -1,86 +1,53 @@
f{,data}sync() usage could be optimized by batching the calls.
make SSL certificate validation more automatic.
make SSL (connect) timeouts produce a bit more than "Unidentified socket error".
add deamon mode. primary goal: keep imap password in memory.
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?
add message expiration based on arrival date (message date would be too
unreliable). MaxAge; probably mutually exclusive to MaxMessages.
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
add regexp-based mailbox path rewriting to the drivers. user would provide
expressions for both directions. every transformation would be immediately
verified with the inverse transform. PathDelimiter and Flatten would become
special cases of this.
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().
add streaming from fetching to storing.
add asynchronous operation to remote mailbox drivers. this is actually
what prevents us from simply using c-client and thus becoming mailsync.
handle custom flags (keywords).
make use of IMAP CONDSTORE extension (rfc4551; CHANGEDSINCE FETCH Modifier);
make use of IMAP QRESYNC extension (rfc5162) to avoid SEARCH to find vanished
messages.
fix maildir_{open_store,list} to handle partial names (last char not slash).
add a way to automatically create and sync subfolders.
could store TUID even when UIDPLUS is supported. would avoid duplicated
messages after abort before new UID arrives.
decouple TUID search from append. that's a prerequisite for usable
MULTIAPPEND, and is generally good for async. should be way faster, too,
as it saves repeated mailbox rescans with single-file formats.
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.
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 request message attributes on a per-message basis from the drivers.
considerations:
- record non-existing UID ranges in the sync database, so IMAP FETCHes needn't
to exclude anyway non-existing messages explicitly.
- when detect unborn pairs and orphaned messages being gone? implied by expunge:
with trashing, by local driver, or of messages we deleted in this run. the
remaining cases could be handled by automatic periodical cleanup passes, an
explicit --cleanup action, or be implied by one of the other actions.
- the benefit of this is questionable, as fine-grained requests will result
in sending huge amounts of data, and upstream is often way slower than
downstream.
maildir: possibly timestamp mails with remote arrival date.
maybe throw out the ctx->recent stuff - it's used only for one info message.
possibly use ^[[1m to highlight error messages.
consider alternative approach to trashing: instead of the current trash-before-
expunge done by mbsync, let MUAs do the trashing (as modern ones typically do).
mbsync wouldn't do any trashing by itself, but should track the moves for
optimization. additionally, there should be a mode to move trashed messages to
the remote store. TrashMode Internal|External, AbsorbRemoteTrash.
a yet different approach to trashing is treating the trash like a normal mailbox.
however, this implies a huge working set.
consider alternative trash implementation: trash only messages we delete,
and trash before marking them deleted in the mailbox. downside: all other
programs have to do the same. and what if the deleted flag is unset?
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
items out of scope of purely UID based approach:
- detect message moves between folders
- recovering from UIDVALIDITY change (uw-imap < 2004.352 does this a lot)

View file

@ -16,8 +16,9 @@
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
AC_DEFUN([AM_MAINTAINER_MODE],
[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])

View file

@ -1,4 +1,6 @@
#! /bin/sh
set -e -v
make -f Makefile.am log
autoreconf -f -i
aclocal
autoheader
automake --add-missing
autoconf

3
build
View file

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

View file

@ -1,280 +0,0 @@
AC_INIT([isync], [1.4.4])
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)"
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>
#include <string.h>
int main(void)
{
time_t t = 0;
char buf[32];
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)"])])
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_FUNCS(vasprintf strnlen memrchr timegm)
AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
AC_CHECK_LIB(nsl, inet_ntoa, [SOCK_LIBS="$SOCK_LIBS -lnsl"])
AC_SUBST(SOCK_LIBS)
have_ipv6=true
sav_LIBS=$LIBS
LIBS="$LIBS $SOCK_LIBS"
AC_CHECK_FUNCS(getaddrinfo inet_ntop, , [have_ipv6=false])
LIBS=$sav_LIBS
if $have_ipv6; then
AC_DEFINE(HAVE_IPV6, 1, [if your libc has IPv6 support])
fi
have_ssl_paths=
AC_ARG_WITH(ssl,
AS_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
""|yes)
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_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`
have_ssl_paths=yes
AC_MSG_RESULT([found])
else
AC_MSG_RESULT([not found])
fi
fi
;;
*)
SSL_LDFLAGS=-L$ob_cv_with_ssl/lib$libsuff
SSL_CPPFLAGS=-I$ob_cv_with_ssl/include
;;
esac
if test -z "$have_ssl_paths"; 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(ssl, SSL_connect,
[SSL_LIBS="-lssl $LIBCRYPTO $LIBDL" have_ssl_paths=yes])
LDFLAGS=$sav_LDFLAGS
fi
sav_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS"
AC_CHECK_HEADER(openssl/ssl.h, , [have_ssl_paths=])
CPPFLAGS=$sav_CPPFLAGS
if test -z "$have_ssl_paths"; then
if test -n "$ob_cv_with_ssl"; then
AC_MSG_ERROR([OpenSSL libs and/or includes were not found where specified])
fi
else
AC_DEFINE(HAVE_LIBSSL, 1, [if you have the OpenSSL libraries])
CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS"
LDFLAGS="$LDFLAGS $SSL_LDFLAGS"
fi
fi
AC_SUBST(SSL_LIBS)
have_sasl_paths=
AC_ARG_WITH(sasl,
AS_HELP_STRING([--with-sasl[=PATH]], [where to look for SASL [detect]]),
[ob_cv_with_sasl=$withval])
if test "x$ob_cv_with_sasl" != xno; then
case $ob_cv_with_sasl in
""|yes)
dnl FIXME: Try various possible paths here...
;;
*)
SASL_LDFLAGS=-L$ob_cv_with_sasl/lib$libsuff
SASL_CPPFLAGS=-I$ob_cv_with_sasl/include
;;
esac
if test -z "$have_sasl_paths"; then
sav_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $SASL_LDFLAGS"
AC_CHECK_LIB(sasl2, sasl_client_init,
[SASL_LIBS="-lsasl2" have_sasl_paths=yes])
LDFLAGS=$sav_LDFLAGS
fi
sav_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $SASL_CPPFLAGS"
AC_CHECK_HEADER(sasl/sasl.h, , [have_sasl_paths=])
CPPFLAGS=$sav_CPPFLAGS
if test -z "$have_sasl_paths"; then
if test -n "$ob_cv_with_sasl"; then
AC_MSG_ERROR([SASL libs and/or includes were not found where specified])
fi
else
AC_DEFINE(HAVE_LIBSASL, 1, [if you have the SASL libraries])
CPPFLAGS="$CPPFLAGS $SASL_CPPFLAGS"
LDFLAGS="$LDFLAGS $SASL_LDFLAGS"
fi
fi
AC_SUBST(SASL_LIBS)
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], [])
LIBS=$sav_LIBS
])
if test "x$ac_cv_berkdb4" = xyes; then
AC_SUBST([DB_LIBS], ["-ldb"])
AC_DEFINE(USE_DB, 1, [if Berkeley DB should be used])
fi
have_zlib=
AC_ARG_WITH(zlib,
AS_HELP_STRING([--with-zlib], [use zlib [detect]]),
[ob_cv_with_zlib=$withval])
if test "x$ob_cv_with_zlib" != xno; then
AC_CHECK_LIB([z], [deflate],
[AC_CHECK_HEADER(zlib.h,
[have_zlib=1
AC_SUBST([Z_LIBS], ["-lz"])
AC_DEFINE([HAVE_LIBZ], 1, [if you have the zlib library])]
)]
)
fi
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_OUTPUT
AC_MSG_RESULT()
if test -n "$have_ssl_paths"; then
AC_MSG_RESULT([Using SSL])
else
AC_MSG_RESULT([Not using SSL])
fi
if test -n "$have_sasl_paths"; then
AC_MSG_RESULT([Using SASL])
else
AC_MSG_RESULT([Not using SASL])
fi
if test -n "$have_zlib"; then
AC_MSG_RESULT([Using zlib])
else
AC_MSG_RESULT([Not using zlib])
fi
if test "x$ac_cv_berkdb4" = xyes; then
AC_MSG_RESULT([Using Berkeley DB])
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()

106
configure.in Normal file
View file

@ -0,0 +1,106 @@
AC_INIT(src/isync.h)
AM_CONFIG_HEADER(config.h)
AM_INIT_AUTOMAKE(isync, 1.0.1)
AM_MAINTAINER_MODE
AM_PROG_CC_STDC
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -pipe -W -Wall -Wshadow -Wstrict-prototypes"
fi
AC_CHECK_FUNCS(vasprintf)
AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
AC_CHECK_LIB(nsl, inet_ntoa, [SOCK_LIBS="$SOCK_LIBS -lnsl"])
AC_SUBST(SOCK_LIBS)
m4_ifdef([AS_HELP_STRING], , [m4_define([AS_HELP_STRING], m4_defn([AC_HELP_STRING]))])
have_ssl_paths=
AC_ARG_WITH(ssl,
AS_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
""|yes)
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.
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 $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
AC_MSG_RESULT([not found])
fi
fi
;;
*)
SSL_LDFLAGS=-L$ob_cv_with_ssl/lib$libsuff
SSL_CPPFLAGS=-I$ob_cv_with_ssl/include
;;
esac
if test -z "$have_ssl_paths"; then
sav_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $SSL_LDFLAGS"
AC_CHECK_LIB(dl, dlopen, [LIBDL=-ldl])
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
fi
sav_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS"
AC_CHECK_HEADER(openssl/ssl.h, , [have_ssl_paths=])
CPPFLAGS=$sav_CPPFLAGS
if test -z "$have_ssl_paths"; then
if test -n "$ob_cv_with_ssl"; then
AC_MSG_ERROR([OpenSSL libs and/or includes were not found where specified])
fi
else
AC_DEFINE(HAVE_LIBSSL, 1, [if you have the OpenSSL libraries])
CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS"
LDFLAGS="$LDFLAGS $SSL_LDFLAGS"
fi
fi
AC_SUBST(SSL_LIBS)
AC_CACHE_CHECK([for Berkley DB 4.2], ac_cv_berkdb4,
[ac_cv_berkdb4=no
AC_TRY_LINK([#include <db.h>],
[DB *db;
db->truncate(db, 0, 0, 0);
db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 0)],
[ac_cv_berkdb4=yes])])
if test "x$ac_cv_berkdb4" = xno; then
AC_MSG_ERROR([Berkley DB 4.2 not found.
You must install libdb4.2 including the respective development files/headers.])
fi
AC_ARG_ENABLE(compat,
AS_HELP_STRING([--disable-compat], [don't include isync compatibility wrapper [no]]),
[ob_cv_enable_compat=$enableval])
if test "x$ob_cv_enable_compat" != xno; then
AC_CHECK_FUNCS(getopt_long)
fi
AM_CONDITIONAL(with_compat, test "x$ob_cv_enable_compat" != xno)
AC_OUTPUT(Makefile src/Makefile src/compat/Makefile isync.spec)
if test -n "$have_ssl_paths"; then
AC_MSG_RESULT([
Using SSL
])
else
AC_MSG_RESULT([
Not using SSL
])
fi

5
debian/.cvsignore vendored Normal file
View file

@ -0,0 +1,5 @@
files
isync
isync.postrm.debhelper
isync.substvars
patched

7
debian/.gitignore vendored
View file

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

13
debian/README.Debian vendored
View file

@ -1,13 +0,0 @@
A note from isync's web site:
isync can be integrated into Mutt fairly easily with a few hooks:
folder-hook ~A bind index $ <sync-mailbox>
folder-hook +maildir 'macro index $ "<sync-mailbox>!mbsync the_channel:maildir\n"'
where the_channel is the Channel used to sync this mailbox, and maildir is the
name of the local mailbox itself. This works well so long as you are not
modifying the IMAP mailbox outside of Mutt. However, if you are using another
mail program simultaneously, Mutt will have the wrong idea of the local mailbox
flags and messages will start disappearing from its index display (don't worry,
they are still on disk).

235
debian/changelog vendored
View file

@ -1,234 +1,13 @@
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.
* Drop debconf note that deals with a pre-Etch transition.
Closes: #492194
-- Christian Perrier <bubulle@debian.org> Sat, 25 Oct 2008 08:40:52 +0200
isync (1.0.4-2) unstable; urgency=low
* Change the libdb4.4-dev build-dependency to libdb-dev. Thanks Luk for
pointing this. (Closes: #499165)
-- Nicolas Boullis <nboullis@debian.org> Wed, 17 Sep 2008 23:58:58 +0200
isync (1.0.4-1) unstable; urgency=low
* The second "thanks Christian" release.
* New upstream release.
- Accept empty "* SEARCH" response. (Closes: #413336)
- Quote user name in generated config. (Closes: #456783)
* Explain the isync->mbsync change in the package description.
(Closes: #430648)
* Fix the debian/watch file that lacked the version and action fields.
* Disable the upstream "deb-clean" stuff in the top-level Makefile, as
in breaks cleaning the build directory.
* Bump Standards-Version to 3.7.3. (No change required.)
-- Nicolas Boullis <nboullis@debian.org> Sat, 03 May 2008 01:42:55 +0200
isync (1.0.3-3.1) unstable; urgency=low
* Non-maintainer upload to fix pending l10n issues.
* Debconf translations:
- Portuguese. Closes: #418283
- Italian. Closes: #418246
- Dutch. Closes: #422244
- Spanish. Closes: #426184
- Finnish. Closes: #468214
- Galician. Closes: #470529
* [Lintian] Do not include debian revision in the build dependency for
libssl-dev
* [Lintian] No longer ignore errors from "make distclean"
-- Christian Perrier <bubulle@debian.org> Wed, 12 Mar 2008 07:24:01 +0100
isync (1.0.3-3) unstable; urgency=low
* The "thanks Christian" release.
* Update German debconf templates translation. Thanks to Erik Schanze
(for the translation) and Christian Perrier (for forwarding the
translation). (Closes: #407615)
-- Nicolas Boullis <nboullis@debian.org> Mon, 5 Feb 2007 00:17:15 +0100
isync (1.0.3-2.1) unstable; urgency=low
* Non-maintainer upload with maintainer's permission
* Debconf templates translations:
- French updated by me
- Brazilian Portuguese translation added
- Czech translation added. Closes: #403473
- Russian translation added. Closes: #403510
- Vietnamese translation added
- Norwegian Bokmål translation added. Closes: #403523
-- Christian Perrier <bubulle@debian.org> Sun, 17 Dec 2006 15:31:04 +0100
isync (1.0.3-2) unstable; urgency=low
* Back to unstable, with permission from Steve Langasek. (Message-ID:
<20061121015225.GF28035@borges.dodds.net>)
* Rewrite the debconf note, thanks to the debian-l10n-english team (and
especially MJ Ray).
* Also add some information about the new version into NEWS.Debian.
* Remove the information about the need to set the T (trashed) flag from
README.Debian.
* Also install the isyncrc.sample sample configuration file.
* Bump Standards-Version to 3.7.2. (No change required.)
-- Nicolas Boullis <nboullis@debian.org> Tue, 5 Dec 2006 00:34:54 +0100
isync (1.0.3-1) experimental; urgency=low
* New upstream release. (Closes: #315423)
- Isync now supports breaking and linking threads. (Closes: #177280)
- It also supports unflagging messages. (Closes: #111286)
- IMAP commands are sent asynchronously. (Closes: #226222)
* Kill the old debconf question about upgrades from pre-0.8 versions.
* Use the (now obsolete) swedish and portugese translations anyway.
(Closes: #337771, #378891)
* New debconf note that warns about upgrades from pre-1.0 versions.
* Add a build dependency on po-debconf.
-- Nicolas Boullis <nboullis@debian.org> Sun, 19 Nov 2006 15:04:31 +0100
isync (0.9.2-4) unstable; urgency=low
* Add Czech debconf translation, thanks to Martin Šín. (Closes: #317571)
* Build with the newest libssl-dev.
* Load the debconf library in postinst to ensure that everything works
as expected, thanks to lintian for noticing the problem and to
Josselin Mouette for pointing to the right doc.
* Fix a bashism in the config script, thanks to lintian.
* Update the postal address of the FSF in the copyright file.
* Bump Standards-Version to 3.6.2. (No change required.)
-- Nicolas Boullis <nboullis@debian.org> Mon, 10 Oct 2005 01:37:50 +0200
isync (0.9.2-3) unstable; urgency=low
* Bump build-dependency from libdb4.0-dev to libdb4.2-dev, thanks to
Andreas Jochens. (Closes: #280268)
-- Nicolas Boullis <nboullis@debian.org> Tue, 9 Nov 2004 18:21:12 +0100
isync (0.9.2-2) unstable; urgency=low
* Add german debconf templates translation, thanks to Erik Schanze.
(Closes: #267675)
-- Nicolas Boullis <nboullis@debian.org> Tue, 24 Aug 2004 00:32:32 +0200
isync (0.9.2-1) unstable; urgency=low
isync (0.9.2+cvsXXXXXXXX-1) unstable; urgency=low
* New upstream release.
- Password prompt now includes the mailbox/server. (Closes: #92893)
* Backported from CVS:
- A few prinf converted to info (disabled with -q).
- A few other printf converted to warn (disabled with -q -q) to be
able to disable the warning when SSL is not available.
(Closes: #228086)
- Update the manpage accordingly (about -q).
- Improve the manpage (about using isync with mutt).
* Add Theodore Y. Ts'o as a co-maintainter.
- Password prompt now includes the mailbox/server (Closes: #92893)
* Theodore Ts'o added as co-maintainer
* Added initial asynchronous flags synchronization patch (Closes: #226222)
* Ignore anything that does not look remotely like a maildir when
collecting mailboxes for OneToOne (from isync CVS)
-- Nicolas Boullis <nboullis@debian.org> Tue, 13 Apr 2004 02:12:42 +0200
-- Theodore Y. Ts'o <tytso@mit.edu> Sun, 11 Jan 2004 02:38:48 -0500
isync (0.9.1-4) unstable; urgency=low

2
debian/compat vendored
View file

@ -1 +1 @@
9
4

37
debian/control vendored
View file

@ -2,37 +2,28 @@ 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: Nicolas Boullis <nboullis@debian.org>, Theodore Y. Ts'o <tytso@mit.edu>
Standards-Version: 3.6.1
Build-Depends: libssl-dev, debhelper (>= 4.1.16), dpkg-dev (>= 1.9.0), libdb4.2-dev, dpatch
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).
.
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 (currently only partially implemented)

60
debian/copyright vendored
View file

@ -1,33 +1,33 @@
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-2003 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* As a special exception, isync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
On Debian systems, the complete text of the GNU General Public
License can be found in /usr/share/common-licenses/GPL

1
debian/dirs vendored Normal file
View file

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

3
debian/docs vendored Normal file
View file

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

39
debian/generate-deb vendored Executable file
View file

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

0
debian/patches/00list vendored Normal file
View file

60
debian/rules vendored
View file

@ -1,8 +1,58 @@
#!/usr/bin/make -f
%:
dh $@ --with=autoreconf
PACKAGE=isync
override_dh_auto_install:
dh_auto_install
$(RM) $(CURDIR)/debian/isync/usr/share/doc/isync/ChangeLog
CFLAGS = -Wall -g
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
CFLAGS += -O0
else
CFLAGS += -O2
endif
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: patch-stamp
dh_testdir
./configure --disable-maintainer-mode --build=$(DEB_BUILD_GNU_TYPE) --host=$(DEB_HOST_GNU_TYPE) --prefix=/usr --mandir=/usr/share/man
$(MAKE) CFLAGS="$(CFLAGS)"
touch build-stamp
clean: clean1 unpatch
clean1:
dh_testdir
dh_testroot
rm -f build-stamp
-$(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
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 clean1
include /usr/share/dpatch/dpatch.make

View file

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

4
debian/watch vendored
View file

@ -1,2 +1,2 @@
version=3
http://sf.net/isync/ isync-(.*)\.tar\.gz
version=2
http://sourceforge.net/project/showfiles.php?group_id=69662 .*/isync-(.*).tar.gz.*

View file

@ -17,8 +17,8 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
if [ $# != 1 ]; then

View file

@ -2,7 +2,7 @@ Summary: Utility to synchronize IMAP mailboxes with local maildir folders
Name: isync
Version: @VERSION@
Release: 1
License: GPL
Copyright: GPL
Group: Applications/Internet
Source: @PACKAGE@-@VERSION@.tar.gz
URL: http://@PACKAGE@.sf.net/
@ -19,17 +19,20 @@ non-permanent internet collection (dIMAP).
%prep
%setup
%build
%configure
./configure --prefix=/usr
%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install
rm -rf $RPM_BUILD_ROOT%{_docdir}/%{name}
%clean
rm -rf $RPM_BUILD_ROOT
%files
%doc AUTHORS COPYING NEWS README TODO ChangeLog src/mbsyncrc.sample
%{_bindir}/*
%{_mandir}/man1/*
%doc AUTHORS COPYING NEWS README TODO ChangeLog src/mbsyncrc.sample src/compat/isyncrc.sample
/usr/bin/isync
/usr/bin/mbsync
/usr/bin/mdconvert
/usr/bin/get-cert
/usr/man/man1/isync.1.gz
/usr/man/man1/mbsync.1.gz
/usr/man/man1/mdconvert.1.gz

5
src/.cvsignore Normal file
View file

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

8
src/.gitignore vendored
View file

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

View file

@ -1,28 +1,16 @@
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
bin_PROGRAMS = mbsync mdconvert
mbsync_SOURCES = main.c sync.c config.c util.c drv_imap.c drv_maildir.c
mbsync_LDADD = -ldb $(SSL_LIBS) $(SOCK_LIBS)
noinst_HEADERS = isync.h
mdconvert_SOURCES = mdconvert.c
mdconvert_LDADD = $(DB_LIBS)
if with_mdconvert
mdconvert_prog = mdconvert
mdconvert_man = mdconvert.1
endif
mdconvert_LDADD = -ldb
EXTRA_PROGRAMS = tst_timers
tst_timers_SOURCES = tst_timers.c util.c
bin_PROGRAMS = mbsync $(mdconvert_prog)
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
man_MANS = mbsync.1 mdconvert.1
EXTRA_DIST = mbsyncrc.sample $(man_MANS)

View file

@ -1,264 +0,0 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* 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
* 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.
*/
#ifndef COMMON_H
#define COMMON_H
#include <autodefs.h>
#include <sys/types.h>
#include <stdarg.h>
#include <stdio.h>
#include <time.h>
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))
#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
#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
# define INLINE
#endif
#define EXE "mbsync"
/* main.c */
#define DEBUG_CRASH 0x01
#define DEBUG_MAILDIR 0x02
#define DEBUG_NET 0x04
#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 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;
extern int Pid;
extern char Hostname[256];
extern const char *Home;
extern uint BufferLimit;
extern int new_total[2], new_done[2];
extern int flags_total[2], flags_done[2];
extern int trash_total[2], trash_done[2];
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 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 );
typedef struct string_list {
struct string_list *next;
char string[1];
} string_list_t;
void add_string_list_n( string_list_t **list, const char *str, uint 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 );
#ifndef HAVE_TIMEGM
time_t timegm( struct tm *tm );
#endif
void *nfmalloc( size_t sz );
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 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 );
#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 arc4_init( void );
uchar arc4_getbyte( void );
uint bucketsForSize( uint size );
typedef struct list_head {
struct list_head *next, *prev;
} list_head_t;
typedef struct notifier {
struct notifier *next;
void (*cb)( int what, void *aux );
void *aux;
#ifdef HAVE_POLL_H
uint index;
#else
int fd;
short events;
#endif
} notifier_t;
#ifdef HAVE_POLL_H
# include <poll.h>
#else
# define POLLIN 1
# define POLLOUT 4
# define POLLERR 8
#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 wipe_notifier( notifier_t *sn );
typedef struct {
list_head_t links;
void (*cb)( void *aux );
void *aux;
time_t timeout;
} wakeup_t;
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; }
void main_loop( void );
#endif

4
src/compat/.cvsignore Normal file
View file

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

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

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

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

@ -0,0 +1,465 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "isync.h"
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <pwd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
static int local_home, local_root;
static char *
my_strndup( const char *s, size_t nchars )
{
char *r = nfmalloc( sizeof(char) * (nchars + 1) );
memcpy( r, s, nchars );
r[nchars] = 0;
return r;
}
char *
expand_strdup( const char *s )
{
struct passwd *pw;
const char *p, *q;
char *r;
if (*s == '~') {
s++;
if (!*s) {
p = 0;
q = Home;
} else if (*s == '/') {
p = s + 1;
q = Home;
} else {
if ((p = strchr( s, '/' ))) {
r = my_strndup( s, (int)(p - s) );
pw = getpwnam( r );
free( r );
p++;
} else
pw = getpwnam( s );
if (!pw)
return 0;
q = pw->pw_dir;
}
nfasprintf( &r, "%s/%s", q, p ? p : "" );
return r;
} else if (*s != '/' && xmaildir) {
nfasprintf( &r, "%s/%s", xmaildir, s );
return r;
} else
return nfstrdup( s );
}
static int
is_true( const char *val )
{
return
!strcasecmp( val, "yes" ) ||
!strcasecmp( val, "true" ) ||
!strcasecmp( val, "on" ) ||
!strcmp( val, "1" );
}
void
load_config( const char *path, config_t ***stor )
{
config_t **sstor, *cfg;
FILE *fp;
char *p, *cmd, *val;
int line = 0;
char buf[1024];
if (!(fp = fopen( path, "r" ))) {
if (errno != ENOENT)
perror( "fopen" );
return;
}
if (!Quiet && !Debug && !Verbose)
printf( "Reading configuration file %s\n", path );
buf[sizeof(buf) - 1] = 0;
cfg = &global;
while (fgets( buf, sizeof(buf) - 1, fp )) {
p = buf;
cmd = next_arg( &p );
val = next_arg( &p );
line++;
if (!cmd || *cmd == '#')
continue;
if (!val) {
fprintf( stderr, "%s:%d: parameter missing\n", path, line );
continue;
}
if (!strcasecmp( "Mailbox", cmd )) {
if (o2o)
break;
cfg = **stor = nfmalloc( sizeof(config_t) );
*stor = &cfg->next;
memcpy( cfg, &global, sizeof(config_t) );
if (val[0] == '~' && val[1] == '/') {
val += 2;
local_home = 1;
} else if (!memcmp( val, Home, HomeLen ) && val[HomeLen] == '/') {
val += HomeLen + 1;
local_home = 1;
} else if (val[0] == '/')
local_root = 1;
else
local_home = 1;
/* not expanded at this point */
cfg->path = nfstrdup( val );
} else if (!strcasecmp( "OneToOne", cmd )) {
if (boxes) {
forbid:
fprintf( stderr,
"%s:%d: keyword '%s' allowed only in global section\n",
path, line, cmd );
continue;
}
o2o = is_true( val );
} else if (!strcasecmp( "Maildir", cmd )) {
if (boxes)
goto forbid;
maildir = nfstrdup( val );
xmaildir = expand_strdup( val );
} else if (!strcasecmp( "Folder", cmd )) {
if (boxes)
goto forbid;
folder = nfstrdup( val );
} else if (!strcasecmp( "Inbox", cmd )) {
if (boxes)
goto forbid;
inbox = nfstrdup( val );
} else if (!strcasecmp( "Host", cmd )) {
if (!memcmp( "imaps:", val, 6 )) {
val += 6;
cfg->use_imaps = 1;
cfg->port = 993;
cfg->use_sslv2 = 1;
cfg->use_sslv3 = 1;
}
cfg->host = nfstrdup( val );
} else if (!strcasecmp( "User", cmd ))
cfg->user = nfstrdup( val );
else if (!strcasecmp( "Pass", cmd ))
cfg->pass = nfstrdup( val );
else if (!strcasecmp ( "Port", cmd ))
cfg->port = atoi( val );
else if (!strcasecmp ( "Box", cmd ))
cfg->box = nfstrdup( val );
else if (!strcasecmp ( "Alias", cmd )) {
if (!boxes) {
fprintf( stderr,
"%s:%d: keyword 'Alias' allowed only in mailbox specification\n",
path, line );
continue;
}
cfg->alias = nfstrdup( val );
} else if (!strcasecmp( "MaxSize", cmd ))
cfg->max_size = atol( val );
else if (!strcasecmp ( "MaxMessages", cmd ))
cfg->max_messages = atol( val );
else if (!strcasecmp ( "UseNamespace", cmd ))
cfg->use_namespace = is_true( val );
else if (!strcasecmp ( "CopyDeletedTo", cmd ))
cfg->copy_deleted_to = nfstrdup( val );
else if (!strcasecmp ( "Tunnel", cmd ))
cfg->tunnel = nfstrdup( val );
else if (!strcasecmp ( "Expunge", cmd ))
cfg->expunge = is_true( val );
else if (!strcasecmp( "Delete", cmd ))
cfg->delete = is_true( val );
else if (!strcasecmp( "CertificateFile", cmd ))
cfg->cert_file = expand_strdup( val );
else if (!strcasecmp( "RequireSSL", cmd ))
cfg->require_ssl = is_true( val );
else if (!strcasecmp( "UseSSLv2", cmd ))
cfg->use_sslv2 = is_true( val );
else if (!strcasecmp( "UseSSLv3", cmd ))
cfg->use_sslv3 = is_true( val );
else if (!strcasecmp( "UseTLSv1", cmd ))
cfg->use_tlsv1 = is_true( val );
else if (!strcasecmp( "RequireCRAM", cmd ))
cfg->require_cram = is_true( val );
else if (buf[0])
fprintf( stderr, "%s:%d: unknown keyword '%s'\n", path, line, cmd );
}
fclose( fp );
if (o2o) {
if (!global.host && !global.tunnel) {
fprintf( stderr, "Neither Host nor Tunnel given to OneToOne. Aborting.\n" );
exit( 1 );
}
} else
for (sstor = &boxes; (cfg = *sstor); ) {
if (!cfg->host && !cfg->tunnel) {
fprintf( stderr, "Mailbox '%s' has neither Host nor Tunnel. Skipping.\n",
cfg->alias ? cfg->alias : cfg->path );
if (&cfg->next == *stor)
*stor = sstor;
*sstor = cfg->next;
continue;
}
sstor = &cfg->next;
}
}
static const char *
tb( int on )
{
return on ? "yes" : "no";
}
static void
write_imap_server( FILE *fp, config_t *cfg )
{
config_t *pbox;
char *p, *p2;
int hl, a1, a2, a3, a4;
char buf[128];
static int tunnels;
if (cfg->tunnel) {
nfasprintf( (char **)&cfg->server_name, "tunnel%d", ++tunnels );
fprintf( fp, "IMAPAccount %s\nTunnel \"%s\"\n",
cfg->server_name, cfg->tunnel );
} else {
if (sscanf( cfg->host, "%d.%d.%d.%d", &a1, &a2, &a3, &a4 ) == 4)
cfg->server_name = nfstrdup( cfg->host );
else {
p = strrchr( cfg->host, '.' );
if (!p)
hl = nfsnprintf( buf, sizeof(buf), "%s", cfg->host );
else {
hl = nfsnprintf( buf, sizeof(buf), "%.*s", p - cfg->host, cfg->host );
p2 = strrchr( buf, '.' );
if (p2)
hl = sprintf( buf, "%s", p2 + 1 );
}
if (boxes) /* !o2o */
for (pbox = boxes; pbox != cfg; pbox = pbox->next)
if (!memcmp( pbox->server_name, buf, hl + 1 )) {
nfasprintf( (char **)&cfg->server_name, "%s-%d", buf, ++pbox->servers );
goto gotsrv;
}
cfg->server_name = nfstrdup( buf );
cfg->servers = 1;
gotsrv: ;
}
fprintf( fp, "IMAPAccount %s\n", cfg->server_name );
if (cfg->use_imaps)
fprintf( fp, "Host imaps:%s\n", cfg->host );
else
fprintf( fp, "Host %s\n", cfg->host );
fprintf( fp, "Port %d\n", cfg->port );
}
if (cfg->user)
fprintf( fp, "User %s\n", cfg->user );
if (cfg->pass)
fprintf( fp, "Pass \"%s\"\n", cfg->pass );
fprintf( fp, "RequireCRAM %s\nRequireSSL %s\n"
"UseSSLv2 %s\nUseSSLv3 %s\nUseTLSv1 %s\n",
tb(cfg->require_cram), tb(cfg->require_ssl),
tb(cfg->use_sslv2), tb(cfg->use_sslv3), tb(cfg->use_tlsv1) );
if ((cfg->use_imaps || cfg->use_sslv2 || cfg->use_sslv3 || cfg->use_tlsv1) &&
cfg->cert_file)
fprintf( fp, "CertificateFile %s\n", cfg->cert_file );
fputc( '\n', fp );
}
static void
write_imap_store( FILE *fp, config_t *cfg )
{
if (cfg->stores > 1)
nfasprintf( (char **)&cfg->store_name, "%s-%d", cfg->server_name, cfg->stores );
else
cfg->store_name = cfg->server_name;
fprintf( fp, "IMAPStore %s\nAccount %s\n",
cfg->store_name, cfg->server_name );
if (*folder)
fprintf( fp, "Path \"%s\"\n", folder );
else
fprintf( fp, "UseNamespace %s\n", tb(cfg->use_namespace) );
if (inbox)
fprintf( fp, "MapInbox \"%s\"\n", inbox );
if (cfg->copy_deleted_to)
fprintf( fp, "Trash \"%s\"\n", cfg->copy_deleted_to );
fputc( '\n', fp );
}
static void
write_channel_parm( FILE *fp, config_t *cfg )
{
if (cfg->max_size)
fprintf( fp, "MaxSize %d\n", cfg->max_size );
if (cfg->max_messages)
fprintf( fp, "MaxMessages %d\n", cfg->max_messages );
if (!cfg->delete && !delete)
fputs( "Sync New ReNew Flags\n", fp );
if (cfg->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;
config_t *box, *sbox, *pbox;
if (!(fp = fdopen( fd, "w" ))) {
perror( "fdopen" );
return;
}
fprintf( fp, "SyncState *\n\n" );
if (local_home || o2o)
fprintf( fp, "MaildirStore local\nPath \"%s/\"\nAltMap %s\n\n", maildir, tb( altmap > 0 ) );
if (local_root)
fprintf( fp, "MaildirStore local_root\nPath /\nAltMap %s\n\n", tb( altmap > 0 ) );
if (o2o) {
write_imap_server( fp, &global );
write_imap_store( fp, &global );
fprintf( fp, "Channel o2o\nMaster :%s:\nSlave :local:\nPattern %%\n", global.store_name );
write_channel_parm( fp, &global );
} else {
for (box = boxes; box; box = box->next) {
for (pbox = boxes; pbox != box; pbox = pbox->next) {
if (box->tunnel) {
if (mstrcmp( pbox->tunnel, box->tunnel ))
continue;
} else {
if (mstrcmp( pbox->host, box->host ) ||
pbox->use_imaps != box->use_imaps ||
pbox->port != box->port)
continue;
}
if (mstrcmp( pbox->user, box->user ) ||
mstrcmp( pbox->pass, box->pass )) /* nonsense */
continue;
if ((box->use_imaps || box->use_sslv2 ||
box->use_sslv3 || box->use_tlsv1) &&
mstrcmp( pbox->cert_file, box->cert_file )) /* nonsense */
continue;
if (pbox->use_imaps != box->use_imaps ||
pbox->use_sslv2 != box->use_sslv2 ||
pbox->use_sslv3 != box->use_sslv3 ||
pbox->use_tlsv1 != box->use_tlsv1)
continue;
box->server_name = pbox->server_name;
for (sbox = boxes; sbox != box; sbox = sbox->next) {
if (sbox->server_name != box->server_name ||
mstrcmp( sbox->copy_deleted_to, box->copy_deleted_to ) ||
(!*folder && sbox->use_namespace != box->use_namespace))
continue;
box->store_name = sbox->store_name;
goto gotall;
}
box->stores = ++pbox->stores;
goto gotsrv;
}
write_imap_server( fp, box );
box->stores = 1;
gotsrv:
write_imap_store( fp, box );
gotall:
if (box->alias)
cn = box->alias;
else {
cn = strrchr( box->path, '/' );
if (cn)
cn++;
else
cn = box->path;
}
for (sbox = boxes; sbox != box; sbox = sbox->next) {
if (sbox->alias)
scn = sbox->alias;
else {
scn = strrchr( sbox->path, '/' );
if (scn)
scn++;
else
scn = sbox->path;
}
if (mstrcmp( cn, scn ))
continue;
nfasprintf( (char **)&box->channel_name, "%s-%d", cn, ++sbox->channels );
goto gotchan;
}
box->channels = 1;
box->channel_name = cn;
gotchan:
if (box->path[0] == '/')
fprintf( fp, "Channel %s\nMaster :%s:%s\nSlave :local_root:%s\n",
box->channel_name, box->store_name, box->box, box->path + 1 );
else
fprintf( fp, "Channel %s\nMaster :%s:%s\nSlave :local:%s\n",
box->channel_name, box->store_name, box->box, box->path );
write_channel_parm( fp, box );
}
}
fclose( fp );
}
config_t *
find_box( const char *s )
{
config_t *p;
char *t;
if (!memcmp( s, 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;
}

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

@ -0,0 +1,259 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "isync.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>
#include <db.h>
static const char *subdirs[] = { "cur", "new", "tmp" };
static const char Flags[] = { 'D', 'F', 'R', 'S', 'T' };
static int
parse_info( const char *s )
{
unsigned i;
int flags;
flags = 0;
if (s && *(s + 1) == '2' && *(s + 2) == ',')
for (s += 3, i = 0; i < as(Flags); i++)
if (strchr( s, Flags[i] ))
flags |= (1 << i);
return flags;
}
typedef struct {
int uid, flags;
} msg_t;
static int
compare_uids( const void *l, const void *r )
{
return ((msg_t *)l)->uid - ((msg_t *)r)->uid;
}
static DBT key, value;
static struct flock lck;
void
convert( config_t *box )
{
DIR *d;
struct dirent *e;
char *s, *p, *mboxdir;
FILE *fp;
msg_t *msgs;
DB *db;
int i, ret, fd, uidval, maxuid, bl, uid, rmsgs, nmsgs, uv[2];
unsigned u;
struct stat sb;
char buf[_POSIX_PATH_MAX], diumname[_POSIX_PATH_MAX],
uvname[_POSIX_PATH_MAX], sname[_POSIX_PATH_MAX],
iuvname[_POSIX_PATH_MAX], imuname[_POSIX_PATH_MAX],
ilname[_POSIX_PATH_MAX], iumname[_POSIX_PATH_MAX];
mboxdir = expand_strdup( box->path );
nfsnprintf( iuvname, sizeof(iuvname), "%s/isyncuidvalidity", mboxdir );
nfsnprintf( diumname, sizeof(iumname), "%s/.isyncuidmap.db", mboxdir );
nfsnprintf( uvname, sizeof(uvname), "%s/.uidvalidity", mboxdir );
if (stat( iuvname, &sb )) {
if (!stat( diumname, &sb ))
altmap++;
else if (!stat( uvname, &sb ))
altmap--;
err1:
free( mboxdir );
return;
}
for (i = 0; i < 3; i++) {
nfsnprintf( buf, sizeof(buf), "%s/%s", mboxdir, subdirs[i] );
if (stat( buf, &sb )) {
fprintf( stderr, "ERROR: stat %s: %s (errno %d)\n", buf,
strerror(errno), errno );
fprintf( stderr,
"ERROR: %s does not appear to be a valid maildir style mailbox\n",
mboxdir );
goto err1;
}
}
nfsnprintf( iumname, sizeof(iumname), "%s/isyncuidmap.db", mboxdir );
nfsnprintf( imuname, sizeof(imuname), "%s/isyncmaxuid", mboxdir );
nfsnprintf( ilname, sizeof(ilname), "%s/isynclock", mboxdir );
nfsnprintf( sname, sizeof(sname), "%s/.mbsyncstate", mboxdir );
if ((fd = open( ilname, O_WRONLY|O_CREAT, 0600 )) < 0) {
perror( ilname );
goto err1;
}
#if SEEK_SET != 0
lck.l_whence = SEEK_SET;
#endif
#if F_WRLCK != 0
lck.l_type = F_WRLCK;
#endif
if (fcntl( fd, F_SETLKW, &lck )) {
perror( ilname );
err2:
close( fd );
goto err1;
}
if (!(fp = fopen( iuvname, "r" ))) {
perror( iuvname );
goto err2;
}
fscanf( fp, "%d", &uidval );
fclose( fp );
if (!(fp = fopen( imuname, "r" ))) {
perror( imuname );
goto err2;
}
fscanf( fp, "%d", &maxuid );
fclose( fp );
if (!stat( iumname, &sb )) {
if (db_create( &db, 0, 0 )) {
fputs( "dbcreate failed\n", stderr );
goto err2;
}
if (db->open( db, 0, iumname, 0, DB_HASH, 0, 0 )) {
fputs( "cannot open db\n", stderr );
db->close( db, 0 );
goto err2;
}
altmap++;
} else {
db = 0;
altmap--;
}
msgs = 0;
rmsgs = 0;
nmsgs = 0;
for (i = 0; i < 2; i++) {
bl = nfsnprintf( buf, sizeof(buf), "%s/%s/", mboxdir, subdirs[i] );
if (!(d = opendir( buf ))) {
perror( "opendir" );
err4:
if (msgs)
free( msgs );
if (db)
db->close( db, 0 );
goto err2;
}
while ((e = readdir( d ))) {
if (*e->d_name == '.')
continue;
s = strchr( e->d_name, ':' );
if (db) {
key.data = e->d_name;
key.size = s ? (size_t)(s - e->d_name) : strlen( e->d_name );
if ((ret = db->get( db, 0, &key, &value, 0 ))) {
if (ret != DB_NOTFOUND)
db->err( db, ret, "Maildir error: db->get()" );
continue;
}
uid = *(int *)value.data;
} else if ((p = strstr( e->d_name, ",U=" )))
uid = atoi( p + 3 );
else
continue;
if (nmsgs == rmsgs) {
rmsgs = rmsgs * 2 + 100;
msgs = nfrealloc( msgs, rmsgs * sizeof(msg_t) );
}
msgs[nmsgs].uid = uid;
msgs[nmsgs++].flags = parse_info( s );
}
closedir( d );
}
qsort( msgs, nmsgs, sizeof(msg_t), compare_uids );
if (!(fp = fopen( sname, "w" ))) {
perror( sname );
goto err4;
}
if (box->max_messages) {
if (!nmsgs)
i = maxuid;
else {
i = nmsgs - box->max_messages;
if (i < 0)
i = 0;
i = msgs[i].uid - 1;
}
} else
i = 0;
fprintf( fp, "%d:%d %d:%d:%d\n", uidval, maxuid, uidval, i, maxuid );
for (i = 0; i < nmsgs; i++) {
fprintf( fp, "%d %d ", msgs[i].uid, msgs[i].uid );
for (u = 0; u < as(Flags); u++)
if (msgs[i].flags & (1 << u))
fputc( Flags[u], fp );
fputc( '\n', fp );
}
fclose( fp );
if (db) {
key.data = (void *)"UIDVALIDITY";
key.size = 11;
uv[0] = uidval;
uv[1] = maxuid;
value.data = uv;
value.size = sizeof(uv);
if ((ret = db->put( db, 0, &key, &value, 0 ))) {
db->err( db, ret, "Maildir error: db->put()" );
goto err4;
}
db->close( db, 0 );
rename( iumname, diumname );
} else {
if (!(fp = fopen( uvname, "w" ))) {
perror( uvname );
goto err4;
}
fprintf( fp, "%d\n%d\n", uidval, maxuid );
fclose( fp );
}
unlink( iuvname );
unlink( imuname );
close( fd );
unlink( ilname );
if (msgs)
free( msgs );
free( mboxdir );
return;
}

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

@ -0,0 +1,319 @@
.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, write to the Free Software
\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\"
..
.TH isync 1 "2004 Mar 29"
..
.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.
..
.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-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 recent messages will not be
automatically deleted.
If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR.
(Default: 0)
..
.TP
\fBMaxSize\fR \fIbytes\fR
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 X.509 CA certificates used to verify server identities.
..
.TP
\fBUseSSLv2\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use SSLv2 for communication with the IMAP server over SSL?
(Default: \fIyes\fR if the imaps port is used, otherwise \fIno\fR)
..
.TP
\fBUseSSLv3\fR \fIyes\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 AUTHOR
Written by Michael R. Elkins <me@mutt.org>,
.br
maintained by Oswald Buddenhagen <ossi@users.sf.net>.

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

@ -0,0 +1,101 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#include <config.h>
#include <sys/types.h>
#include <stdarg.h>
#define as(ar) (sizeof(ar)/sizeof(ar[0]))
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_UNUSED __attribute__((unused))
# define ATTR_NORETURN __attribute__((noreturn))
# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
#else
# define ATTR_UNUSED
# define ATTR_NORETURN
# define ATTR_PRINTFLIKE(fmt,var)
#endif
typedef struct config {
struct config *next;
const char *server_name;
int servers;
char *host;
int port;
char *user;
char *pass;
char *tunnel;
unsigned int require_cram:1;
unsigned int require_ssl:1;
unsigned int use_imaps:1;
unsigned int use_sslv2:1;
unsigned int use_sslv3:1;
unsigned int use_tlsv1:1;
char *cert_file;
const char *store_name;
int stores;
char *copy_deleted_to;
unsigned int use_namespace:1;
const char *channel_name;
int channels;
const char *alias;
const char *box;
const char *path; /* path relative to .maildir, or absolute path */
int max_size;
unsigned int max_messages;
unsigned int expunge:1;
unsigned int delete:1;
} config_t;
extern int Quiet, Verbose, Debug;
extern const char *Home;
extern 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 ATTR_NORETURN oob( void );

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

@ -0,0 +1,55 @@
# Global configuration section
# Values here are used as defaults for any following Mailbox section that
# doesn't specify it.
# SSL server certificate file
CertificateFile /etc/ssl/certs/ca-certificates.crt
# 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

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

@ -0,0 +1,426 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "isync.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#if HAVE_GETOPT_LONG
# define _GNU_SOURCE
# include <getopt.h>
struct option Opts[] = {
{"write", 0, NULL, 'w' },
{"writeto", 0, NULL, 'W' },
{"all", 0, NULL, 'a' },
{"list", 0, NULL, 'l'},
{"config", 1, NULL, 'c'},
{"create", 0, NULL, 'C'},
{"create-local", 0, NULL, 'L'},
{"create-remote", 0, NULL, 'R'},
{"delete", 0, NULL, 'd'},
{"expunge", 0, NULL, 'e'},
{"fast", 0, NULL, 'f'},
{"help", 0, NULL, 'h'},
{"remote", 1, NULL, 'r'},
{"folder", 1, NULL, 'F'},
{"maildir", 1, NULL, 'M'},
{"one-to-one", 0, NULL, '1'},
{"inbox", 1, NULL, 'I'},
{"host", 1, NULL, 's'},
{"port", 1, NULL, 'p'},
{"debug", 0, NULL, 'D'},
{"quiet", 0, NULL, 'q'},
{"user", 1, NULL, 'u'},
{"version", 0, NULL, 'v'},
{"verbose", 0, NULL, 'V'},
{0, 0, 0, 0}
};
#endif
static void
version( void )
{
puts( PACKAGE " " VERSION );
exit( 0 );
}
static void
usage( int code )
{
fputs(
PACKAGE " " VERSION " - mbsync wrapper: IMAP4 to maildir synchronizer\n"
"Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>\n"
"Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>\n"
"usage:\n"
" " PACKAGE " [ flags ] mailbox [mailbox ...]\n"
" " PACKAGE " [ flags ] -a\n"
" " PACKAGE " [ flags ] -l\n"
" -a, --all synchronize all defined mailboxes\n"
" -l, --list list all defined mailboxes and exit\n"
" -L, --create-local create local maildir mailbox if nonexistent\n"
" -R, --create-remote create remote imap mailbox if nonexistent\n"
" -C, --create create both local and remote mailboxes if nonexistent\n"
" -d, --delete delete local msgs that don't exist on the server\n"
" -e, --expunge expunge deleted messages\n"
" -f, --fast only fetch new messages\n"
" -r, --remote BOX remote mailbox\n"
" -F, --folder DIR remote IMAP folder containing mailboxes\n"
" -M, --maildir DIR local directory containing mailboxes\n"
" -1, --one-to-one map every IMAP <folder>/box to <maildir>/box\n"
" -I, --inbox BOX map IMAP INBOX to <maildir>/BOX (exception to -1)\n"
" -s, --host HOST IMAP server address\n"
" -p, --port PORT server IMAP port\n"
" -u, --user USER IMAP user name\n"
" -c, --config CONFIG read an alternate config file (default: ~/.isyncrc)\n"
" -D, --debug print debugging messages\n"
" -V, --verbose verbose mode (display network traffic)\n"
" -q, --quiet don't display progress info\n"
" -v, --version display version\n"
" -h, --help display this help message\n\n"
"Note that this is a wrapper binary only; the \"real\" isync is named \"mbsync\".\n"
"Options to permanently transform your old isync configuration:\n"
" -w, --write write permanent mbsync configuration\n"
" -W, --writeto FILE write permanent mbsync configuration to FILE\n",
code ? stderr : stdout );
exit( code );
}
static const char *
strrstr( const char *h, const char *n )
{
char *p = strstr( h, n );
if (!p)
return 0;
do {
h = p;
p = strstr( h + 1, n );
} while (p);
return h;
}
static void
add_arg( char ***args, const char *arg )
{
int nu = 0;
if (*args)
for (; (*args)[nu]; nu++);
*args = nfrealloc( *args, sizeof(char *) * (nu + 2));
(*args)[nu] = nfstrdup( arg );
(*args)[nu + 1] = 0;
}
#define OP_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 */
global.port = 143;
global.box = "INBOX";
global.use_namespace = 1;
global.require_ssl = 1;
global.use_tlsv1 = 1;
folder = "";
maildir = "~";
xmaildir = Home;
#define FLAGS "wW:alCLRc:defhp:qu:r:F:M:1I:s:vVD"
mod = all = list = ops = writeout = Quiet = Verbose = Debug = 0;
#if HAVE_GETOPT_LONG
while ((i = getopt_long( argc, argv, FLAGS, Opts, NULL )) != -1)
#else
while ((i = getopt( argc, argv, FLAGS )) != -1)
#endif
{
switch (i) {
case 'W':
outconfig = optarg;
/* plopp */
case 'w':
writeout = 1;
break;
case 'l':
list = 1;
/* plopp */
case 'a':
all = 1;
break;
case '1':
o2o = 1;
mod = 1;
break;
case 'C':
ops |= OP_CREATE_REMOTE|OP_CREATE_LOCAL;
break;
case 'L':
ops |= OP_CREATE_LOCAL;
break;
case 'R':
ops |= OP_CREATE_REMOTE;
break;
case 'c':
config = optarg;
break;
case 'd':
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':
#if HAVE_LIBSSL
if (!strncasecmp( "imaps:", optarg, 6 )) {
global.use_imaps = 1;
global.port = 993;
global.use_sslv2 = 1;
global.use_sslv3 = 1;
optarg += 6;
}
#endif
global.host = optarg;
mod = 1;
break;
case 'u':
global.user = optarg;
mod = 1;
break;
case 'D':
Debug = 1;
break;
case 'V':
Verbose = 1;
break;
case 'q':
Quiet++;
break;
case 'v':
version();
case 'h':
usage( 0 );
default:
usage( 1 );
}
}
if (config) {
if (*config != '/') {
if (!getcwd( path1, sizeof(path1) )) {
fprintf( stderr, "Can't obtain working directory\n" );
return 1;
}
pl = strlen( path1 );
nfsnprintf( path1 + pl, sizeof(path1) - pl, "/%s", config );
config = path1;
}
} else {
nfsnprintf( path1, sizeof(path1), "%s/.isyncrc", Home );
config = path1;
}
stor = &boxes;
load_config( config, &stor );
if (!all && !o2o)
for (i = optind; argv[i]; i++)
if (!(box = find_box( argv[i] ))) {
box = nfmalloc( sizeof(config_t) );
memcpy( box, &global, sizeof(config_t) );
box->path = argv[i];
*stor = box;
stor = &box->next;
mod = 1;
}
if (writeout) {
all = 1;
if (mod)
fprintf( stderr,
"Warning: command line switches that influence the resulting config file\n"
"have been supplied.\n" );
} else {
if (!argv[optind] && !all) {
fprintf( stderr, "No mailbox specified. Try isync -h\n" );
return 1;
}
}
if (all) {
if (o2o) {
DIR * dir;
struct dirent *de;
if (!(dir = opendir( xmaildir ))) {
fprintf( stderr, "%s: %s\n", xmaildir, strerror(errno) );
return 1;
}
while ((de = readdir( dir ))) {
if (*de->d_name == '.')
continue;
nfsnprintf( path1, sizeof(path1), "%s/%s/cur", xmaildir, de->d_name );
if (stat( path1, &st ) || !S_ISDIR( st.st_mode ))
continue;
global.path = de->d_name;
global.box = (inbox && !strcmp( inbox, global.path )) ?
"INBOX" : global.path;
convert( &global );
}
closedir( dir );
} else
for (box = boxes; box; box = box->next)
convert( box );
} else {
for (i = optind; argv[i]; i++)
if (o2o) {
global.path = argv[i];
global.box =
(inbox && !strcmp( global.path, inbox )) ?
"INBOX" : global.path;
convert( &global );
} else
convert( find_box( argv[i] ) );
}
if (writeout) {
if (!outconfig) {
const char *p = strrchr( config, '/' );
if (!p)
p = config;
p = strrstr( p, "isync" );
if (!p)
nfsnprintf( path2, sizeof(path2), "%s.mbsync", config );
else
nfsnprintf( path2, sizeof(path2), "%.*smb%s", p - config, config, p + 1 );
outconfig = path2;
}
if ((fd = creat( outconfig, 0666 )) < 0) {
fprintf( stderr, "Error: cannot write new config %s: %s\n", outconfig, strerror(errno) );
return 1;
}
} else {
strcpy( path2, "/tmp/mbsyncrcXXXXXX" );
if ((fd = mkstemp( path2 )) < 0) {
fprintf( stderr, "Can't create temp file\n" );
return 1;
}
}
write_config( fd );
if (writeout)
return 0;
args = 0;
add_arg( &args, "mbsync" );
if (Verbose)
add_arg( &args, "-V" );
if (Debug)
add_arg( &args, "-D" );
for (; Quiet; Quiet--)
add_arg( &args, "-q" );
add_arg( &args, "-cT" );
add_arg( &args, path2 );
if (ops & OP_FAST)
add_arg( &args, "-Ln" );
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 );
perror( args[0] );
return 1;
}

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

@ -0,0 +1,154 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "isync.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pwd.h>
#include <ctype.h>
char *
next_arg( char **s )
{
char *ret;
if (!s || !*s)
return 0;
while (isspace( (unsigned char) **s ))
(*s)++;
if (!**s) {
*s = 0;
return 0;
}
if (**s == '"') {
++*s;
ret = *s;
*s = strchr( *s, '"' );
} else {
ret = *s;
while (**s && !isspace( (unsigned char) **s ))
(*s)++;
}
if (*s) {
if (**s)
*(*s)++ = 0;
if (!**s)
*s = 0;
}
return ret;
}
#ifndef HAVE_VASPRINTF
static int
vasprintf( char **strp, const char *fmt, va_list ap )
{
int len;
char tmp[1024];
if ((len = vsnprintf( tmp, sizeof(tmp), fmt, ap )) < 0 || !(*strp = malloc( len + 1 )))
return -1;
if (len >= (int)sizeof(tmp))
vsprintf( *strp, fmt, ap );
else
memcpy( *strp, tmp, len + 1 );
return len;
}
#endif
void
oob( void )
{
fputs( "Fatal: buffer too small. Please report a bug.\n", stderr );
abort();
}
int
nfsnprintf( char *buf, int blen, const char *fmt, ... )
{
int ret;
va_list va;
va_start( va, fmt );
if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
oob();
va_end( va );
return ret;
}
static void ATTR_NORETURN
oom( void )
{
fputs( "Fatal: Out of memory\n", stderr );
abort();
}
void *
nfmalloc( size_t sz )
{
void *ret;
if (!(ret = malloc( sz )))
oom();
return ret;
}
void *
nfrealloc( void *mem, size_t sz )
{
char *ret;
if (!(ret = realloc( mem, sz )) && sz)
oom();
return ret;
}
char *
nfstrdup( const char *str )
{
char *ret;
if (!(ret = strdup( str )))
oom();
return ret;
}
int
nfvasprintf( char **str, const char *fmt, va_list va )
{
int ret = vasprintf( str, fmt, va );
if (ret < 0)
oom();
return ret;
}
int
nfasprintf( char **str, const char *fmt, ... )
{
int ret;
va_list va;
va_start( va, fmt );
ret = nfvasprintf( str, fmt, va );
va_end( va );
return ret;
}

View file

@ -1,7 +1,7 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2006,2011 Oswald Buddenhagen <ossi@users.sf.net>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -14,79 +14,31 @@
* 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/>.
*
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* As a special exception, mbsync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
#include "config.h"
#include "isync.h"
#include "sync.h"
#include <assert.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <pwd.h>
#include <sys/types.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
static store_conf_t *stores;
store_conf_t *stores;
channel_conf_t *channels;
group_conf_t *groups;
int global_mops, global_sops;
char *global_sync_state;
char *
get_arg( conffile_t *cfile, int required, int *comment )
{
char *ret, *p, *t;
int escaped, quoted;
char c;
p = cfile->rest;
assert( p );
while ((c = *p) && isspace( (uchar)c ))
p++;
if (!c || c == '#') {
if (comment)
*comment = (c == '#');
if (required) {
error( "%s:%d: parameter missing\n", cfile->file, cfile->line );
cfile->err = 1;
}
ret = NULL;
} else {
for (escaped = 0, quoted = 0, ret = t = p; c; c = *p) {
p++;
if (escaped && c >= 32) {
escaped = 0;
*t++ = c;
} else if (c == '\\')
escaped = 1;
else if (c == '"')
quoted ^= 1;
else if (!quoted && isspace( (uchar)c ))
break;
else
*t++ = c;
}
*t = 0;
if (escaped) {
error( "%s:%d: unterminated escape sequence\n", cfile->file, cfile->line );
cfile->err = 1;
ret = NULL;
}
if (quoted) {
error( "%s:%d: missing closing quote\n", cfile->file, cfile->line );
cfile->err = 1;
ret = NULL;
}
}
cfile->rest = p;
return ret;
}
char
int
parse_bool( conffile_t *cfile )
{
if (!strcasecmp( cfile->val, "yes" ) ||
@ -97,11 +49,9 @@ parse_bool( conffile_t *cfile )
if (strcasecmp( cfile->val, "no" ) &&
strcasecmp( cfile->val, "false" ) &&
strcasecmp( cfile->val, "off" ) &&
strcmp( cfile->val, "0" )) {
error( "%s:%d: invalid boolean value '%s'\n",
cfile->file, cfile->line, cfile->val );
cfile->err = 1;
}
strcmp( cfile->val, "0" ))
fprintf( stderr, "%s:%d: invalid boolean value '%s'\n",
cfile->file, cfile->line, cfile->val );
return 0;
}
@ -110,24 +60,23 @@ parse_int( conffile_t *cfile )
{
char *p;
int ret;
ret = strtol( cfile->val, &p, 10 );
if (*p) {
error( "%s:%d: invalid integer value '%s'\n",
cfile->file, cfile->line, cfile->val );
cfile->err = 1;
fprintf( stderr, "%s:%d: invalid integer value '%s'\n",
cfile->file, cfile->line, cfile->val );
return 0;
}
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')
@ -137,26 +86,15 @@ parse_size( conffile_t *cfile )
if (*p) {
fprintf (stderr, "%s:%d: invalid size '%s'\n",
cfile->file, cfile->line, cfile->val);
cfile->err = 1;
return 0;
}
return ret;
}
static const struct {
int op;
const char *name;
} boxOps[] = {
{ OP_EXPUNGE, "Expunge" },
{ OP_CREATE, "Create" },
{ OP_REMOVE, "Remove" },
};
static int
getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
static int
getopt_helper( conffile_t *cfile, int *cops, int *mops, int *sops, char **sync_state )
{
char *arg;
uint i;
if (!strcasecmp( "Sync", cfile->cmd )) {
arg = cfile->val;
@ -174,91 +112,81 @@ 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;
*sops |= OP_RENEW;
else if (!strcasecmp( "PullNew", arg ))
conf->ops[N] |= OP_NEW;
*sops |= OP_NEW;
else if (!strcasecmp( "PullDelete", arg ))
conf->ops[N] |= OP_DELETE;
*sops |= OP_DELETE;
else if (!strcasecmp( "PullFlags", arg ))
conf->ops[N] |= OP_FLAGS;
*sops |= OP_FLAGS;
else if (!strcasecmp( "PushReNew", arg ))
conf->ops[F] |= OP_RENEW;
*mops |= OP_RENEW;
else if (!strcasecmp( "PushNew", arg ))
conf->ops[F] |= OP_NEW;
*mops |= OP_NEW;
else if (!strcasecmp( "PushDelete", arg ))
conf->ops[F] |= OP_DELETE;
*mops |= OP_DELETE;
else if (!strcasecmp( "PushFlags", arg ))
conf->ops[F] |= OP_FLAGS;
*mops |= OP_FLAGS;
else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg ))
*cops |= XOP_PULL|XOP_PUSH;
else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg )) {
error( "%s:%d: invalid Sync arg '%s'\n",
cfile->file, cfile->line, arg );
cfile->err = 1;
}
while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
conf->ops[F] |= XOP_HAVE_TYPE;
else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg ))
fprintf( stderr, "%s:%d: invalid Sync arg '%s'\n",
cfile->file, cfile->line, arg );
while ((arg = next_arg( &cfile->rest )));
*mops |= XOP_HAVE_TYPE;
} else if (!strcasecmp( "Expunge", cfile->cmd )) {
arg = cfile->val;
do
if (!strcasecmp( "Both", arg ))
*cops |= OP_EXPUNGE;
else if (!strcasecmp( "Master", arg ))
*mops |= OP_EXPUNGE;
else if (!strcasecmp( "Slave", arg ))
*sops |= OP_EXPUNGE;
else if (strcasecmp( "None", arg ))
fprintf( stderr, "%s:%d: invalid Expunge arg '%s'\n",
cfile->file, cfile->line, arg );
while ((arg = next_arg( &cfile->rest )));
*mops |= XOP_HAVE_EXPUNGE;
} else if (!strcasecmp( "Create", cfile->cmd )) {
arg = cfile->val;
do
if (!strcasecmp( "Both", arg ))
*cops |= OP_CREATE;
else if (!strcasecmp( "Master", arg ))
*mops |= OP_CREATE;
else if (!strcasecmp( "Slave", arg ))
*sops |= OP_CREATE;
else if (strcasecmp( "None", arg ))
fprintf( stderr, "%s:%d: invalid Create arg '%s'\n",
cfile->file, cfile->line, arg );
while ((arg = next_arg( &cfile->rest )));
*mops |= XOP_HAVE_CREATE;
} else if (!strcasecmp( "SyncState", cfile->cmd ))
conf->sync_state = expand_strdup( cfile->val );
else if (!strcasecmp( "CopyArrivalDate", cfile->cmd ))
conf->use_internal_date = parse_bool( cfile );
else if (!strcasecmp( "MaxMessages", cfile->cmd ))
conf->max_messages = parse_int( cfile );
else if (!strcasecmp( "ExpireUnread", cfile->cmd ))
conf->expire_unread = parse_bool( cfile );
else {
for (i = 0; i < as(boxOps); i++) {
if (!strcasecmp( boxOps[i].name, cfile->cmd )) {
int op = boxOps[i].op;
arg = cfile->val;
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( "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);
return 1;
}
}
*sync_state = expand_strdup( cfile->val );
else
return 0;
}
return 1;
}
int
getcline( conffile_t *cfile )
{
char *arg;
int comment;
char *p;
if (cfile->rest && (arg = get_arg( cfile, ARG_OPTIONAL, NULL ))) {
error( "%s:%d: excess token '%s'\n", cfile->file, cfile->line, arg );
cfile->err = 1;
}
while (fgets( cfile->buf, cfile->bufl, cfile->fp )) {
cfile->line++;
cfile->rest = cfile->buf;
if (!(cfile->cmd = get_arg( cfile, ARG_OPTIONAL, &comment ))) {
if (comment)
continue;
p = cfile->buf;
if (!(cfile->cmd = next_arg( &p )))
return 1;
}
if (!(cfile->val = get_arg( cfile, ARG_REQUIRED, NULL )))
if (*cfile->cmd == '#')
continue;
if (!(cfile->val = next_arg( &p ))) {
fprintf( stderr, "%s:%d: parameter missing\n",
cfile->file, cfile->line );
continue;
}
cfile->rest = p;
return 1;
}
return 0;
@ -266,30 +194,29 @@ getcline( conffile_t *cfile )
/* XXX - this does not detect None conflicts ... */
int
merge_ops( int cops, int ops[] )
merge_ops( int cops, int *mops, int *sops )
{
int aops, op;
uint i;
int aops;
aops = ops[F] | ops[N];
if (ops[F] & XOP_HAVE_TYPE) {
aops = *mops | *sops;
if (*mops & XOP_HAVE_TYPE) {
if (aops & OP_MASK_TYPE) {
if (aops & cops & OP_MASK_TYPE) {
cfl:
error( "Conflicting Sync args specified.\n" );
fprintf( stderr, "Conflicting Sync args specified.\n" );
return 1;
}
ops[F] |= cops & OP_MASK_TYPE;
ops[N] |= cops & OP_MASK_TYPE;
*mops |= cops & OP_MASK_TYPE;
*sops |= cops & OP_MASK_TYPE;
if (cops & XOP_PULL) {
if (ops[N] & OP_MASK_TYPE)
if (*sops & OP_MASK_TYPE)
goto cfl;
ops[N] |= OP_MASK_TYPE;
*sops |= OP_MASK_TYPE;
}
if (cops & XOP_PUSH) {
if (ops[F] & OP_MASK_TYPE)
if (*mops & OP_MASK_TYPE)
goto cfl;
ops[F] |= OP_MASK_TYPE;
*mops |= OP_MASK_TYPE;
}
} else if (cops & (OP_MASK_TYPE|XOP_MASK_DIR)) {
if (!(cops & OP_MASK_TYPE))
@ -297,36 +224,40 @@ 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;
*sops |= cops & OP_MASK_TYPE;
if (cops & XOP_PUSH)
ops[F] |= cops & OP_MASK_TYPE;
*mops |= 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 (aops & cops & op) {
error( "Conflicting %s args specified.\n", boxOps[i].name );
return 1;
}
ops[F] |= cops & op;
ops[N] |= cops & op;
if (*mops & XOP_HAVE_EXPUNGE) {
if (aops & cops & OP_EXPUNGE) {
fprintf( stderr, "Conflicting Expunge args specified.\n" );
return 1;
}
*mops |= cops & OP_EXPUNGE;
*sops |= cops & OP_EXPUNGE;
}
if (*mops & XOP_HAVE_CREATE) {
if (aops & cops & OP_CREATE) {
fprintf( stderr, "Conflicting Create args specified.\n" );
return 1;
}
*mops |= cops & OP_CREATE;
*sops |= cops & OP_CREATE;
}
return 0;
}
int
load_config( const char *where )
load_config( const char *where, int pseudo )
{
conffile_t cfile;
store_conf_t *store, **storeapp = &stores;
store_conf_t *store, **storeapp = &stores, **sptarg;
channel_conf_t *channel, **channelapp = &channels;
group_conf_t *group, **groupapp = &groups;
string_list_t *chanlist, **chanlistapp;
char *arg, *p;
uint len, max_size;
int cops, gcops, glob_ok, fn, i;
char *arg, *p, **ntarg;
int err, len, cops, gcops, max_size;
char path[_POSIX_PATH_MAX];
char buf[1024];
@ -336,119 +267,101 @@ load_config( const char *where )
} 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 );
perror( "Cannot open config file" );
return 1;
}
buf[sizeof(buf) - 1] = 0;
cfile.buf = buf;
cfile.bufl = sizeof(buf) - 1;
cfile.line = 0;
cfile.err = 0;
cfile.ms_warn = 0;
cfile.rest = NULL;
gcops = 0;
glob_ok = 1;
global_conf.expire_unread = -1;
gcops = err = 0;
reloop:
while (getcline( &cfile )) {
if (!cfile.cmd)
continue;
for (i = 0; i < N_DRIVERS; i++)
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 = "";
*storeapp = store;
storeapp = &store->next;
*storeapp = NULL;
}
glob_ok = 0;
goto reloop;
if (imap_driver.parse_store( &cfile, &store, &err ) ||
maildir_driver.parse_store( &cfile, &store, &err ))
{
if (store) {
if (!store->path)
store->path = "";
*storeapp = store;
storeapp = &store->next;
*storeapp = 0;
}
if (!strcasecmp( "Channel", cfile.cmd ))
}
else if (!strcasecmp( "Channel", cfile.cmd ))
{
channel = nfcalloc( sizeof(*channel) );
channel->name = nfstrdup( cfile.val );
channel->max_messages = global_conf.max_messages;
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 );
else if (!strcasecmp( "MaxMessages", cfile.cmd ))
channel->max_messages = parse_int( &cfile );
else if (!strcasecmp( "Pattern", cfile.cmd ) ||
!strcasecmp( "Patterns", cfile.cmd ))
{
arg = cfile.val;
do
add_string_list( &channel->patterns, arg );
while ((arg = get_arg( &cfile, ARG_OPTIONAL, NULL )));
while ((arg = next_arg( &cfile.rest )));
}
else if (!strcasecmp( "Far", cfile.cmd )) {
fn = F;
else if (!strcasecmp( "Master", cfile.cmd )) {
sptarg = &channel->master;
ntarg = &channel->master_name;
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 )) {
sptarg = &channel->slave;
ntarg = &channel->slave_name;
linkst:
if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) {
error( "%s:%d: malformed mailbox spec\n",
cfile.file, cfile.line );
cfile.err = 1;
fprintf( stderr, "%s:%d: malformed mailbox spec\n",
cfile.file, cfile.line );
err = 1;
continue;
}
*p = 0;
for (store = stores; store; store = store->next)
if (!strcmp( store->name, cfile.val + 1 )) {
channel->stores[fn] = store;
memset( sptarg, 0, sizeof(*sptarg) );
*sptarg = store;
goto stpcom;
}
error( "%s:%d: unknown store '%s'\n",
cfile.file, cfile.line, cfile.val + 1 );
cfile.err = 1;
fprintf( stderr, "%s:%d: unknown store '%s'\n",
cfile.file, cfile.line, cfile.val + 1 );
err = 1;
continue;
stpcom:
if (*++p)
channel->boxes[fn] = 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 );
cfile.err = 1;
*ntarg = nfstrdup( p );
} else if (!getopt_helper( &cfile, &cops, &channel->mops, &channel->sops, &channel->sync_state )) {
fprintf( stderr, "%s:%d: unknown keyword '%s'\n",
cfile.file, cfile.line, cfile.cmd );
err = 1;
}
}
if (!channel->stores[F]) {
error( "channel '%s' refers to no far side store\n", channel->name );
cfile.err = 1;
} else if (!channel->stores[N]) {
error( "channel '%s' refers to no near side store\n", channel->name );
cfile.err = 1;
} else if (merge_ops( cops, channel->ops ))
cfile.err = 1;
if (!channel->master) {
fprintf( stderr, "channel '%s' refers to no master store\n", channel->name );
err = 1;
} else if (!channel->slave) {
fprintf( stderr, "channel '%s' refers to no slave store\n", channel->name );
err = 1;
} else if (merge_ops( cops, &channel->mops, &channel->sops ))
err = 1;
else {
if (max_size != UINT_MAX) {
if (!max_size)
max_size = UINT_MAX;
channel->stores[F]->max_size = channel->stores[N]->max_size = max_size;
}
if (max_size >= 0)
channel->master->max_size = channel->slave->max_size = max_size;
*channelapp = channel;
channelapp = &channel->next;
}
glob_ok = 0;
goto reloop;
}
else if (!strcasecmp( "Group", cfile.cmd ))
{
@ -456,81 +369,74 @@ 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;
p = cfile.rest;
while ((arg = next_arg( &p ))) {
addone:
len = strlen( arg );
chanlist = nfmalloc( sizeof(*chanlist) + len );
memcpy( chanlist->string, arg, len + 1 );
*chanlistapp = chanlist;
chanlistapp = &chanlist->next;
*chanlistapp = 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 ))
{
p = cfile.rest;
arg = cfile.val;
goto addone;
}
else
{
error( "%s:%d: keyword '%s' is not recognized in Group sections\n",
cfile.file, cfile.line, cfile.cmd );
cfile.err = 1;
fprintf( stderr, "%s:%d: unknown keyword '%s'\n",
cfile.file, cfile.line, cfile.cmd );
err = 1;
}
}
glob_ok = 0;
goto reloop;
break;
}
else if (!strcasecmp( "FSync", cfile.cmd ))
else if (!getopt_helper( &cfile, &gcops, &global_mops, &global_sops, &global_sync_state ))
{
UseFSync = parse_bool( &cfile );
}
else if (!strcasecmp( "FieldDelimiter", cfile.cmd ))
{
if (strlen( cfile.val ) != 1) {
error( "%s:%d: Field delimiter must be exactly one character long\n", cfile.file, cfile.line );
cfile.err = 1;
} else {
FieldDelimiter = cfile.val[0];
if (!ispunct( FieldDelimiter )) {
error( "%s:%d: Field delimiter must be a punctuation character\n", cfile.file, cfile.line );
cfile.err = 1;
}
}
}
else if (!strcasecmp( "BufferLimit", cfile.cmd ))
{
BufferLimit = parse_size( &cfile );
if (!BufferLimit) {
error( "%s:%d: BufferLimit cannot be zero\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",
cfile.file, cfile.line, cfile.cmd );
cfile.err = 1;
fprintf( stderr, "%s:%d: unknown section keyword '%s'\n",
cfile.file, cfile.line, cfile.cmd );
err = 1;
while (getcline( &cfile ))
if (!cfile.cmd)
goto reloop;
break;
}
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 "/" );
return cfile.err;
err |= merge_ops( gcops, &global_mops, &global_sops );
if (!global_sync_state)
global_sync_state = expand_strdup( "~/." EXE "/" );
if (!err && pseudo)
unlink( where );
return err;
}
void
parse_generic_store( store_conf_t *store, conffile_t *cfg, int *err )
{
if (!strcasecmp( "Trash", cfg->cmd ))
store->trash = nfstrdup( cfg->val );
else if (!strcasecmp( "TrashRemoteNew", cfg->cmd ))
store->trash_remote_new = parse_bool( cfg );
else if (!strcasecmp( "TrashNewOnly", cfg->cmd ))
store->trash_only_new = parse_bool( cfg );
else if (!strcasecmp( "MaxSize", cfg->cmd ))
store->max_size = parse_size( cfg );
else if (!strcasecmp( "MapInbox", cfg->cmd ))
store->map_inbox = nfstrdup( cfg->val );
else {
fprintf( stderr, "%s:%d: unknown keyword '%s'\n",
cfg->file, cfg->line, cfg->cmd );
*err = 1;
}
}

View file

@ -1,51 +0,0 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* 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
* 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.
*/
#ifndef CONFIG_H
#define CONFIG_H
#include "common.h"
typedef struct {
const char *file;
FILE *fp;
char *buf;
int bufl;
int line;
int err;
int ms_warn;
char *cmd, *val, *rest;
} conffile_t;
#define ARG_OPTIONAL 0
#define ARG_REQUIRED 1
char *get_arg( conffile_t *cfile, int required, int *comment );
char parse_bool( conffile_t *cfile );
int parse_int( conffile_t *cfile );
uint parse_size( conffile_t *cfile );
int getcline( conffile_t *cfile );
int merge_ops( int cops, int ops[] );
int load_config( const char *filename );
#endif

View file

@ -1,78 +0,0 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* 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
* 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 <stdlib.h>
#include <string.h>
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 )
{
message_t *tmsg;
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 )
{
if (!strcasecmp( "Trash", cfg->cmd )) {
store->trash = nfstrdup( cfg->val );
} else if (!strcasecmp( "TrashRemoteNew", cfg->cmd )) {
store->trash_remote_new = parse_bool( cfg );
} else if (!strcasecmp( "TrashNewOnly", cfg->cmd )) {
store->trash_only_new = parse_bool( cfg );
} else if (!strcasecmp( "MaxSize", cfg->cmd )) {
store->max_size = parse_size( cfg );
} else if (!strcasecmp( "MapInbox", cfg->cmd )) {
store->map_inbox = nfstrdup( cfg->val );
} else if (!strcasecmp( "Flatten", cfg->cmd )) {
const char *p;
for (p = cfg->val; *p; p++) {
if (*p == '/') {
error( "%s:%d: flattened hierarchy delimiter cannot contain the canonical delimiter '/'\n", cfg->file, cfg->line );
cfg->err = 1;
return;
}
}
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 );
cfg->err = 1;
}
}

View file

@ -1,302 +0,0 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* 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
* 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.
*/
#ifndef DRIVER_H
#define DRIVER_H
#include "config.h"
typedef struct driver driver_t;
#define FAIL_TEMP 0 /* Retry immediately (also: no error) */
#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
} store_conf_t;
/* For message->flags */
/* Keep the mailbox driver flag definitions in sync: */
/* grep for MAILBOX_DRIVER_FLAG */
/* 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
/* 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)
} 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)
#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)
} store_t;
typedef struct {
char *data;
uint len;
time_t date;
uchar flags;
} msg_data_t;
#define DRV_OK 0
/* Message went missing, or mailbox is full, etc. */
#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
/* 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,
not that it must. The lack of it OTOH implies that it CANNOT,
and as CRLF is the canonical format, we convert.
*/
#define DRV_CRLF 1
/*
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.
struct driver {
/* Return driver capabilities. */
xint (*get_caps)( store_t *ctx );
/* Parse configuration. */
int (*parse_store)( conffile_t *cfg, store_conf_t **storep );
/* Close remaining server connections. All stores must be discarded 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 );
/* 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 );
/* 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. */
void (*list_store)( store_t *ctx, int flags,
void (*cb)( int sts, string_list_t *boxes, void *aux ), void *aux );
/* Invoked before open_box(), this informs the driver which box is to be opened. */
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 );
/* 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 );
/* Confirm that the open mailbox is empty. */
int (*confirm_box_empty)( store_t *ctx );
/* Delete the open mailbox. The mailbox is expected to be empty.
* Subfolders of the mailbox are *not* deleted.
* Some artifacts of the mailbox may remain, but they won't be
* recognized as a mailbox any more. */
void (*delete_box)( store_t *ctx,
void (*cb)( int sts, void *aux ), void *aux );
/* Remove the last artifacts of the open mailbox, as far as possible. */
int (*finish_delete_box)( store_t *ctx );
/* 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 );
/* 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 );
/* 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,
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. */
void (*store_msg)( store_t *ctx, msg_data_t *data, int to_trash,
void (*cb)( int sts, uint 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 );
/* 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 (*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 (*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 (*cb)( int sts, void *aux ), void *aux );
/* Cancel queued commands which are not in flight yet; they will have their
* callbacks invoked with DRV_CANCELED. Afterwards, wait for the completion of
* the in-flight commands. If the store is canceled before this command completes,
* the callback will *not* be invoked. */
void (*cancel_cmds)( store_t *ctx,
void (*cb)( void *aux ), void *aux );
/* Commit any pending set_msg_flags() commands. */
void (*commit_cmds)( store_t *ctx );
/* Get approximate amount of memory occupied by the driver. */
uint (*get_memory_usage)( store_t *ctx );
/* Get the FAIL_* state of the driver. */
int (*get_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 );
#define N_DRIVERS 2
extern driver_t *drivers[N_DRIVERS];
extern driver_t maildir_driver, imap_driver, proxy_driver;
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,198 +0,0 @@
#!/usr/bin/perl
#
# mbsync - mailbox synchronizer
# Copyright (C) 2017 Oswald Buddenhagen <ossi@users.sf.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, mbsync may be linked with the OpenSSL library,
# despite that library's more restrictive license.
#
use strict;
use warnings;
die("Usage: $0 driver.h drv_proxy.c drv_proxy.inc\n")
if ($#ARGV != 2);
my ($in_header, $in_source, $out_source) = @ARGV;
my %templates;
my %defines;
my %excluded;
my %special;
open(my $ins, $in_source) or die("Cannot open $in_source: $!\n");
my $template;
my $define;
my $conts;
while (<$ins>) {
if ($template) {
if (/^\/\/\# END$/) {
$templates{$template} = $conts;
$template = undef;
} else {
$conts .= $_;
}
} elsif ($define) {
if (/^\/\/\# END$/) {
$defines{$define} = $conts;
$define = undef;
} else {
($_ eq "\n") or s/^\t// or die("DEFINE content is not indented: $_");
$conts .= $_;
}
} else {
if (/^\/\/\# TEMPLATE (\w+)$/) {
$template = $1;
$conts = "";
} elsif (/^\/\/\# DEFINE (\w+)$/) {
$define = $1;
$conts = "";
} elsif (/^\/\/\# DEFINE (\w+) (.*)$/) {
$defines{$1} = $2;
} elsif (/^\/\/\# UNDEFINE (\w+)$/) {
$defines{$1} = "";
} elsif (/^\/\/\# EXCLUDE (\w+)$/) {
$excluded{$1} = 1;
} elsif (/^\/\/\# SPECIAL (\w+)$/) {
$special{$1} = 1;
}
}
}
close($ins);
open(my $inh, $in_header) or die("Cannot open $in_header: $!\n");
my $sts = 0;
my $cont = "";
while (<$inh>) {
if ($sts == 0) {
if (/^struct driver \{$/) {
$sts = 1;
}
} elsif ($sts == 1) {
if (/^\};$/) {
$sts = 0;
} else {
$cont .= $_;
}
}
}
close($inh);
$cont =~ s,\n, ,g;
$cont =~ s,/\*.*?\*/, ,g;
$cont =~ s,\h+, ,g;
my @ptypes = map { s,^ ,,r } split(/;/, $cont);
pop @ptypes; # last one is empty
my @cmd_table;
sub make_args($)
{
$_ = shift;
s/(?:^|(?<=, ))(?:const )?\w+ \*?//g;
return $_;
}
sub type_to_format($)
{
$_ = shift;
s/xint /\%\#x/g;
s/uint /\%u/g;
s/int /\%d/g;
s/const char \*/\%s/g;
return $_;
}
sub make_format($)
{
$_ = type_to_format(shift);
s/, (\%\#?.)(\w+)/, $2=$1/g;
return $_;
}
sub indent($$)
{
my ($str, $indent) = @_;
return $str =~ s,^(?=.),$indent,smgr;
}
open(my $outh, ">".$out_source) or die("Cannot create $out_source: $!\n");
for (@ptypes) {
/^([\w* ]+)\(\*(\w+)\)\( (.*) \)$/ or die("Cannot parse prototype '$_'\n");
my ($cmd_type, $cmd_name, $cmd_args) = ($1, $2, $3);
if (defined($excluded{$cmd_name})) {
push @cmd_table, "NULL";
next;
}
push @cmd_table, "proxy_$cmd_name";
next if (defined($special{$cmd_name}));
my %replace;
$replace{'name'} = $cmd_name;
$replace{'type'} = $cmd_type;
$cmd_args =~ s/^store_t \*ctx// or die("Arguments '$cmd_args' don't start with 'store_t *ctx'\n");
if ($cmd_name =~ /^get_/) {
$template = "GETTER";
$replace{'fmt'} = type_to_format($cmd_type);
} else {
if ($cmd_type eq "void " && $cmd_args =~ s/, void \(\*cb\)\( (.*)void \*aux \), void \*aux$//) {
my $cmd_cb_args = $1;
if (length($cmd_cb_args)) {
$replace{'decl_cb_args'} = $cmd_cb_args;
my $r_cmd_cb_args = $cmd_cb_args;
$r_cmd_cb_args =~ s/^int sts, // or die("Callback arguments of $cmd_name don't start with sts.\n");
$replace{'decl_cb_state'} = $r_cmd_cb_args =~ s/, /\;\n/gr;
my $pass_cb_args = make_args($cmd_cb_args);
$replace{'save_cb_args'} = $pass_cb_args =~ s/([^,]+), /cmd->$1 = $1\;\n/gr;
$pass_cb_args =~ s/([^, ]+)/cmd->$1/g;
$replace{'pass_cb_args'} = $pass_cb_args;
$replace{'print_pass_cb_args'} = $pass_cb_args =~ s/(.*), $/, $1/r;
$replace{'print_fmt_cb_args'} = make_format($cmd_cb_args =~ s/(.*), $/, $1/r);
$replace{'gen_cmd_t'} = "gen_sts_cmd_t";
$replace{'GEN_CMD'} = "GEN_STS_CMD\n";
$replace{'gen_cmd'} = "&cmd->gen.gen";
} else {
$replace{'gen_cmd_t'} = "gen_cmd_t";
$replace{'GEN_CMD'} = "GEN_CMD\n";
$replace{'gen_cmd'} = "&cmd->gen";
}
$replace{'checked'} //= '0';
$template = "CALLBACK";
} elsif ($cmd_type eq "void ") {
$template = "REGULAR_VOID";
} else {
$template = "REGULAR";
$replace{'fmt'} = type_to_format($cmd_type);
}
$replace{'decl_args'} = $cmd_args;
$replace{'print_pass_args'} = $replace{'pass_args'} = make_args($cmd_args);
$replace{'print_fmt_args'} = make_format($cmd_args);
}
for (keys %defines) {
$replace{$1} = delete $defines{$_} if (/^${cmd_name}_(.*)$/);
}
my %used;
my $text = $templates{$template};
$text =~ s/^(\h*)\@(\w+)\@\n/$used{$2} = 1; indent($replace{$2} \/\/ "", $1)/smeg;
$text =~ s/\@(\w+)\@/$used{$1} = 1; $replace{$1} \/\/ ""/eg;
print $outh $text."\n";
my @not_used = grep { !defined($used{$_}) } keys %replace;
die("Fatal: unconsumed replacements in $cmd_name: ".join(" ", @not_used)."\n") if (@not_used);
}
die("Fatal: unconsumed DEFINEs: ".join(" ", keys %defines)."\n") if (%defines);
print $outh "struct driver proxy_driver = {\n".join("", map { "\t$_,\n" } @cmd_table)."};\n";
close $outh;

250
src/isync.h Normal file
View file

@ -0,0 +1,250 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* As a special exception, mbsync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
#define _GNU_SOURCE
#include <config.h>
#include <sys/types.h>
#include <stdarg.h>
#include <stdio.h>
#define as(ar) (sizeof(ar)/sizeof(ar[0]))
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_UNUSED __attribute__((unused))
# define ATTR_NORETURN __attribute__((noreturn))
# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
#else
# define ATTR_UNUSED
# define ATTR_NORETURN
# define ATTR_PRINTFLIKE(fmt,var)
#endif
#define EXE "mbsync"
typedef struct {
const char *file;
FILE *fp;
char *buf;
int bufl;
int line;
char *cmd, *val, *rest;
} conffile_t;
#define OP_NEW (1<<0)
#define OP_RENEW (1<<1)
#define OP_DELETE (1<<2)
#define OP_FLAGS (1<<3)
#define OP_MASK_TYPE (OP_NEW|OP_RENEW|OP_DELETE|OP_FLAGS) /* asserted in the target ops */
#define OP_EXPUNGE (1<<4)
#define OP_CREATE (1<<5)
#define XOP_PUSH (1<<6)
#define XOP_PULL (1<<7)
#define XOP_MASK_DIR (XOP_PUSH|XOP_PULL)
#define XOP_HAVE_TYPE (1<<8)
#define XOP_HAVE_EXPUNGE (1<<9)
#define XOP_HAVE_CREATE (1<<10)
typedef struct driver driver_t;
typedef struct store_conf {
struct store_conf *next;
char *name;
driver_t *driver;
const char *path; /* should this be here? its interpretation is driver-specific */
char *map_inbox;
char *trash;
unsigned max_size; /* off_t is overkill */
unsigned trash_remote_new:1, trash_only_new:1;
} store_conf_t;
typedef struct string_list {
struct string_list *next;
char string[1];
} string_list_t;
typedef struct channel_conf {
struct channel_conf *next;
char *name;
store_conf_t *master, *slave;
char *master_name, *slave_name;
char *sync_state;
string_list_t *patterns;
int mops, sops;
unsigned max_messages; /* for slave only */
} channel_conf_t;
typedef struct group_conf {
struct group_conf *next;
char *name;
string_list_t *channels;
} group_conf_t;
/* For message->flags */
/* Keep the mailbox driver flag definitions in sync! */
/* The order is according to alphabetical maildir flag sort */
#define F_DRAFT (1<<0) /* Draft */
#define F_FLAGGED (1<<1) /* Flagged */
#define F_ANSWERED (1<<2) /* Replied */
#define F_SEEN (1<<3) /* Seen */
#define F_DELETED (1<<4) /* Trashed */
#define NUM_FLAGS 5
/* For message->status */
#define M_RECENT (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
#define M_DEAD (1<<1) /* expunged */
#define M_FLAGS (1<<2) /* flags fetched */
#define M_PROCESSED (1<<3) /* registered in pair */
#define M_NOT_SYNCED (1<<4) /* not in remote mailbox, yet */
#define M_EXPIRED (1<<5) /* kicked out by MaxMessages */
typedef struct message {
struct message *next;
/* string_list_t *keywords; */
size_t size; /* zero implies "not fetched" */
int uid;
unsigned char flags, status;
} message_t;
/* For opts, both in store and driver_t->select() */
#define OPEN_OLD (1<<0)
#define OPEN_NEW (1<<1)
#define OPEN_FLAGS (1<<2)
#define OPEN_SIZE (1<<3)
#define OPEN_CREATE (1<<4)
#define OPEN_EXPUNGE (1<<5)
#define OPEN_SETFLAGS (1<<6)
#define OPEN_APPEND (1<<7)
typedef struct store {
store_conf_t *conf; /* foreign */
/* currently open mailbox */
const char *name; /* foreign! maybe preset? */
char *path; /* own */
message_t *msgs; /* own */
int uidvalidity;
unsigned char opts; /* maybe preset? */
/* note that the following do _not_ reflect stats from msgs, but mailbox totals */
int count; /* # of messages */
int recent; /* # of recent messages - don't trust this beyond the initial read */
} store_t;
typedef struct {
char *data;
int len;
unsigned char flags;
unsigned char crlf:1;
} msg_data_t;
#define DRV_OK 0
#define DRV_MSG_BAD -1
#define DRV_BOX_BAD -2
#define DRV_STORE_BAD -3
struct driver {
int (*parse_store)( conffile_t *cfg, store_conf_t **storep, int *err );
store_t *(*open_store)( store_conf_t *conf, store_t *oldctx );
void (*close_store)( store_t *ctx );
int (*list)( store_t *ctx, string_list_t **boxes );
void (*prepare)( store_t *ctx, int opts );
int (*select)( store_t *ctx, int minuid, int maxuid, int *excs, int nexcs );
int (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data );
int (*store_msg)( store_t *ctx, msg_data_t *data, int *uid ); /* if uid is null, store to trash */
int (*set_flags)( store_t *ctx, message_t *msg, int uid, int add, int del ); /* msg can be null, therefore uid as a fallback */
int (*trash_msg)( store_t *ctx, message_t *msg ); /* This may expunge the original message immediately, but it needn't to */
int (*check)( store_t *ctx ); /* IMAP-style: flush */
int (*close)( store_t *ctx ); /* IMAP-style: expunge inclusive */
};
/* main.c */
extern int Pid;
extern char Hostname[256];
extern const char *Home;
/* util.c */
extern int Verbose, Quiet, Debug;
void debug( const char *, ... );
void info( const char *, ... );
void infoc( char );
void warn( const char *, ... );
char *next_arg( char ** );
void add_string_list( string_list_t **list, const char *str );
void free_string_list( string_list_t *list );
void free_generic_messages( message_t * );
void strip_cr( msg_data_t *msgdata );
void *nfmalloc( size_t sz );
void *nfcalloc( size_t sz );
void *nfrealloc( void *mem, size_t sz );
char *nfstrdup( const char *str );
int nfvasprintf( char **str, const char *fmt, va_list va );
int nfasprintf( char **str, const char *fmt, ... );
int nfsnprintf( char *buf, int blen, const char *fmt, ... );
void ATTR_NORETURN oob( void );
char *expand_strdup( const char *s );
void sort_ints( int *arr, int len );
void arc4_init( void );
unsigned char arc4_getbyte( void );
/* sync.c */
#define SYNC_OK 0
#define SYNC_FAIL 1
#define SYNC_MASTER_BAD 2
#define SYNC_SLAVE_BAD 3
int sync_boxes( store_t *, const char *,
store_t *, const char *,
channel_conf_t * );
/* config.c */
extern channel_conf_t *channels;
extern group_conf_t *groups;
extern int global_mops, global_sops;
extern char *global_sync_state;
int parse_bool( conffile_t *cfile );
int parse_int( conffile_t *cfile );
int parse_size( conffile_t *cfile );
int getcline( conffile_t *cfile );
int merge_ops( int cops, int *mops, int *sops );
int load_config( const char *filename, int pseudo );
void parse_generic_store( store_conf_t *store, conffile_t *cfg, int *err );
/* drv_*.c */
extern driver_t maildir_driver, imap_driver;

1171
src/main.c

File diff suppressed because it is too large Load diff

View file

@ -1,32 +1,34 @@
.\" 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"
.
.ig
\" mbsync - mailbox synchronizer
\" Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
\" Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
\" Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
\"
\" This program is free software; you can redistribute it and/or modify
\" it under the terms of the GNU General Public License as published by
\" the Free Software Foundation; either version 2 of the License, or
\" (at your option) any later version.
\"
\" This program is distributed in the hope that it will be useful,
\" but WITHOUT ANY WARRANTY; without even the implied warranty of
\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\" GNU General Public License for more details.
\"
\" You should have received a copy of the GNU General Public License
\" along with this program; if not, write to the Free Software
\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\"
\" As a special exception, mbsync may be linked with the OpenSSL library,
\" despite that library's more restrictive license.
..
.TH mbsync 1 "2004 Mar 27"
..
.SH NAME
mbsync - synchronize IMAP4 and Maildir mailboxes
.
..
.SH SYNOPSIS
\fBmbsync\fR [\fIoptions\fR ...] {{\fIchannel\fR[\fB:\fIbox\fR[{\fB,\fR|\fB\\n\fR}...]]|\fIgroup\fR} ...|\fB-a\fR}
.
..
.SH DESCRIPTION
\fBmbsync\fR is a command line application which synchronizes mailboxes;
currently Maildir and IMAP4 mailboxes are supported.
@ -34,15 +36,12 @@ New messages, message deletions and flag changes can be propagated both ways;
the operation set can be selected in a fine-grained manner.
.br
Synchronization is based on unique message identifiers (UIDs), so no
identification conflicts can occur (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).
identification conflicts can occur (as opposed to some other mail synchronizers).
OTOH, \fBmbsync\fR is susceptible to UID validity changes (that \fIshould\fR
never happen, but see "Compatibility" in the README).
Synchronization state is kept in one local text file per mailbox pair;
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.
.
multiple replicas of a mailbox can be maintained.
..
.SH OPTIONS
.TP
\fB-c\fR, \fB--config\fR \fIfile\fR
@ -57,13 +56,10 @@ 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]
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},\
@ -80,49 +76,25 @@ Display a summary of command line options.
Display version information.
.TP
\fB-V\fR, \fB--verbose\fR
Enable \fIverbose\fR mode, which displays what is currently happening.
Enable \fIverbose\fR mode, which displays the IMAP4 network traffic.
.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]
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
.br
\fBn\fR, \fBnet\fR - print network traffic (protocol only)
.br
\fBN\fR, \fBnet-all\fR - print network traffic (including payloads)
.br
\fBs\fR, \fBsync\fR - print synchronization debug info
.in -4
All categories except \fBcrash\fR implictly enable \fIverbose\fR mode.
Without category specification, all categories except net-all are enabled.
\fB-D\fR, \fB--debug\fR
Enable printing \fIdebug\fR information.
.TP
\fB-q\fR, \fB--quiet\fR
Suppress progress counters (this is implicit if stdout is no TTY,
or any debugging categories are enabled) and notices.
Suppress informational messages.
If specified twice, suppress warning messages as well.
.
..
.SH CONFIGURATION
The configuration file is mandatory; \fBmbsync\fR will not run without it.
Lines starting with a hash mark (\fB#\fR) are comments and are ignored entirely.
Configuration items are keywords followed by one or more arguments;
arguments containing spaces must be enclosed in double quotes (\fB"\fR),
and literal double quotes and backslashes (\fB\\\fR) must be backslash-escaped.
arguments containing spaces must be enclosed in double quotes (\fB"\fR).
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.
Every section defines an object with an identifier unique within that
There are a few global options, the rest applies to particular sections.
Sections are started by a section keyword and are terminated by an empty line
or end of file.
Every section defines an object with a an identifier unique within that
object class.
.P
There are two basic object classes: Stores and Channels. A Store defines
@ -130,17 +102,10 @@ 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
Store's internal path separators, which may be slashes, periods, etc., or
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,77 +114,54 @@ 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)
.
(Default: \fI""\fR)
..
.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.
MeBytes instead of bytes. \fBB\fR is accepted but superflous.
If \fIsize\fR is 0, the maximum message size is \fBunlimited\fR.
(Default: \fI0\fR)
.
..
.TP
\fBMapInbox\fR \fImailbox\fR
Create a virtual mailbox (relative to \fBPath\fR) which aliases
Create a virtual mailbox (relative to \fBPath\fR), which is backed by
the \fBINBOX\fR. Makes sense in conjunction with \fBPatterns\fR in the
Channels section, though with a Maildir near side, 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
hierarchy delimiter \fB/\fR with \fIdelim\fR.
This can be useful when the MUA used to access the Store provides
suboptimal handling of hierarchical mailboxes, as is the case with
\fBMutt\fR.
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.
.
Channels section.
..
.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.
prior to expunging. See \fBINHERENT PROBLEMS\fR below.
(Default: none)
.
..
.TP
\fBTrashNewOnly\fR \fByes\fR|\fBno\fR
\fBTrashNewOnly\fR \fIyes\fR|\fIno\fR
When trashing, copy only not yet propagated messages. This makes sense if the
remote Store has a \fBTrash\fR as well (with \fBTrashNewOnly\fR \fBno\fR).
(Default: \fBno\fR)
.
remote Store has a \fBTrash\fR as well (with \fBTrashNewOnly\fR \fIno\fR).
(Default: \fIno\fR)
..
.TP
\fBTrashRemoteNew\fR \fByes\fR|\fBno\fR
\fBTrashRemoteNew\fR \fIyes\fR|\fIno\fR
When expunging the remote Store, copy not yet propagated messages to this
Store's \fBTrash\fR. When using this, the remote Store does not need an own
\fBTrash\fR at all, yet all messages are archived.
(Default: \fBno\fR)
.
(Default: \fIno\fR)
..
.SS Maildir Stores
The reference point for relative \fBPath\fRs is the current working directory.
The reference point for relative \fBPath\fRs is $HOME.
.P
As \fBmbsync\fR needs UIDs, but no standardized UID storage scheme exists for
Maildir, \fBmbsync\fR supports two schemes, each with its pros and cons.
@ -233,7 +175,7 @@ versions 0.8 and 0.9.x. The invariant parts of the file names of the messages
are used as keys into a Berkeley database named .isyncuidmap.db, which holds
the UID validity as well.
.br
The \fBnative\fR scheme is faster, more space efficient, endianness independent
The \fBnative\fR scheme is faster, more space efficient, endianess independent
and "human readable", but will be disrupted if a message is copied from another
mailbox without getting a new file name; this would result in duplicated UIDs
sooner or later, which in turn results in a UID validity change, making
@ -245,236 +187,88 @@ 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
\fBAltMap\fR \fIyes\fR|\fIno\fR
Use the \fBalternative\fR UID storage scheme for mailboxes in this Store.
This does not affect mailboxes that do already have a UID storage scheme;
use \fBmdconvert\fR to change it.
See \fBRECOMMENDATIONS\fR below.
(Default: \fBno\fR)
.
(Default: \fIno\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.
The location of the \fBINBOX\fR. This is \fInot\fR relative to \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.
.br
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.
.
\fBHost\fR [\fBimaps:\fR]\fIhost\fR
Specify the DNS name or IP address of the IMAP server. If \fIhost\fR is
prefixed with \fBimaps:\fR the connection is assumed to be an SSL connection
to port 993.
Note that modern servers support SSL on the default port 143 via the
STARTTLS extension, which will be used automatically by default.
..
.TP
\fBPort\fR \fIport\fR
Specify the TCP port number of the IMAP server. (Default: 143 for IMAP,
993 for IMAPS)
.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)
.
Specify the TCP port number of the IMAP server. (Default: 143 for imap,
993 for imaps)
..
.TP
\fBUser\fR \fIusername\fR
Specify the login name on the IMAP server.
.
.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.
.
Specify the login name on the IMAP server. (Default: current local user)
..
.TP
\fBPass\fR \fIpassword\fR
Specify the password for \fIusername\fR on the IMAP server.
Note that this option is \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
.
Note that this option is \fBNOT\fR required.
If no password is specified in the configuration file, \fBmbsync\fR
will prompt you for it.
..
.TP
\fBTunnel\fR \fIcommand\fR
Specify a command to run to establish a connection rather than opening a TCP
socket. This allows you to run an IMAP session over an SSH tunnel, for
example.
.
example. This makes most other IMAPAccount options superflous.
..
.TP
\fBAuthMechs\fR \fItype\fR ...
The list of acceptable authentication mechanisms.
In addition to the mechanisms listed in the SASL registry (link below),
the legacy IMAP \fBLOGIN\fR mechanism is known.
The wildcard \fB*\fR represents all mechanisms that are deemed secure
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)
.
\fBRequireCRAM\fR \fIyes\fR|\fIno\fR
If set to \fIyes\fR, \fBmbsync\fR will abort the connection if no CRAM-MD5
authentication is possible. (Default: \fIno\fR)
..
.TP
\fBSSLType\fR {\fBNone\fR|\fBSTARTTLS\fR|\fBIMAPS\fR}
Select the connection security/encryption method:
.br
\fBNone\fR - no security.
This is the default when \fBTunnel\fR is set, as tunnels are usually secure.
.br
\fBSTARTTLS\fR - security is established via the STARTTLS extension
after connecting the regular IMAP port 143. Most servers support this,
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]
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]).
.
.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.
(Default: \fByes\fR)
.
\fBRequireSSL\fR \fIyes\fR|\fIno\fR
\fBmbsync\fR will abort the connection if a TLS/SSL session cannot be
established with the IMAP server. (Default: \fIyes\fR)
..
.TP
\fBCertificateFile\fR \fIpath\fR
File containing 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.
.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
.
File containing X.509 CA certificates used to verify server identities.
This option is \fImandatory\fR if SSL is used. See \fBSSL CERTIFICATES\fR below.
..
.TP
\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.
.
\fBUseSSLv2\fR \fIyes\fR|\fIno\fR
Use SSLv2 for communication with the IMAP server over SSL?
(Default: \fIyes\fR if an imaps \fBHost\fR is used, otherwise \fIno\fR)
..
.TP
\fBClientKey\fR \fIpath\fR
File containing the private key corresponding to \fBClientCertificate\fR.
.
\fBUseSSLv3\fR \fIyes\fR|\fIno\fR
Use SSLv3 for communication with the IMAP server over SSL?
(Default: \fIyes\fR if an imaps \fBHost\fR is used, otherwise \fIno\fR)
..
.TP
\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).
.
.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.
(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)
.
\fBUseTLSv1\fR \fIyes\fR|\fIno\fR
Use TLSv1 for communication with the IMAP server over SSL?
(Default: \fIyes\fR)
..
.SS IMAP Stores
The reference point for relative \fBPath\fRs is whatever the server likes it
to be; probably the user's $HOME or $HOME/Mail on that server. The location
@ -482,108 +276,76 @@ 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
\fBUseNamespace\fR \fIyes\fR|\fIno\fR
Selects whether the server's first "personal" NAMESPACE should be prefixed to
mailbox names. Disabling this makes sense for some broken IMAP servers.
This option is meaningless if a \fBPath\fR was specified.
(Default: \fByes\fR)
.
.TP
\fBPathDelimiter\fR \fIdelim\fR
Specify the server's hierarchy delimiter.
(Default: taken from the server's first "personal" NAMESPACE)
.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)
.
(Default: \fIyes\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.
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.
.
{\fBMaster\fR|\fBSlave\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
Specify the Master resp. Slave Store to be connected by this Channel.
If \fImailbox\fR is omitted, \fBINBOX\fR is assumed.
The \fImailbox\fR specification can be overridden by \fBPatterns\fR, which
in turn can be overridden by a mailbox list in a Channel reference (a Group
specification or the command line).
..
.TP
\fBPattern\fR[\fBs\fR] [\fB!\fR]\fIpattern\fR ...
Instead of synchronizing only one mailbox pair, synchronize all mailboxes
that match the \fIpattern\fR(s). The mailbox names are the same on 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
times); later matches take precedence.
.br
Note that \fBINBOX\fR is not matched by wildcards, unless it lives under
\fBPath\fR.
.br
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
the actual date of the message) will be deleted first.
Messages that are flagged (marked as important) and (by default) unread
messages will not be automatically deleted.
Messages that are flagged (marked as important) and recent messages will not
be automatically deleted.
If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR
(Default: \fI0\fR).
.
..
.TP
\fBExpireUnread\fR \fByes\fR|\fBno\fR
Selects whether unread messages should be affected by \fBMaxMessages\fR.
Normally, unread messages are considered important and thus never expired.
This ensures that you never miss new messages even after an extended absence.
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}
\fBSync\fR {\fINone\fR|[\fIPull\fR] [\fIPush\fR] [\fINew\fR] [\fIReNew\fR] [\fIDelete\fR] [\fIFlags\fR]|\fIFull\fR}
Select the synchronization operation(s) to perform:
.br
\fBPull\fR - propagate changes from 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
@ -591,7 +353,7 @@ Store are marked as deleted only, i.e., they won't be really deleted until
that Store is expunged.
.br
\fBFlags\fR - propagate flag changes. Note that Deleted/Trashed is a flag as
well; this is particularly interesting if you use \fBmutt\fR with the
well; this is particularily interesting if you use \fBmutt\fR with the
maildir_trash option.
.br
\fBAll\fR (\fB--full\fR on the command line) - all of the above.
@ -609,10 +371,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,72 +382,44 @@ 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 {\fINone\fR|\fIMaster\fR|\fISlave\fR|\fIBoth\fR}
Automatically create missing mailboxes [on the Master/Slave].
Otherwise print an error message and skip that mailbox pair if a mailbox
and the corresponding sync state does not exist.
(Global default: \fBNone\fR)
.
does not exist.
(Global default: \fINone\fR)
..
.TP
\fBRemove\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Propagate mailbox deletions [to the far/near side].
Otherwise print an error message and skip that mailbox pair if a mailbox
does not exist but the corresponding sync state does.
.br
For MailDir mailboxes it is sufficient to delete the cur/ subdirectory to
mark them as deleted. This ensures compatibility with \fBSyncState *\fR.
.br
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.
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
the messages.
Enabling this makes sense in order to keep the time stamp based message
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)
.
\fBExpunge\fR {\fINone\fR|\fIMaster\fR|\fISlave\fR|\fIBoth\fR}
Permanently remove all messages [on the Master/Slave] marked for deletion.
(Global default: \fINone\fR)
..
.P
\fBSync\fR, \fBCreate\fR, \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,
\fBSync\fR, \fBCreate\fR and \fBExpunge\fR can be used outside any section for
a global effect. The global settings are overridden by Channel-specific options,
which in turn are overridden by command line switches.
.
..
.TP
\fBSyncState\fR {\fB*\fR|\fIpath\fR}
Set the location of this Channel's synchronization state files.
\fB*\fR means that the state should be saved in a file named .mbsyncstate
in the 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
(see also \fBFieldDelimiter\fR below).
\fB:\fImaster\fB:\fImaster-box\fB_:\fIslave\fB:\fIslave-box\fR.
.br
(Global default: \fI~/.mbsync/\fR).
.
..
.SS Groups
.TP
\fBGroup\fR \fIname\fR [\fIchannel\fR[\fB:\fIbox\fR[\fB,\fR...]]] ...
@ -696,111 +430,26 @@ with the same name on the command line.
One or more Channels can be specified on the same line.
.br
If you supply one or more \fIbox\fRes to a \fIchannel\fR, they will be used
instead of what is specified in the Channel's Patterns.
The same can be done on the command line, except that there newlines can be
used as mailbox name separators as well.
.
instead of what is specified in the Channel. The same can be done on the command
line, except that there newlines can be used as mailbox name separators as well.
..
.TP
\fBChannel\fR[\fBs\fR] \fIchannel\fR[\fB:\fIbox\fR[\fB,\fR...]] ...
Add the specified channels to the group. This option can be specified multiple
times within a Group.
.
.SS Global Options
.TP
\fBFSync\fR \fByes\fR|\fBno\fR
.br
Selects whether \fBmbsync\fR performs forced flushing, which determines
the level of data safety after system crashes and power outages.
Disabling it is reasonably safe for file systems which are mounted with
data=ordered mode.
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
\fBSyncState\fR.
\fBmbsync\fR prefers to use the colon, but this is incompatible with
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
\fBmbsync\fR will refrain from using more memory. Note that this is no
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
.in -4
.P
This represents the cumulative progress over channels, boxes, and messages
affected on the far and near side, 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
be configured not to do it.
.P
By default, \fBmbsync\fR will not delete any messages - deletions are
propagated by marking the messages as deleted on the remote store.
Once you have verified that your setup works, you will typically want to
set \fBExpunge\fR to \fBBoth\fR, so that deletions become effective.
.P
\fBmbsync\fR's built-in trash functionality relies on \fBmbsync\fR doing
the expunging of deleted messages. This is the case when it propagates
deletions of previously propagated messages, and the trash is on the target
store (typically your IMAP server).
.br
However, when you intend \fBmbsync\fR to trash messages which were not
propagated yet, the MUA must mark the messages as deleted without expunging
them (e.g., \fBMutt\fR's \fBmaildir_trash\fR option). Note that most
messages are propagated a long time before they are deleted, so this is a
corner case you probably do not want to optimize for. This also implies
that the \fBTrashNewOnly\fR and \fBTrashRemoteNew\fR options are typically
not very useful.
.P
If your server supports auto-trashing (as Gmail does), it is probably a
good idea to rely on that instead of \fBmbsync\fR's trash functionality.
If you do that, and intend to synchronize the trash like other mailboxes,
you should not use \fBmbsync\fR's \fBTrash\fR option at all.
.P
Use of the \fBTrash\fR option with M$ Exchange 2013 requires the use of
\fBDisableExtension MOVE\fR due to a server bug.
.P
When using the more efficient default UID mapping scheme, it is important
that the MUA renames files when moving them between Maildir folders.
Mutt always does that, while mu4e needs to be configured to do it:
.br
.in +4
(setq mu4e-change-filenames-when-moving t)
.in -4
.
..
.SH SSL CERTIFICATES
[to be done]
..
.SH INHERENT PROBLEMS
Changes done after \fBmbsync\fR has retrieved the message list will not be
synchronised until the next time \fBmbsync\fR is invoked.
.P
Using \fBTrash\fR on IMAP Stores without the UIDPLUS extension (notably,
M$ Exchange up to at least 2010) bears a race condition: messages will be
Using \fBTrash\fR on IMAP Stores bears a race condition: messages will be
lost if they are marked as deleted after the message list was retrieved but
before the mailbox is expunged.
There is no risk as long as the IMAP mailbox is accessed by only one client
(including \fBmbsync\fR) at a time.
.
before the mailbox is expunged. There is no risk as long as the IMAP mailbox
is not simultaneously accessed by \fBmbsync\fR and another mail client.
..
.SH FILES
.TP
.B ~/.mbsyncrc
@ -808,16 +457,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,
contributions by Theodore Y. Ts'o.
..
.SH AUTHOR
Written by Michael R. Elkins <me@mutt.org>,
.br
rewritten and maintained by Oswald Buddenhagen <ossi@users.sf.net>,
.br
contributions by Theodore Y. Ts'o <tytso@mit.edu>.

View file

@ -11,21 +11,13 @@ Trash Trash
IMAPStore work
Host work.host.com
User tehuser
Pass xxxxxxxx
# Fetch password from gnome-keyring:
#PassCmd "gnome-keyring-query get mail_pw"
# Fetch password from .netrc:
#PassCmd "sed -n -e 's,^machine work\\.host\\.com login tehuser password \\(.*\\),\\1,p' < $HOME/.netrc"
# Fetch password from a gpg-encrypted file:
#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"
CertificateFile /etc/ssl/certs/ca-certificates.crt
Channel work
Far :work:
Near :local:work
Expunge Near
Master :work:
Slave :local:work
Expunge Slave
Sync PullNew Push
@ -35,8 +27,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 +37,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 +57,8 @@ RequireSSL no
UseTLSv1 no
Channel rst
Far :st1:somebox
Near :st2:
Master :st1:somebox
Slave :st2:
IMAPAccount server
@ -81,17 +73,10 @@ 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
Patterns *

View file

@ -13,7 +13,8 @@
\" 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/>.
\" along with this program; if not, write to the Free Software
\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
..
.TH mdconvert 1 "2004 Mar 27"
..

View file

@ -13,10 +13,11 @@
* 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/>.
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <autodefs.h>
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
@ -34,43 +35,17 @@
#define EXE "mdconvert"
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_NORETURN __attribute__((noreturn))
# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
#else
# define ATTR_NORETURN
# define ATTR_PRINTFLIKE(fmt,var)
#endif
static void ATTR_NORETURN
oob( void )
{
fputs( "Fatal: buffer too small. Please report a bug.\n", stderr );
abort();
}
static void ATTR_PRINTFLIKE(1, 2)
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 );
}
static int ATTR_PRINTFLIKE(3, 4)
static int
nfsnprintf( char *buf, int blen, const char *fmt, ... )
{
int ret;
va_list va;
va_start( va, fmt );
if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
oob();
if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen) {
fputs( "Fatal: buffer too small. Please report a bug.\n", stderr );
abort();
}
va_end( va );
return ret;
}
@ -79,7 +54,7 @@ static const char *subdirs[] = { "cur", "new" };
static struct flock lck;
static DBT key, value;
static int
static inline int
convert( const char *box, int altmap )
{
DB *db;
@ -106,22 +81,22 @@ convert( const char *box, int altmap )
nfsnprintf( tdpath, sizeof(tdpath), "%s.tmp", dpath );
if ((sfd = open( spath, O_RDWR )) < 0) {
if (errno != ENOENT)
sys_error( "Cannot open %s", spath );
perror( spath );
return 1;
}
if (fcntl( sfd, F_SETLKW, &lck )) {
sys_error( "Cannot lock %s", spath );
perror( spath );
goto sbork;
}
if ((dfd = open( tdpath, O_RDWR|O_CREAT, 0600 )) < 0) {
sys_error( "Cannot create %s", tdpath );
perror( 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, DB_CREATE, 0 ))) {
db->err( db, ret, "Error: db->open(%s)", dbpath );
dbork:
db->close( db, 0 );
@ -135,7 +110,7 @@ convert( const char *box, int altmap )
key.data = (void *)"UIDVALIDITY";
key.size = 11;
if (altmap) {
if ((n = read( sfd, buf, sizeof(buf) - 1 )) <= 0 ||
if ((n = read( sfd, buf, sizeof(buf) )) <= 0 ||
(buf[n] = 0, sscanf( buf, "%d\n%d", &uv[0], &uv[1] ) != 2))
{
fprintf( stderr, "Error: cannot read UIDVALIDITY of '%s'.\n", box );
@ -143,12 +118,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;
}
@ -163,7 +138,7 @@ convert( const char *box, int altmap )
for (i = 0; i < 2; i++) {
bl = nfsnprintf( buf, sizeof(buf), "%s/%s/", box, subdirs[i] );
if (!(d = opendir( buf ))) {
sys_error( "Cannot list %s", buf );
perror( "opendir" );
goto dbork;
}
while ((e = readdir( d ))) {
@ -179,7 +154,7 @@ convert( const char *box, int altmap )
if (u)
ml = u - e->d_name;
else
ru = "", ml = sizeof(buf);
ru = "", ml = INT_MAX;
if (altmap) {
if (!p)
continue;
@ -188,7 +163,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 +172,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;
@ -208,11 +183,9 @@ convert( const char *box, int altmap )
nfsnprintf( buf2 + bl, sizeof(buf2) - bl, "%.*s,U=%d%s", ml, e->d_name, uid, ru );
}
if (rename( buf, buf2 )) {
if (errno == ENOENT) {
closedir( d );
if (errno == ENOENT)
goto again;
}
sys_error( "Cannot rename %s to %s", buf, buf2 );
perror( buf );
ebork:
closedir( d );
goto dbork;
@ -225,12 +198,11 @@ convert( const char *box, int altmap )
db->close( db, 0 );
close( dfd );
if (rename( tdpath, dpath )) {
sys_error( "Cannot rename %s to %s", tdpath, dpath );
close( sfd );
perror( dpath );
return 1;
}
if (unlink( spath ))
sys_error( "Cannot remove %s", spath );
perror( spath );
close( sfd );
return 0;
}

View file

@ -1,806 +0,0 @@
#! /usr/bin/perl -w
#
# Copyright (C) 2006,2013 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/>.
#
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";
}
chdir "tmp" or die "Cannot enter temp direcory.\n";
sub show($$$);
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, 0, 0,
1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 8, 8, "" ],
);
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, "" ],
[ 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", 6, 0, "", 7, 7, "FT", 0, 8, "", 10, 9, "", 9, 10, "" ],
);
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, "" ],
[ 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, "", 10, 9, "", 9, 10, "" ],
);
test("full + expunge both", \@x01, \@X02, \@O02);
my @O03 = ("", "", "Expunge Near\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, "" ],
[ 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, "", 5, 0, "T", 6, 0, "", 7, 0, "T", 10, 9, "", 9, 10, "" ],
);
test("full + expunge near side", \@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, "" ],
[ 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, "" ],
);
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, 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);
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, 0, 0,
1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 0, "", 7, 7, "", 0, 8, "" ],
);
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, "" ],
[ 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, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 8, 8, "", 10, 9, "", 9, 10, "" ],
);
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, 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);
# size restriction tests
my @x10 = (
[ 2,
A, 1, "", B, 2, "*" ],
[ 1,
C, 1, "*" ],
[ 0, 0, 0,
],
);
my @O11 = ("MaxSize 1k\n", "MaxSize 1k\n", "Expunge Near");
#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,
],
);
my @X23 = (
[ 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" ]
);
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);
# expiration tests
my @x30 = (
[ 6,
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
[ 0,
],
[ 0, 0, 0,
],
);
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, "" ],
[ 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, "" ],
);
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, "" ],
[ 4,
A, 1, "F", D, 2, "", E, 3, "S", F, 4, "" ],
[ 6, 3, 4,
1, 1, "F", 4, 2, "", 5, 3, "S", 6, 4, "" ],
);
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, "" ],
[ 6,
A, 1, "S", B, 2, "ST", D, 4, "", E, 5, "", F, 6, "" ],
[ 6, 3, 0,
1, 1, "FS", 2, 2, "~S", 3, 3, "~S", 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, "" ],
[ 6,
B, 2, "FS", D, 4, "", E, 5, "", F, 6, "" ],
[ 6, 3, 6,
2, 2, "FS", 4, 4, "", 5, 5, "", 6, 6, "" ],
);
test("max messages + expunge", \@x50, \@X51, \@O51);
################################################################################
print "OK.\n";
exit 0;
sub qm($)
{
shift;
s/\\/\\\\/g;
s/\"/\\"/g;
s/\"/\\"/g;
s/\n/\\n/g;
return $_;
}
# [ $far, $near, $channel ]
sub writecfg($)
{
my ($sfx) = @_;
open(FILE, ">", ".mbsyncrc") or
die "Cannot open .mbsyncrc.\n";
print FILE
"FSync no
MaildirStore far
Path ./
Inbox ./far
".$$sfx[0]."
MaildirStore near
Path ./
Inbox ./near
".$$sfx[1]."
Channel test
Far :far:
Near :near:
SyncState *
".$$sfx[2];
close FILE;
}
sub killcfg()
{
unlink $_ for (glob("*.log"));
unlink ".mbsyncrc";
}
# $run_async, $mbsync_options, $log_file
# Return: $exit_code, \@mbsync_output
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 |";
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;
}
# $path
# Return: $max_uid, { uid => [ seq, flags ] }
sub readbox($)
{
my $bn = shift;
(-d $bn) or
die "No mailbox '$bn'.\n";
(-d $bn."/tmp" and -d $bn."/new" and -d $bn."/cur") or
die "Invalid mailbox '$bn'.\n";
open(FILE, "<", $bn."/.uidvalidity") or die "Cannot read UID validity of mailbox '$bn'.\n";
my $dummy = <FILE>;
chomp(my $mu = <FILE>);
close FILE;
my %ms = ();
for my $d ("cur", "new") {
opendir(DIR, $bn."/".$d) or next;
for my $f (grep(!/^\.\.?$/, readdir(DIR))) {
my ($uid, $flg, $ph, $num);
if ($f =~ /^\d+\.\d+_\d+\.[-[:alnum:]]+,U=(\d+):2,(.*)$/) {
($uid, $flg) = ($1, $2);
} else {
print STDERR "unrecognided file name '$f' in '$bn'.\n";
exit 1;
}
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);
$sz += length($_);
}
close FILE;
if (!defined($num)) {
print STDERR "message '$f' in '$bn' has no identifier.\n";
exit 1;
}
@{ $ms{$uid} } = ($num, $flg.($sz>1000?"*":"").($ph?"?":""));
}
}
return $mu, \%ms;
}
# $boxname
# Output:
# [ maxuid,
# serial, uid, "flags", ... ],
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];
}
printbox(\@bc);
}
# $filename
# Output:
# [ maxuid[F], maxxfuid, maxuid[N],
# uid[F], uid[N], "flags", ... ],
sub showstate($)
{
my ($fn) = @_;
if (!open(FILE, "<", $fn)) {
print STDERR " Cannot read sync state $fn: $!\n";
return;
}
chomp(my @ls = <FILE>);
close FILE;
my %hdr;
OUTER: while (1) {
while (@ls) {
$_ = shift(@ls);
last OUTER if (!length($_));
if (!/^([^ ]+) (\d+)$/) {
print STDERR "Malformed sync state header entry: $_\n";
close FILE;
return;
}
$hdr{$1} = $2;
}
print STDERR "Unterminated sync state header.\n";
close FILE;
return;
}
my @T = ($hdr{'MaxPulledUid'} // "missing",
$hdr{'MaxExpiredFarUid'} // "0",
$hdr{'MaxPushedUid'} // "missing");
for (@ls) {
/^(\d+) (\d+) (.*)$/;
push @T, $1, $2, $3;
}
printstate(\@T);
}
# $filename
sub showchan($)
{
my ($fn) = @_;
showbox("far");
showbox("near");
showstate($fn);
}
# $source_state_name, $target_state_name, $configs_name
sub show($$$)
{
my ($sx, $tx, $sfxn) = @_;
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");
print ");\n";
writecfg($sfx);
runsync(0, "", "");
killcfg();
print "my \@X$tx = (\n";
showchan("near/.mbsyncstate");
print ");\n";
print "test(\"\", \\\@x$sx, \\\@X$tx, \\\@O$sfxn);\n\n";
rmtree "near";
rmtree "far";
}
# $box_name, \@box_state
sub mkbox($$)
{
my ($bn, $bs) = @_;
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";
close FILE;
for (my $i = 1; $i < @$bs; $i += 3) {
my ($num, $uid, $flg) = ($$bs[$i], $$bs[$i + 1], $$bs[$i + 2]);
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);
close FILE;
}
}
# \@far_state, \@near_state, \@sync_state
sub mkchan($$$)
{
my ($f, $n, $t) = @_;
mkbox("far", $f);
mkbox("near", $n);
open(FILE, ">", "near/.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";
}
close FILE;
}
# $box_name, \@box_state
sub ckbox($$)
{
my ($bn, $bs) = @_;
my ($mu, $ms) = readbox($bn);
if ($mu != $$bs[0]) {
print STDERR "MAXUID mismatch for '$bn' (got $mu, wanted $$bs[0]).\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";
return 1;
}
if ($$m[0] ne $num) {
print STDERR "Subject mismatch for $bn:$uid.\n";
return 1;
}
if ($$m[1] ne $flg) {
print STDERR "Flag mismatch for $bn:$uid.\n";
return 1;
}
}
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($$)
{
my ($fn, $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);
open(FILE, "<", $fn) or die "Cannot read sync state $fn.\n";
chomp(my @ls = <FILE>);
close FILE;
OUTER: while (1) {
while (@ls) {
my $l = shift(@ls);
last OUTER if (!length($l));
if ($l !~ /^([^ ]+) (\d+)$/) {
print STDERR "Malformed sync state header entry: $l\n";
return 1;
}
my $want = delete $hdr{$1};
if (!defined($want)) {
print STDERR "Unexpected sync state header entry: $1\n";
return 1;
}
if ($2 != $want) {
print STDERR "Sync state header entry $1 mismatch: got $2, wanted $want\n";
return 1;
}
}
print STDERR "Unterminated sync state header.\n";
return 1;
}
my @ky = keys %hdr;
if (@ky) {
print STDERR "Keys missing from sync state header: @ky\n";
return 1;
}
my $i = 3;
for my $l (@ls) {
if ($i == @$t) {
print STDERR "Excess sync state entry: '$l'.\n";
return 1;
}
my $xl = $$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2];
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";
return 1;
}
return 0;
}
# $state_file, \@chan_state
sub ckchan($$)
{
my ($fn, $cs) = @_;
my $rslt = ckstate($fn, $$cs[2]);
$rslt |= ckbox("far", $$cs[0]);
$rslt |= ckbox("near", $$cs[1]);
return $rslt;
}
# \@box_state
sub printbox($)
{
my ($bs) = @_;
print " [ $$bs[0],\n ";
my $frst = 1;
for (my $i = 1; $i < @$bs; $i += 3) {
if ($frst) {
$frst = 0;
} else {
print ", ";
}
print mn($$bs[$i]).", ".$$bs[$i + 1].", \"".$$bs[$i + 2]."\"";
}
print " ],\n";
}
# \@sync_state
sub printstate($)
{
my ($t) = @_;
print " [ ".$$t[0].", ".$$t[1].", ".$$t[2].",\n ";
my $frst = 1;
for (my $i = 3; $i < @$t; $i += 3) {
if ($frst) {
$frst = 0;
} else {
print ", ";
}
print(($$t[$i] // "??").", ".($$t[$i + 1] // "??").", \"".($$t[$i + 2] // "??")."\"");
}
print " ],\n";
}
# \@chan_state
sub printchan($)
{
my ($cs) = @_;
printbox($$cs[0]);
printbox($$cs[1]);
printstate($$cs[2]);
}
sub readfile($)
{
my ($file) = @_;
open(FILE, $file) or return;
my @nj = <FILE>;
close FILE;
return \@nj;
}
# $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)) {
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 "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])) {
print "Journal replay failed.\n";
print "Options:\n";
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ], [ \"-0\", \"--no-expunge\" ]\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");
}
print "Debug output:\n";
print @$jret;
exit 1;
}
my ($ixc, $iret) = runsync($async, "", "3-verify.log");
if ($ixc || ckchan("near/.mbsyncstate", $tx)) {
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 "Debug output:\n";
print @$iret;
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();
}

File diff suppressed because it is too large Load diff

View file

@ -1,155 +0,0 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* 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
* 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.
*/
#ifndef SOCKET_H
#define SOCKET_H
#include "common.h"
#ifdef HAVE_LIBZ
#include <zlib.h>
#endif
#ifdef HAVE_LIBSSL
# include <openssl/ssl.h>
# include <openssl/x509.h>
enum {
TLSv1 = 4,
TLSv1_1 = 8,
TLSv1_2 = 16,
TLSv1_3 = 32
};
#endif
typedef struct {
char *tunnel;
char *host;
ushort port;
int timeout;
#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;
SSL_CTX *SSLContext;
#endif
} server_conf_t;
typedef struct buff_chunk {
struct buff_chunk *next;
uint len;
char data[1];
} buff_chunk_t;
typedef struct {
/* connection */
int fd;
int state;
const server_conf_t *conf; /* needed during connect */
#ifdef HAVE_IPV6
struct addrinfo *addrs, *curr_addr; /* needed during connect */
#else
struct addr_info *addrs, *curr_addr; /* needed during connect */
#endif
char *name;
#ifdef HAVE_LIBSSL
SSL *ssl;
wakeup_t ssl_fake;
#endif
#ifdef HAVE_LIBZ
z_streamp in_z, out_z;
wakeup_t z_fake;
int z_written;
#endif
void (*bad_callback)( void *aux ); /* async fail while sending or listening */
void (*read_callback)( void *aux ); /* data available for reading */
void (*write_callback)( void *aux ); /* all *queued* data was sent */
union {
void (*connect)( int ok, void *aux );
void (*starttls)( int ok, void *aux );
} callbacks;
void *callback_aux;
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 */
#ifdef HAVE_LIBZ
uint 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 */
/* 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' */
char buf[100000];
#ifdef HAVE_LIBZ
char z_buf[100000];
#endif
} conn_t;
/* call this before doing anything with the socket */
static INLINE void socket_init( conn_t *conn,
const server_conf_t *conf,
void (*bad_callback)( void *aux ),
void (*read_callback)( void *aux ),
void (*write_callback)( void *aux ),
void *aux )
{
conn->conf = conf;
conn->bad_callback = bad_callback;
conn->read_callback = read_callback;
conn->write_callback = write_callback;
conn->callback_aux = aux;
conn->fd = -1;
conn->name = NULL;
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 */
char *socket_read_line( conn_t *sock ); /* don't free return value; never waits */
typedef enum { KeepOwn = 0, GiveOwn } ownership_t;
typedef struct {
char *buf;
uint len;
ownership_t takeOwn;
} conn_iovec_t;
void socket_write( conn_t *sock, conn_iovec_t *iov, int iovcnt );
#endif

3211
src/sync.c

File diff suppressed because it is too large Load diff

View file

@ -1,87 +0,0 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* 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
* 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.
*/
#ifndef SYNC_H
#define SYNC_H
#include "driver.h"
#define F 0 // far side
#define N 1 // near side
#define OP_NEW (1<<0)
#define OP_RENEW (1<<1)
#define OP_DELETE (1<<2)
#define OP_FLAGS (1<<3)
#define OP_MASK_TYPE (OP_NEW|OP_RENEW|OP_DELETE|OP_FLAGS) /* asserted in the target ops */
#define OP_EXPUNGE (1<<4)
#define OP_CREATE (1<<5)
#define OP_REMOVE (1<<6)
#define XOP_PUSH (1<<8)
#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)
typedef struct channel_conf {
struct channel_conf *next;
const char *name;
store_conf_t *stores[2];
const char *boxes[2];
char *sync_state;
string_list_t *patterns;
int ops[2];
int max_messages; // For near side only.
signed char expire_unread;
char use_internal_date;
} channel_conf_t;
typedef struct group_conf {
struct group_conf *next;
const char *name;
string_list_t *channels;
} group_conf_t;
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];
#define SYNC_OK 0 /* assumed to be 0 */
#define SYNC_FAIL 1
#define SYNC_BAD(fn) (4<<(fn))
#define SYNC_NOGOOD 16 /* internal */
#define SYNC_CANCELED 32 /* internal */
#define BOX_POSSIBLE -1
#define BOX_ABSENT 0
#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 (*cb)( int sts, void *aux ), void *aux );
#endif

View file

@ -1,116 +0,0 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2014 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 "common.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* Just to satisfy the references in util.c */
int DFlags;
const char *Home;
typedef struct {
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 )
{
printf( "starting timer %d, should expire after %d\n", timer->id, to );
time( &timer->start );
conf_wakeup( &timer->timer, to );
}
static void
timed_out( void *aux )
{
tst_t *timer = (tst_t *)aux;
printf( "timer %d expired after %d, repeat %d\n",
timer->id, (int)(time( 0 ) - timer->start), timer->other );
if (timer->other >= 0) {
timer_start( timer, timer->other );
} else {
wipe_wakeup( &timer->timer );
wipe_wakeup( &timer->morph_timer );
free( timer );
}
}
static void
morph_timed_out( void *aux )
{
tst_t *timer = (tst_t *)aux;
printf( "morphing timer %d after %d\n",
timer->id, (int)(time( 0 ) - timer->start) );
timer_start( timer, timer->morph_to );
}
static int nextid;
int
main( int argc, char **argv )
{
int i;
for (i = 1; i < argc; i++) {
char *val = argv[i];
tst_t *timer = nfmalloc( sizeof(*timer) );
init_wakeup( &timer->timer, timed_out, timer );
init_wakeup( &timer->morph_timer, morph_timed_out, timer );
timer->id = ++nextid;
timer->first = strtol( val, &val, 0 );
if (*val == '@') {
timer->other = timer->first;
timer->first = strtol( ++val, &val, 0 );
} else {
timer->other = -1;
}
if (*val == ':') {
timer->morph_to = strtol( ++val, &val, 0 );
if (*val != '@')
goto fail;
timer->morph_at = strtol( ++val, &val, 0 );
} else {
timer->morph_at = -1;
}
if (*val) {
fail:
fprintf( stderr, "Fatal: syntax error in %s, use <timeout>[@<delay>][:<newtimeout>@<delay>]\n", argv[i] );
return 1;
}
timer_start( timer, timer->first );
if (timer->morph_at >= 0) {
printf( "timer %d, should morph after %d\n", timer->id, timer->morph_at );
conf_wakeup( &timer->morph_timer, timer->morph_at );
}
}
main_loop();
return 0;
}

View file

@ -1,7 +1,7 @@
/*
* mbsync - mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2006,2011,2012 Oswald Buddenhagen <ossi@users.sf.net>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -14,77 +14,34 @@
* 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/>.
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* As a special exception, mbsync may be linked with the OpenSSL library,
* despite that library's more restrictive license.
*/
#include "common.h"
#include "isync.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>
#include <ctype.h>
static int need_nl;
int Verbose, Quiet, Debug;
void
flushn( void )
{
if (need_nl) {
putchar( '\n' );
fflush( stdout );
need_nl = 0;
}
}
static void ATTR_PRINTFLIKE(1, 0)
printn( const char *msg, va_list va )
{
if (*msg == '\v')
msg++;
else
flushn();
vprintf( msg, va );
fflush( stdout );
}
void
vdebug( int cat, const char *msg, va_list va )
{
if (DFlags & cat) {
vprintf( msg, va );
fflush( stdout );
need_nl = 0;
}
}
void
vdebugn( int cat, const char *msg, va_list va )
{
if (DFlags & cat) {
vprintf( msg, va );
fflush( stdout );
need_nl = 1;
}
}
void
progress( const char *msg, ... )
debug( const char *msg, ... )
{
va_list va;
va_start( va, msg );
vprintf( msg, va );
va_end( va );
fflush( stdout );
need_nl = 1;
if (Debug) {
va_start( va, msg );
vprintf( msg, va );
va_end( va );
}
}
void
@ -92,37 +49,20 @@ info( const char *msg, ... )
{
va_list va;
if (DFlags & VERBOSE) {
if (!Quiet) {
va_start( va, msg );
printn( msg, va );
vprintf( msg, va );
va_end( va );
need_nl = 0;
fflush( stdout );
}
}
void
infon( const char *msg, ... )
infoc( char c )
{
va_list va;
if (DFlags & VERBOSE) {
va_start( va, msg );
printn( msg, va );
va_end( va );
need_nl = 1;
}
}
void
notice( const char *msg, ... )
{
va_list va;
if (!(DFlags & QUIET)) {
va_start( va, msg );
printn( msg, va );
va_end( va );
need_nl = 0;
if (!Quiet) {
putchar( c );
fflush( stdout );
}
}
@ -131,64 +71,55 @@ warn( const char *msg, ... )
{
va_list va;
if (!(DFlags & VERYQUIET)) {
flushn();
if (Quiet < 2) {
va_start( va, msg );
vfprintf( stderr, msg, va );
va_end( va );
}
}
void
error( const char *msg, ... )
char *
next_arg( char **s )
{
va_list va;
char *ret;
flushn();
va_start( va, msg );
vfprintf( stderr, msg, va );
va_end( va );
}
void
vsys_error( const char *msg, va_list va )
{
char buf[1024];
int errno_bak = errno;
flushn();
if ((uint)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
oob();
errno = errno_bak;
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 )
{
string_list_t *elem;
elem = nfmalloc( offsetof(string_list_t, string) + len + 1 );
elem->next = *list;
*list = elem;
memcpy( elem->string, str, len );
elem->string[len] = 0;
if (!s || !*s)
return 0;
while (isspace( (unsigned char) **s ))
(*s)++;
if (!**s) {
*s = 0;
return 0;
}
if (**s == '"') {
++*s;
ret = *s;
*s = strchr( *s, '"' );
} else {
ret = *s;
while (**s && !isspace( (unsigned char) **s ))
(*s)++;
}
if (*s) {
if (**s)
*(*s)++ = 0;
if (!**s)
*s = 0;
}
return ret;
}
void
add_string_list( string_list_t **list, const char *str )
{
add_string_list_n( list, str, strlen( str ) );
string_list_t *elem;
int len;
len = strlen( str );
elem = nfmalloc( sizeof(*elem) + len );
elem->next = *list;
*list = elem;
memcpy( elem->string, str, len + 1 );
}
void
@ -202,6 +133,31 @@ free_string_list( string_list_t *list )
}
}
void
free_generic_messages( message_t *msgs )
{
message_t *tmsg;
for (; msgs; msgs = tmsg) {
tmsg = msgs->next;
free( msgs );
}
}
void
strip_cr( msg_data_t *msgdata )
{
int i, o;
if (msgdata->crlf) {
for (i = o = 0; i < msgdata->len; i++)
if (msgdata->data[i] != '\r')
msgdata->data[o++] = msgdata->data[i];
msgdata->len = o;
msgdata->crlf = 0;
}
}
#ifndef HAVE_VASPRINTF
static int
vasprintf( char **strp, const char *fmt, va_list ap )
@ -214,128 +170,11 @@ 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
#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
size_t
strnlen( const char *str, size_t maxlen )
{
const char *estr = memchr( str, 0, maxlen );
return estr ? (size_t)(estr - str) : maxlen;
}
#endif
int
starts_with( const char *str, int strl, const char *cmp, uint cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return ((uint)strl >= cmpl) && !memcmp( str, cmp, cmpl );
}
int
starts_with_upper( const char *str, int strl, const char *cmp, uint cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
if ((uint)strl < cmpl)
return 0;
for (uint 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 )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return ((uint)strl == cmpl) && !memcmp( str, cmp, cmpl );
}
#ifndef HAVE_TIMEGM
/*
Converts struct tm to time_t, assuming the data in tm is UTC rather
than local timezone.
mktime is similar but assumes struct tm, also known as the
"broken-down" form of time, is in local time zone. timegm
uses mktime to make the conversion understanding that an offset
will be introduced by the local time assumption.
mktime_from_utc then measures the introduced offset by applying
gmtime to the initial result and applying mktime to the resulting
"broken-down" form. The difference between the two mktime results
is the measured offset which is then subtracted from the initial
mktime result to yield a calendar time which is the value returned.
tm_isdst in struct tm is set to 0 to force mktime to introduce a
consistent offset (the non DST offset) since tm and tm+o might be
on opposite sides of a DST change.
Some implementations of mktime return -1 for the nonexistent
localtime hour at the beginning of DST. In this event, use
mktime(tm - 1hr) + 3600.
Schematically
mktime(tm) --> t+o
gmtime(t+o) --> tm+o
mktime(tm+o) --> t+2o
t+o - (t+2o - t+o) = t
Contributed by Roger Beeman <beeman@cisco.com>, with the help of
Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.
Further improved by Roger with assistance from Edward J. Sabol
based on input by Jamie Zawinski.
*/
static time_t
my_mktime( struct tm *t )
{
time_t tl = mktime( t );
if (tl == -1) {
t->tm_hour--;
tl = mktime( t );
if (tl != -1)
tl += 3600;
}
return tl;
}
time_t
timegm( struct tm *t )
{
time_t tl, tb;
struct tm *tg;
if ((tl = my_mktime( t )) == -1)
return tl;
tg = gmtime( &tl );
tg->tm_isdst = 0;
if ((tb = my_mktime( tg )) == -1)
return tb;
return tl - (tb - tl);
}
#endif
void
oob( void )
{
@ -350,13 +189,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 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
oob();
va_end( va );
return ret;
}
void
static void ATTR_NORETURN
oom( void )
{
fputs( "Fatal: Out of memory\n", stderr );
@ -393,19 +232,14 @@ nfrealloc( void *mem, size_t sz )
return ret;
}
char *
nfstrndup( const char *str, size_t nchars )
{
char *ret = nfmalloc( nchars + 1 );
memcpy( ret, str, nchars );
ret[nchars] = 0;
return ret;
}
char *
nfstrdup( const char *str )
{
return nfstrndup( str, strlen( str ) );
char *ret;
if (!(ret = strdup( str )))
oom();
return ret;
}
int
@ -449,6 +283,15 @@ cur_user( void )
}
*/
static char *
my_strndup( const char *s, size_t nchars )
{
char *r = nfmalloc( nchars + 1 );
memcpy( r, s, nchars );
r[nchars] = 0;
return r;
}
char *
expand_strdup( const char *s )
{
@ -459,20 +302,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 = my_strndup( 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 : "" );
@ -481,120 +324,41 @@ expand_strdup( const char *s )
return nfstrdup( 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 )
{
char *p;
uint i, l, ll, num, inl, outl;
assert( arg );
l = strlen( arg );
assert( in );
inl = strlen( in );
if (!inl) {
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;
for (num = 0, i = 0; i < l; ) {
for (ll = 0; ll < inl; ll++)
if (arg[i + ll] != in[ll])
goto fout;
num++;
i += inl;
continue;
fout:
if (outl) {
for (ll = 0; ll < outl; ll++)
if (arg[i + ll] != out[ll])
goto fnexti;
return -1;
}
fnexti:
i++;
}
if (!num)
goto copy;
if (!outl)
return -2;
*result = nfmalloc( reserve + l + num * (outl - inl) + 1 );
p = *result + reserve;
for (i = 0; i < l; ) {
for (ll = 0; ll < inl; ll++)
if (arg[i + ll] != in[ll])
goto rnexti;
memcpy( p, out, outl );
p += outl;
i += inl;
continue;
rnexti:
*p++ = arg[i++];
}
*p = 0;
return 0;
}
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 );
}
static struct {
uchar i, j, s[256];
unsigned char i, j, s[256];
} rs;
void
arc4_init( void )
{
int i, fd;
uchar j, si, dat[128];
unsigned char j, si, dat[128];
if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) {
error( "Fatal: no random number source available.\n" );
fprintf( stderr, "Fatal: no random number source available.\n" );
exit( 3 );
}
if (read( fd, dat, 128 ) != 128) {
error( "Fatal: cannot read random number source.\n" );
fprintf( stderr, "Fatal: cannot read random number source.\n" );
exit( 3 );
}
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];
@ -607,10 +371,10 @@ arc4_init( void )
arc4_getbyte();
}
uchar
unsigned char
arc4_getbyte( void )
{
uchar si, sj;
unsigned char si, sj;
rs.i++;
si = rs.s[rs.i];
@ -620,285 +384,3 @@ arc4_getbyte( void )
rs.s[rs.j] = si;
return rs.s[(si + sj) & 0xff];
}
static const uchar prime_deltas[] = {
0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 17, 27, 3,
1, 29, 3, 21, 7, 17, 15, 9, 43, 35, 15, 0, 0, 0, 0, 0
};
uint
bucketsForSize( uint size )
{
uint base = 4, bits = 2;
for (;;) {
uint prime = base + prime_deltas[bits];
if (prime >= size)
return prime;
base <<= 1;
bits++;
}
}
static void
list_prepend( list_head_t *head, list_head_t *to )
{
assert( !head->next );
assert( to->next );
assert( to->prev->next == to );
head->next = to;
head->prev = to->prev;
head->prev->next = head;
to->prev = head;
}
static void
list_unlink( list_head_t *head )
{
assert( head->next );
assert( head->next->prev == head);
assert( head->prev->next == head);
head->next->prev = head->prev;
head->prev->next = head->next;
head->next = head->prev = NULL;
}
static notifier_t *notifiers;
static int changed; /* Iterator may be invalid now. */
#ifdef HAVE_POLL_H
static struct pollfd *pollfds;
static uint npolls, rpolls;
#else
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
#endif
void
init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux )
{
#ifdef HAVE_POLL_H
uint idx = npolls++;
if (rpolls < npolls) {
rpolls = npolls;
pollfds = nfrealloc( pollfds, npolls * sizeof(*pollfds) );
}
pollfds[idx].fd = fd;
pollfds[idx].events = 0; /* POLLERR & POLLHUP implicit */
sn->index = idx;
#else
sn->fd = fd;
sn->events = 0;
#endif
sn->cb = cb;
sn->aux = aux;
sn->next = notifiers;
notifiers = sn;
}
void
conf_notifier( notifier_t *sn, short and_events, short or_events )
{
#ifdef HAVE_POLL_H
uint 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;
#endif
for (snp = &notifiers; *snp != sn; snp = &(*snp)->next)
assert( *snp );
*snp = sn->next;
sn->next = NULL;
changed = 1;
#ifdef HAVE_POLL_H
idx = sn->index;
memmove( pollfds + idx, pollfds + idx + 1, (--npolls - idx) * sizeof(*pollfds) );
for (sn = notifiers; sn; sn = sn->next) {
if (sn->index > idx)
sn->index--;
}
#endif
}
static time_t
get_now( void )
{
return time( NULL );
}
static list_head_t timers = { &timers, &timers };
void
init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux )
{
tmr->cb = cb;
tmr->aux = aux;
tmr->links.next = tmr->links.prev = NULL;
}
void
wipe_wakeup( wakeup_t *tmr )
{
if (tmr->links.next)
list_unlink( &tmr->links );
}
void
conf_wakeup( wakeup_t *tmr, int to )
{
list_head_t *head, *succ;
if (to < 0) {
if (tmr->links.next)
list_unlink( &tmr->links );
} else {
time_t timeout = to;
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) {
if (head != &tmr->links && timeout > ((wakeup_t *)head)->timeout)
break;
}
assert( head != &tmr->links );
}
tmr->timeout = timeout;
if (succ != &tmr->links) {
if (tmr->links.next)
list_unlink( &tmr->links );
list_prepend( &tmr->links, succ );
}
}
}
static void
event_wait( void )
{
list_head_t *head;
notifier_t *sn;
int m;
#ifdef HAVE_POLL_H
int timeout = -1;
if ((head = timers.next) != &timers) {
wakeup_t *tmr = (wakeup_t *)head;
time_t delta = tmr->timeout;
if (!delta || (delta -= get_now()) <= 0) {
list_unlink( head );
tmr->cb( tmr->aux );
return;
}
timeout = (int)delta * 1000;
}
switch (poll( pollfds, npolls, timeout )) {
case 0:
return;
case -1:
perror( "poll() failed in event loop" );
abort();
default:
break;
}
for (sn = notifiers; sn; sn = sn->next) {
uint n = sn->index;
if ((m = pollfds[n].revents)) {
assert( !(m & POLLNVAL) );
sn->cb( m | shifted_bit( m, POLLHUP, POLLIN ), sn->aux );
if (changed) {
changed = 0;
break;
}
}
}
#else
struct timeval *timeout = 0;
struct timeval to_tv;
fd_set rfds, wfds, efds;
int fd;
if ((head = timers.next) != &timers) {
wakeup_t *tmr = (wakeup_t *)head;
time_t delta = tmr->timeout;
if (!delta || (delta -= get_now()) <= 0) {
list_unlink( head );
tmr->cb( tmr->aux );
return;
}
to_tv.tv_sec = delta;
to_tv.tv_usec = 0;
timeout = &to_tv;
}
FD_ZERO( &rfds );
FD_ZERO( &wfds );
FD_ZERO( &efds );
m = -1;
for (sn = notifiers; sn; sn = sn->next) {
fd = sn->fd;
if (sn->events & POLLIN)
FD_SET( fd, &rfds );
if (sn->events & POLLOUT)
FD_SET( fd, &wfds );
FD_SET( fd, &efds );
if (fd > m)
m = fd;
}
switch (select( m + 1, &rfds, &wfds, &efds, timeout )) {
case 0:
return;
case -1:
perror( "select() failed in event loop" );
abort();
default:
break;
}
for (sn = notifiers; sn; sn = sn->next) {
fd = sn->fd;
m = 0;
if (FD_ISSET( fd, &rfds ))
m |= POLLIN;
if (FD_ISSET( fd, &wfds ))
m |= POLLOUT;
if (FD_ISSET( fd, &efds ))
m |= POLLERR;
if (m) {
sn->cb( m, sn->aux );
if (changed) {
changed = 0;
break;
}
}
}
#endif
}
void
main_loop( void )
{
while (notifiers || timers.next != &timers)
event_wait();
}