Compare commits

..

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

39 changed files with 4188 additions and 3386 deletions

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

@ -59,7 +59,7 @@ log:
cov-scan: clean
/opt/cov-analysis-*/bin/cov-build --dir cov-int $(MAKE)
tar cavf isync-cov.tar.xz cov-int
tar cavf isync.tar.xz cov-int
deb:
CFLAGS= INSTALL= dpkg-buildpackage -b --no-sign

21
NEWS
View file

@ -1,24 +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.

3
README
View file

@ -23,7 +23,8 @@ Mailboxes can be safely modified while ``mbsync'' operates.
Multiple replicas of each mailbox can be maintained.
isync is the project name, while mbsync is the current executable name; this
change was necessary because of massive changes in the user interface.
change was necessary because of massive changes in the user interface. An
isync executable still exists; it is a compatibility wrapper around mbsync.
* Features

17
TODO
View file

@ -15,7 +15,7 @@ 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?
- refuse to delete master 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.
@ -28,15 +28,21 @@ 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
make it possible to have different mailbox names for Master and Slave in
Patterns.
- use far:near for the pattern
- use master:slave for the pattern
- for quoting, use more colons: the longest sequence of colons is the
separator
- this makes Groups mostly useless, as they are mostly a workaround for this
function being missing so far
- this is needed for move detection, which would work only within one Channel
kill the concept of an INBOX, it is a relic from single-channel operation.
if somebody needs it, he can have two stores with different Paths. the path
can name a single (in-)box (curr. broken with maildir). an empty box name
actually means empty, so the IMAP mailbox should use INBOX for Path (can't
make that the default, as it would mess up the NAMESPACE).
add regexp-based mailbox path rewriting to the drivers. user would provide
expressions for both directions. every transformation would be immediately
verified with the inverse transform. PathDelimiter and Flatten would become
@ -61,8 +67,9 @@ messages.
use MULTIAPPEND and FETCH with multiple messages.
dummy messages resulting from MaxSize should contain a dump of the original
message's MIME structure and its (reasonably sized) text parts.
create dummies describing MIME structure of messages bigger than MaxSize.
flagging the dummy would fetch the real message. possibly remove --renew.
note that all interaction needs to happen on the slave side probably.
don't SELECT boxes unless really needed; in particular not for appending,
and in write-only mode not before changes are made.

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,47 +1,14 @@
AC_INIT([isync], [1.4.4])
AC_INIT([isync], [1.3.2])
AC_CONFIG_HEADERS([autodefs.h])
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE
AM_MAINTAINER_MODE
AC_PROG_CC
AC_PROG_CC_C99
if test "$GCC" = yes; then
warnings="
-Wall -Wextra
-Wshadow
-Wcast-qual
-Wformat=2 -Wformat-signedness -Wformat-nonliteral
-Wstrict-prototypes
-Wno-overlength-strings
"
CFLAGS="$CFLAGS -pipe -std=c11 -pedantic $(echo $warnings)"
CFLAGS="$CFLAGS -pipe -W -Wall -Wshadow -Wstrict-prototypes -std=c99 -pedantic -Wno-overlength-strings"
fi
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
void fkt(void)
{
int a = 42; // c99 comment
for (int i = 0; i < a; i++) {} // declaration inside for()
int b; // declaration after code
}
// c11 anonymous structs/unions
struct base {
int a;
};
union deriv {
struct base gen;
struct {
int a;
int b;
};
};
])], , [AC_MSG_ERROR([compiler does not support required C11 features])])
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
AC_CHECK_PROG(PERL, perl, perl)
@ -62,8 +29,8 @@ if test "x$ob_cv_perl_ver" = "xno"; then
fi
AC_CACHE_CHECK([whether strftime supports %z], ob_cv_strftime_z,
[AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <time.h>
[AC_TRY_RUN(
[#include <time.h>
#include <string.h>
int main(void)
@ -73,12 +40,12 @@ int main(void)
strftime(buf, sizeof(buf), "%z", localtime(&t));
return !(buf[0] == '+' || buf[0] == '-');
}
]])], [ob_cv_strftime_z=yes], [ob_cv_strftime_z=no], [ob_cv_strftime_z="yes (assumed)"])])
], [ob_cv_strftime_z=yes], [ob_cv_strftime_z=no], [ob_cv_strftime_z="yes (assumed)"])])
if test "x$ob_cv_strftime_z" = x"no"; then
AC_MSG_ERROR([libc lacks necessary feature])
fi
AC_CHECK_HEADERS(poll.h sys/select.h)
AC_CHECK_HEADERS(sys/poll.h sys/select.h)
AC_CHECK_FUNCS(vasprintf strnlen memrchr timegm)
AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
@ -96,7 +63,7 @@ fi
have_ssl_paths=
AC_ARG_WITH(ssl,
AS_HELP_STRING([--with-ssl[=PATH]], [where to look for SSL [detect]]),
AC_HELP_STRING([--with-ssl[=PATH]], [where to look for SSL [detect]]),
[ob_cv_with_ssl=$withval])
if test "x$ob_cv_with_ssl" != xno; then
case $ob_cv_with_ssl in
@ -193,13 +160,12 @@ AC_CACHE_CHECK([for Berkeley DB >= 4.1], ac_cv_berkdb4,
[ac_cv_berkdb4=no
sav_LIBS=$LIBS
LIBS="$LIBS -ldb"
AC_LINK_IFELSE([AC_LANG_PROGRAM(
[#include <db.h>],
[DB *db;
db_create(&db, 0, 0);
db->truncate(db, 0, 0, 0);
db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 0);
])], [ac_cv_berkdb4=yes], [])
AC_TRY_LINK([#include <db.h>],
[DB *db;
db_create(&db, 0, 0);
db->truncate(db, 0, 0, 0);
db->open(db, 0, "foo", "foo", DB_HASH, DB_CREATE, 0)],
[ac_cv_berkdb4=yes])
LIBS=$sav_LIBS
])
if test "x$ac_cv_berkdb4" = xyes; then
@ -221,32 +187,16 @@ if test "x$ob_cv_with_zlib" != xno; then
)
fi
AC_ARG_ENABLE(compat,
AC_HELP_STRING([--disable-compat], [don't include isync compatibility wrapper [no]]),
[ob_cv_enable_compat=$enableval])
if test "x$ob_cv_enable_compat" != xno; then
AC_CHECK_FUNCS(getopt_long)
fi
AM_CONDITIONAL(with_compat, test "x$ob_cv_enable_compat" != xno -a "x$ac_cv_berkdb4" = xyes)
AM_CONDITIONAL(with_mdconvert, test "x$ac_cv_berkdb4" = xyes)
case $target_os in
darwin*)
darwin=yes
;;
*)
darwin=no
;;
esac
AC_ARG_WITH(
macos-keychain,
[AS_HELP_STRING([--with-macos-keychain], [Support macOS keychain])],
[have_macos_keychain=$withval],
[have_macos_keychain=$darwin])
if test "x$have_macos_keychain" != xno; then
if test $darwin = no; then
AC_MSG_ERROR([Cannot use macOS Keychain outside macOS.])
fi
have_macos_keychain=yes
AC_DEFINE(HAVE_MACOS_KEYCHAIN, 1, [Define to 1 if you have the macOS Keychain Services API.])
AC_SUBST(KEYCHAIN_LIBS, ["-Wl,-framework,Security,-framework,CoreFoundation"])
fi
AC_CONFIG_FILES([Makefile src/Makefile isync.spec])
AC_CONFIG_FILES([Makefile src/Makefile src/compat/Makefile isync.spec])
AC_OUTPUT
AC_MSG_RESULT()
@ -270,11 +220,4 @@ if test "x$ac_cv_berkdb4" = xyes; then
else
AC_MSG_RESULT([Not using Berkeley DB])
fi
if test $darwin = yes; then
if test "x$have_macos_keychain" = xyes; then
AC_MSG_RESULT([Using macOS Keychain])
else
AC_MSG_RESULT([Not using macOS Keychain])
fi
fi
AC_MSG_RESULT()

13
debian/README.Debian vendored
View file

@ -3,11 +3,10 @@ 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"'
folder-hook +maildir 'macro index $ "<sync-mailbox>!isync -e 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).
where maildir is the name of the local mailbox (or its alias). This works well
so long as you are not modifying the IMAP mailbox outside of Mutt. However, if
you are using another mail program simultaneously Mutt will have the wrong idea
of the local mailbox flags and messages will start disappearing from its index
display (don't worry, they are still on disk).

5
debian/control vendored
View file

@ -26,6 +26,11 @@ Description: IMAP and MailDir mailbox synchronizer
deletions and flag changes can be propagated both ways. isync is suitable
for use in IMAP-disconnected mode.
.
The main application was much improved in version 1.0. Those improvements
lead to interface changes and the application being renamed to mbsync. The
application isync is now only a wrapper to keep compatibility with earlier
versions.
.
Features:
* Fine-grained selection of synchronization operations to perform
* Synchronizes single mailboxes or entire mailbox collections

View file

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

View file

@ -1,5 +1,10 @@
if with_compat
compat_dir = compat
endif
SUBDIRS = $(compat_dir)
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)
mbsync_LDADD = $(DB_LIBS) $(SSL_LIBS) $(SOCK_LIBS) $(SASL_LIBS) $(Z_LIBS)
noinst_HEADERS = common.h config.h driver.h sync.h socket.h
drv_proxy.$(OBJEXT): drv_proxy.inc

View file

@ -37,46 +37,25 @@ 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 __stringify(x) #x
#define stringify(x) __stringify(x)
#define shifted_bit(in, from, to) \
((int)(((uint)(in) / (from > to ? from / to : 1) * (to > from ? to / from : 1)) & to))
(((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)))
# define ATTR_PACKED(ref) __attribute__((packed,aligned(sizeof(ref))))
#else
# define ATTR_UNUSED
# define ATTR_NORETURN
# define ATTR_PRINTFLIKE(fmt,var)
# define ATTR_PACKED(ref)
#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__)
#if __GNUC__ >= 7
# define FALLTHROUGH __attribute__((fallthrough));
#else
# define FALLTHROUGH
@ -107,7 +86,6 @@ typedef unsigned long ulong;
#define VERBOSE 0x800
#define KEEPJOURNAL 0x1000
#define ZERODELAY 0x2000
#define FORCEASYNC 0x4000
extern int DFlags;
extern int JLimit;
@ -118,7 +96,7 @@ extern int Pid;
extern char Hostname[256];
extern const char *Home;
extern uint BufferLimit;
extern int BufferLimit;
extern int new_total[2], new_done[2];
extern int flags_total[2], flags_done[2];
@ -128,24 +106,23 @@ void stats( void );
/* util.c */
void ATTR_PRINTFLIKE(2, 0) vdebug( int, const char *, va_list va );
void ATTR_PRINTFLIKE(2, 0) vdebugn( int, const char *, va_list va );
void vdebug( int, const char *, va_list va );
void vdebugn( int, const char *, va_list va );
void ATTR_PRINTFLIKE(1, 2) info( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) infon( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) progress( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) notice( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) warn( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) error( const char *, ... );
void ATTR_PRINTFLIKE(1, 0) vsys_error( const char *, va_list va );
void ATTR_PRINTFLIKE(1, 2) sys_error( const char *, ... );
void flushn( void );
typedef struct string_list {
struct string_list *next;
char string[1];
} string_list_t;
} ATTR_PACKED(void *) string_list_t;
void add_string_list_n( string_list_t **list, const char *str, uint len );
void add_string_list_n( string_list_t **list, const char *str, int len );
void add_string_list( string_list_t **list, const char *str );
void free_string_list( string_list_t *list );
@ -156,9 +133,9 @@ void *memrchr( const void *s, int c, size_t n );
size_t strnlen( const char *str, size_t maxlen );
#endif
int starts_with( const char *str, int strl, const char *cmp, uint cmpl );
int starts_with_upper( const char *str, int strl, const char *cmp, uint cmpl );
int equals( const char *str, int strl, const char *cmp, uint cmpl );
int starts_with( const char *str, int strl, const char *cmp, int cmpl );
int starts_with_upper( const char *str, int strl, const char *cmp, int cmpl );
int equals( const char *str, int strl, const char *cmp, int cmpl );
#ifndef HAVE_TIMEGM
time_t timegm( struct tm *tm );
@ -169,40 +146,35 @@ void *nfcalloc( size_t sz );
void *nfrealloc( void *mem, size_t sz );
char *nfstrndup( const char *str, size_t nchars );
char *nfstrdup( const char *str );
int ATTR_PRINTFLIKE(2, 0) nfvasprintf( char **str, const char *fmt, va_list va );
int nfvasprintf( char **str, const char *fmt, va_list va );
int ATTR_PRINTFLIKE(2, 3) nfasprintf( char **str, const char *fmt, ... );
int ATTR_PRINTFLIKE(3, 4) nfsnprintf( char *buf, int blen, const char *fmt, ... );
void ATTR_NORETURN oob( void );
void ATTR_NORETURN oom( void );
char *expand_strdup( const char *s );
int map_name( const char *arg, char **result, uint reserve, const char *in, const char *out );
int map_name( const char *arg, char **result, int reserve, const char *in, const char *out );
#define DEFINE_ARRAY_TYPE(T) \
typedef struct { \
T *data; \
uint size; \
} T##_array_t; \
typedef union { \
int size; \
} ATTR_PACKED(T *) T##_array_t; \
typedef struct { \
T##_array_t array; \
struct { \
T *data; \
uint size; \
uint alloc; \
}; \
} T##_array_alloc_t; \
int alloc; \
} ATTR_PACKED(T *) T##_array_alloc_t; \
static INLINE T *T##_array_append( T##_array_alloc_t *arr ) \
{ \
if (arr->size == arr->alloc) { \
if (arr->array.size == arr->alloc) { \
arr->alloc = arr->alloc * 2 + 100; \
arr->data = nfrealloc( arr->data, arr->alloc * sizeof(T) ); \
arr->array.data = nfrealloc( arr->array.data, arr->alloc * sizeof(T) ); \
} \
return &arr->data[arr->size++]; \
return &arr->array.data[arr->array.size++]; \
}
#define ARRAY_INIT(arr) \
do { (arr)->data = NULL; (arr)->size = (arr)->alloc = 0; } while (0)
do { (arr)->array.data = 0; (arr)->array.size = (arr)->alloc = 0; } while (0)
#define ARRAY_SQUEEZE(arr) \
do { \
@ -216,7 +188,7 @@ int find_uint_array( const uint_array_t array, uint value );
void arc4_init( void );
uchar arc4_getbyte( void );
uint bucketsForSize( uint size );
int bucketsForSize( int size );
typedef struct list_head {
struct list_head *next, *prev;
@ -226,16 +198,15 @@ typedef struct notifier {
struct notifier *next;
void (*cb)( int what, void *aux );
void *aux;
#ifdef HAVE_POLL_H
uint index;
#ifdef HAVE_SYS_POLL_H
int index;
#else
int fd;
short events;
int fd, events;
#endif
} notifier_t;
#ifdef HAVE_POLL_H
# include <poll.h>
#ifdef HAVE_SYS_POLL_H
# include <sys/poll.h>
#else
# define POLLIN 1
# define POLLOUT 4
@ -243,8 +214,7 @@ typedef struct notifier {
#endif
void init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux );
void conf_notifier( notifier_t *sn, short and_events, short or_events );
short notifier_config( notifier_t *sn );
void conf_notifier( notifier_t *sn, int and_events, int or_events );
void wipe_notifier( notifier_t *sn );
typedef struct {
@ -257,7 +227,7 @@ typedef struct {
void init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux );
void conf_wakeup( wakeup_t *tmr, int timeout );
void wipe_wakeup( wakeup_t *tmr );
static INLINE int ATTR_UNUSED pending_wakeup( wakeup_t *tmr ) { return tmr->links.next != NULL; }
static INLINE int pending_wakeup( wakeup_t *tmr ) { return tmr->links.next != 0; }
void main_loop( void );

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

@ -0,0 +1 @@
/isync

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

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

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

@ -0,0 +1,551 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "isync.h"
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <pwd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
static char *
my_strndup( const char *s, size_t nchars )
{
char *r = nfmalloc( sizeof(char) * (nchars + 1) );
memcpy( r, s, nchars );
r[nchars] = 0;
return r;
}
char *
expand_strdup( const char *s )
{
struct passwd *pw;
const char *p, *q;
char *r;
if (*s == '~') {
s++;
if (!*s) {
p = 0;
q = Home;
} else if (*s == '/') {
p = s + 1;
q = Home;
} else {
if ((p = strchr( s, '/' ))) {
r = my_strndup( s, (int)(p - s) );
pw = getpwnam( r );
free( r );
p++;
} else
pw = getpwnam( s );
if (!pw)
return 0;
q = pw->pw_dir;
}
nfasprintf( &r, "%s/%s", q, p ? p : "" );
return r;
} else if (*s != '/' && xmaildir) {
nfasprintf( &r, "%s/%s", xmaildir, s );
return r;
} else
return nfstrdup( s );
}
static int
is_true( const char *val )
{
return
!strcasecmp( val, "yes" ) ||
!strcasecmp( val, "true" ) ||
!strcasecmp( val, "on" ) ||
!strcmp( val, "1" );
}
void
load_config( const char *path, config_t ***stor )
{
config_t **sstor, *cfg;
FILE *fp;
char *p, *cmd, *val;
int line = 0;
char buf[1024];
if (!(fp = fopen( path, "r" ))) {
if (errno != ENOENT)
sys_error( "Cannot read config file '%s'", path );
return;
}
if (!Quiet && !Debug && !Verbose)
printf( "Reading configuration file %s\n", path );
buf[sizeof(buf) - 1] = 0;
cfg = &global;
while (fgets( buf, sizeof(buf) - 1, fp )) {
p = buf;
cmd = next_arg( &p );
val = next_arg( &p );
line++;
if (!cmd || *cmd == '#')
continue;
if (!val) {
fprintf( stderr, "%s:%d: parameter missing\n", path, line );
continue;
}
if (!strcasecmp( "Mailbox", cmd )) {
if (o2o)
break;
cfg = **stor = nfmalloc( sizeof(config_t) );
*stor = &cfg->next;
memcpy( cfg, &global, sizeof(config_t) );
/* not expanded at this point */
cfg->path = nfstrdup( val );
} else if (!strcasecmp( "OneToOne", cmd )) {
if (boxes) {
forbid:
fprintf( stderr,
"%s:%d: keyword '%s' allowed only in global section\n",
path, line, cmd );
continue;
}
o2o = is_true( val );
} else if (!strcasecmp( "Maildir", cmd )) {
if (boxes)
goto forbid;
maildir = nfstrdup( val );
xmaildir = expand_strdup( val );
} else if (!strcasecmp( "Folder", cmd )) {
if (boxes)
goto forbid;
folder = nfstrdup( val );
} else if (!strcasecmp( "Inbox", cmd )) {
if (boxes)
goto forbid;
inbox = nfstrdup( val );
} else if (!strcasecmp( "Host", cmd )) {
if (starts_with( val, -1, "imaps:", 6 )) {
val += 6;
cfg->use_imaps = 1;
cfg->port = 993;
cfg->use_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 ))
fprintf( stderr, "Warning: UseSSLv2 is no longer supported\n" );
else if (!strcasecmp( "UseSSLv3", cmd ))
cfg->use_sslv3 = is_true( val );
else if (!strcasecmp( "UseTLSv1", cmd ))
cfg->use_tlsv1 = is_true( val );
else if (!strcasecmp( "RequireCRAM", cmd ))
cfg->require_cram = is_true( val );
else if (buf[0])
fprintf( stderr, "%s:%d: unknown keyword '%s'\n", path, line, cmd );
}
fclose( fp );
if (o2o) {
if (!global.host && !global.tunnel) {
fprintf( stderr, "Neither Host nor Tunnel given to OneToOne. Aborting.\n" );
exit( 1 );
}
} else
for (sstor = &boxes; (cfg = *sstor); ) {
if (!cfg->host && !cfg->tunnel) {
fprintf( stderr, "Mailbox '%s' has neither Host nor Tunnel. Skipping.\n",
cfg->alias ? cfg->alias : cfg->path );
if (&cfg->next == *stor)
*stor = sstor;
*sstor = cfg->next;
continue;
}
sstor = &cfg->next;
}
}
static const char *
tb( int on )
{
return on ? "yes" : "no";
}
static const char *
quotify( const char *str )
{
char *ostr;
int i;
for (i = 0; str[i]; i++) {
if (isspace( str[i] )) {
nfasprintf( &ostr, "\"%s\"", str );
return ostr;
}
}
return str;
}
static void
write_imap_server( FILE *fp, config_t *cfg )
{
config_t *pbox;
char *p, *p2;
int hl, a1, a2, a3, a4;
char buf[128], ubuf[64];
static int tunnels;
/* The old server names determine the derived store names. They are kinda stupid,
* but can't be changed, because store names are encoded in state file names. */
if (cfg->tunnel)
nfasprintf( (char **)&cfg->old_server_name, "tunnel%d", ++tunnels );
else {
if (sscanf( cfg->host, "%d.%d.%d.%d", &a1, &a2, &a3, &a4 ) == 4)
hl = nfsnprintf( buf, sizeof(buf), "%s", cfg->host );
else {
/* XXX this does not avoid clashes. add port? */
p = strrchr( cfg->host, '.' );
if (!p)
hl = nfsnprintf( buf, sizeof(buf), "%s", cfg->host );
else {
hl = nfsnprintf( buf, sizeof(buf), "%.*s", (int)(p - cfg->host), cfg->host );
p2 = strrchr( buf, '.' );
if (p2)
hl = sprintf( buf, "%s", p2 + 1 );
}
}
if (boxes) /* !o2o */
for (pbox = boxes; pbox != cfg; pbox = pbox->next)
if (equals( pbox->old_server_name, -1, buf, hl )) {
nfasprintf( (char **)&cfg->old_server_name, "%s-%d", buf, ++pbox->old_servers );
goto gotsrv;
}
cfg->old_server_name = nfstrdup( buf );
cfg->old_servers = 1;
gotsrv: ;
}
/* The "new" server names determine the names of the accounts themselves.
* They are optimized for descriptiveness, e.g. in password prompts. */
if (cfg->user)
nfsnprintf( ubuf, sizeof(ubuf), "%s@", cfg->user );
else
ubuf[0] = 0;
if (!cfg->host)
hl = nfsnprintf( buf, sizeof(buf), "%stunnel", ubuf );
else {
if (cfg->port != (cfg->use_imaps ? 993 : 143))
hl = nfsnprintf( buf, sizeof(buf), "%s%s_%d", ubuf, cfg->host, cfg->port );
else
hl = nfsnprintf( buf, sizeof(buf), "%s%s", ubuf, cfg->host );
}
if (boxes) /* !o2o */
for (pbox = boxes; pbox != cfg; pbox = pbox->next)
if (equals( pbox->server_name, -1, buf, hl )) {
nfasprintf( (char **)&cfg->server_name, "%s-%d", buf, ++pbox->servers );
goto ngotsrv;
}
cfg->server_name = nfstrdup( buf );
cfg->servers = 1;
ngotsrv: ;
fprintf( fp, "IMAPAccount %s\n", cfg->server_name );
if (cfg->tunnel)
fprintf( fp, "Tunnel \"%s\"\n", cfg->tunnel );
else {
if (cfg->use_imaps)
fprintf( fp, "Host imaps:%s\n", cfg->host );
else
fprintf( fp, "Host %s\n", cfg->host );
fprintf( fp, "Port %d\n", cfg->port );
}
if (cfg->user)
fprintf( fp, "User %s\n", quotify( cfg->user ) );
if (cfg->pass)
fprintf( fp, "Pass %s\n", quotify( cfg->pass ) );
fprintf( fp, "RequireCRAM %s\nRequireSSL %s\n"
"UseSSLv3 %s\nUseTLSv1 %s\nUseTLSv1.1 %s\nUseTLSv1.2 %s\n",
tb(cfg->require_cram), tb(cfg->require_ssl),
tb(cfg->use_sslv3), tb(cfg->use_tlsv1), tb(cfg->use_tlsv1), tb(cfg->use_tlsv1) );
if ((cfg->use_imaps || cfg->use_sslv3 || cfg->use_tlsv1) && cfg->cert_file)
fprintf( fp, "CertificateFile %s\n", quotify( cfg->cert_file ) );
fputc( '\n', fp );
}
static void
write_imap_store( FILE *fp, config_t *cfg )
{
if (cfg->stores > 1)
nfasprintf( (char **)&cfg->store_name, "%s-%d", cfg->old_server_name, cfg->stores );
else
cfg->store_name = cfg->old_server_name;
fprintf( fp, "IMAPStore %s\nAccount %s\n",
cfg->store_name, cfg->server_name );
if (*folder)
fprintf( fp, "Path %s\n", quotify( folder ) );
else
fprintf( fp, "UseNamespace %s\n", tb(cfg->use_namespace) );
if (inbox)
fprintf( fp, "MapInbox %s\n", quotify( inbox ) );
if (cfg->copy_deleted_to)
fprintf( fp, "Trash %s\n", quotify( cfg->copy_deleted_to ) );
fputc( '\n', fp );
}
static void
write_channel_parm( FILE *fp, config_t *cfg )
{
if (cfg->max_size)
fprintf( fp, "MaxSize %d\n", cfg->max_size );
if (cfg->max_messages)
fprintf( fp, "MaxMessages %d\n", cfg->max_messages );
if (cfg->delete && !global.delete && !delete)
fputs( "Sync All\n", fp );
if (cfg->expunge && !global.expunge && !expunge)
fputs( "Expunge Both\n", fp );
fputc( '\n', fp );
}
static int
mstrcmp( const char *s1, const char *s2 )
{
if (s1 == s2)
return 0;
if (!s1 || !s2)
return 1;
return strcmp( s1, s2 );
}
void
write_config( int fd )
{
FILE *fp;
const char *cn, *scn;
char *path, *local_box, *local_store;
config_t *box, *sbox, *pbox;
int pl;
if (!(fp = fdopen( fd, "w" ))) {
perror( "fdopen" );
return;
}
fputs( "SyncState *\n", fp );
if (!global.delete && !delete)
fputs( "Sync New ReNew Flags\n", fp );
if (global.expunge || expunge)
fputs( "Expunge Both\n", fp );
fputc( '\n', fp );
if (o2o) {
write_imap_server( fp, &global );
write_imap_store( fp, &global );
fprintf( fp, "MaildirStore local\nPath %s/\n", quotify( maildir ) );
if (!inbox) { /* just in case listing actually produces an INBOX ... */
nfasprintf( (char **)&inbox, "%s/INBOX", maildir );
fprintf( fp, "Inbox %s\n", quotify( inbox ) );
}
if (altmap > 0)
fputs( "AltMap yes\n", fp );
fprintf( fp, "\nChannel o2o\nMaster :%s:\nSlave :local:\nPattern %%\n", global.store_name );
write_channel_parm( fp, &global );
} else {
for (box = boxes; box; box = box->next) {
for (pbox = boxes; pbox != box; pbox = pbox->next) {
if (box->tunnel) {
if (mstrcmp( pbox->tunnel, box->tunnel ))
continue;
} else {
if (mstrcmp( pbox->host, box->host ) ||
pbox->use_imaps != box->use_imaps ||
pbox->port != box->port)
continue;
}
if (mstrcmp( pbox->user, box->user ) ||
mstrcmp( pbox->pass, box->pass )) /* nonsense */
continue;
if ((box->use_imaps ||
box->use_sslv3 || box->use_tlsv1) &&
mstrcmp( pbox->cert_file, box->cert_file )) /* nonsense */
continue;
if (pbox->use_imaps != box->use_imaps ||
pbox->use_sslv3 != box->use_sslv3 ||
pbox->use_tlsv1 != box->use_tlsv1)
continue;
box->server_name = pbox->server_name;
for (sbox = boxes; sbox != box; sbox = sbox->next) {
if (sbox->server_name != box->server_name ||
mstrcmp( sbox->copy_deleted_to, box->copy_deleted_to ) ||
(!*folder && sbox->use_namespace != box->use_namespace))
continue;
box->store_name = sbox->store_name;
goto gotall;
}
box->stores = ++pbox->stores;
goto gotsrv;
}
write_imap_server( fp, box );
box->stores = 1;
gotsrv:
write_imap_store( fp, box );
gotall:
path = expand_strdup( box->path );
if (starts_with( path, -1, Home, HomeLen ) && path[HomeLen] == '/')
nfasprintf( &path, "~%s", path + HomeLen );
local_store = local_box = strrchr( path, '/' ) + 1;
pl = local_store - path;
/* try to re-use existing store */
for (pbox = boxes; pbox != box; pbox = pbox->next)
if (pbox->local_store_path && equals( pbox->local_store_path, -1, path, pl ))
goto gotstor;
box->local_store_path = my_strndup( path, pl );
/* derive a suitable name */
if (!strcmp( box->local_store_path, "/var/mail/" ) || !strcmp( box->local_store_path, "/var/spool/mail/" )) {
local_store = nfstrdup( "spool" );
} else if (!strcmp( box->local_store_path, "~/" )) {
local_store = nfstrdup( "home" );
} else {
local_store = memrchr( box->local_store_path, '/', pl - 1 );
if (local_store) {
local_store = my_strndup( local_store + 1, pl - 2 - (local_store - box->local_store_path) );
for (pl = 0; local_store[pl]; pl++)
local_store[pl] = tolower( local_store[pl] );
} else {
local_store = nfstrdup( "local" );
}
}
/* avoid name clashes with imap stores */
for (pbox = boxes; pbox != box; pbox = pbox->next)
if (!strcmp( pbox->store_name, local_store )) {
nfasprintf( &local_store, "local_%s", local_store );
goto gotsdup;
}
gotsdup:
/* avoid name clashes with other local stores */
for (pbox = boxes; pbox != box; pbox = pbox->next)
if (pbox->local_store_name && !strcmp( pbox->local_store_name, local_store )) {
nfasprintf( (char **)&box->local_store_name, "%s-%d", local_store, ++pbox->local_stores );
goto gotdup;
}
box->local_store_name = local_store;
box->local_stores = 1;
gotdup:
fprintf( fp, "MaildirStore %s\nPath %s\n", box->local_store_name, quotify( box->local_store_path ) );
if (altmap > 0)
fputs( "AltMap yes\n", fp );
fputc( '\n', fp );
pbox = box;
gotstor:
if (box->alias)
cn = box->alias;
else {
cn = strrchr( box->path, '/' );
if (cn)
cn++;
else
cn = box->path;
}
for (sbox = boxes; sbox != box; sbox = sbox->next) {
if (sbox->alias)
scn = sbox->alias;
else {
scn = strrchr( sbox->path, '/' );
if (scn)
scn++;
else
scn = sbox->path;
}
if (mstrcmp( cn, scn ))
continue;
nfasprintf( (char **)&box->channel_name, "%s-%d", cn, ++sbox->channels );
goto gotchan;
}
box->channels = 1;
box->channel_name = cn;
gotchan:
fprintf( fp, "Channel %s\nMaster :%s:%s\nSlave :%s:%s\n",
box->channel_name, box->store_name, quotify( box->box ), pbox->local_store_name, quotify( local_box ) );
write_channel_parm( fp, box );
}
}
fclose( fp );
}
config_t *
find_box( const char *s )
{
config_t *p;
char *t;
if (starts_with( s, -1, Home, HomeLen ) && s[HomeLen] == '/')
s += HomeLen + 1;
for (p = boxes; p; p = p->next) {
if (!strcmp( s, p->path ) || (p->alias && !strcmp( s, p->alias )))
return p;
/* check to see if the full pathname was specified on the
* command line.
*/
t = expand_strdup( p->path );
if (!strcmp( s, t )) {
free( t );
return p;
}
free( t );
}
return 0;
}

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

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

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

@ -0,0 +1,327 @@
.\" isync - mbsync wrapper: IMAP4 to Maildir mailbox synchronizer
.\" Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
.\" Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
.\"
.\" This program is free software; you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation; either version 2 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>.
.\"
.
.TH isync 1 "2010 Feb 7"
.
.SH NAME
isync - synchronize IMAP4 and Maildir mailboxes
.
.SH SYNOPSIS
\fBisync\fR [\fIoptions\fR ...] {\fImailbox\fR ...|\fI-a\fR|\fI-l\fR}
.
.SH DESCRIPTION
\fBisync\fR is a command line application which synchronizes local
Maildir mailboxes with remote IMAP4 mailboxes, suitable for use in
IMAP-disconnected mode. Multiple copies of the remote IMAP4 mailboxes can
be maintained, and all flags are synchronized.
.br
\fBisync\fR is only a wrapper binary around \fBmbsync\fR to simplify upgrades.
It will automatically migrate the UID mapping from previous versions of
\fBisync\fR (even before 0.8) to the new format, and transparently call
\fBmbsync\fR. If you were using \fBisync\fR version 0.8 or 0.9.x you might
want to use \fBmdconvert\fR to convert the mailboxes to the more efficient
\fBnative\fR UID storage scheme after migrating them.
.br
\fBisync\fR is deprecated. Please use the \fB-w\fR option to permanently
migrate the configuration and start using \fBmbsync\fR directly.
.
.SH OPTIONS
.TP
\fB-c\fR, \fB--config\fR \fIfile\fR
Read configuration from \fIfile\fR.
By default, the configuration is read from ~/.isyncrc if it exists.
.TP
\fB-1\fR, \fB--one-to-one\fR
Instead of using the mailbox specifications in ~/.isyncrc, isync will pick up
all mailboxes from the local directory and remote folder and map them 1:1
onto each other according to their names.
.TP
\fB-I\fR, \fB--inbox\fR \fImailbox\fR
Exception to the 1:1 mapping created by -1: the special IMAP mailbox \fIINBOX\fR
is mapped to the local \fImailbox\fR (relative to the maildir).
.TP
\fB-a\fR, \fB--all\fR
Synchronize all mailboxes (either specified in ~/.isyncrc or determined by the
1:1 mapping).
.TP
\fB-l\fR, \fB--list\fR
Don't synchronize anything, but list all mailboxes and exit.
.TP
\fB-L\fR, \fB--create-local\fR
Automatically create the local Maildir mailbox if it doesn't already
exist.
.TP
\fB-R\fR, \fB--create-remote\fR
Automatically create the remote IMAP mailbox if it doesn't already exist.
.TP
\fB-C\fR, \fB--create\fR
Automatically create any mailboxes if they don't already exist.
This is simply a combination of -L and -R.
.TP
\fB-d\fR, \fB--delete\fR
Causes \fBisync\fR to propagate message deletions.
By default, \fIdead\fR messages are \fBnot\fR deleted.
.TP
\fB-e\fR, \fB--expunge\fR
Causes \fBisync\fR to permanently remove all messages marked for deletion.
By default, \fIdeleted\fR messages are \fBnot\fR expunged.
.TP
\fB-f\fR, \fB--fast\fR
Only fetch new messages existing on the server into the local mailbox.
Message deletions and flag changes will not be propagated.
.TP
\fB-h\fR, \fB--help\fR
Displays a summary of command line options
.TP
\fB-p\fR, \fB--port\fR \fIport\fR
Specifies the port on the IMAP server to connect to (default: 143 for imap,
993 for imaps)
.TP
\fB-q\fR, \fB--quiet\fR
Suppress informational messages.
If specified twice, suppress warning messages as well.
.TP
\fB-r\fR, \fB--remote\fR \fIbox\fR
Specifies the name of the remote IMAP mailbox to synchronize with
(Default: INBOX)
.TP
\fB-s\fR, \fB--host\fR [\fBimaps:\fR]\fIhost\fR
Specifies the hostname of the IMAP server
.TP
\fB-u\fR, \fB--user\fR \fIuser\fR
Specifies the login name to access the IMAP server (default: $USER)
.TP
\fB-P\fR, \fB--pass\fR \fIpassword\fR
Specifies the password to access the IMAP server (prompted for by default)
.TP
\fB-M\fR, \fB--maildir\fR \fIdir\fR
Specifies the location for your local mailboxes.
.TP
\fB-F\fR, \fB--folder\fR \fIfolder\fR/
Specifies the location for your remote mailboxes.
.TP
\fB-v\fR, \fB--version\fR
Displays \fBisync\fR version information.
.TP
\fB-V\fR, \fB--verbose\fR
Enables \fIverbose\fR mode, which displays the IMAP4 network traffic.
.TP
\fB-D\fR, \fB--debug\fR
Enable printing of \fIdebug\fR messages.
.TP
\fB-w\fR, \fB--write\fR
Don't run \fBmbsync\fR, but instead write a permanent config file for it.
The UID mappings of all configured mailboxes will be migrated.
Note that most command line options that would affect an actual sync operation
will be incorporated into the new config file as well; exceptions are
--fast and --create[-remote|-local].
The name of the new config file is determined by replacing the last occurrence
of "isync" with "mbsync", or appending ".mbsync" if "isync" was not found.
.TP
\fB-W\fR, \fB--writeto\fR \fIfile\fR
Like \fB-w\fR, but use the specified name for the new config file.
.
.SH CONFIGURATION
\fBisync\fR by default reads \fI~/.isyncrc\fR to load configuration data.
Each non-empty line of the configuration file that does not start with a
hash mark consists of a command.
The following commands are understood:
.TP
\fBMailbox\fR \fIpath\fR
Defines a local Maildir mailbox. All configuration commands following this
line, up until the next \fIMailbox\fR command, apply to this mailbox only.
.
.TP
\fBHost\fR [\fBimaps:\fR]\fIname\fR
Defines the DNS name or IP address of the IMAP server. If the hostname is
prefixed with \fBimaps:\fR the connection is assumed to be a SSL connection
to port 993 (though you can change this by placing a \fBPort\fR command
\fBafter\fR the \fBHost\fR command).
Note that modern servers support SSL on the default port 143.
\fBisync\fR will always attempt to use SSL if available.
.
.TP
\fBPort\fR \fIport\fR
Defines the TCP port number of the IMAP server (Default: 143 for imap,
993 for imaps)
.
.TP
\fBBox\fR \fImailbox\fR
Defines the name of the remote IMAP mailbox associated with the local
Maildir mailbox (Default: INBOX)
.
.TP
\fBUser\fR \fIusername\fR
Defines the login name on the IMAP server (Default: current user)
.
.TP
\fBPass\fR \fIpassword\fR
Defines the password for \fIusername\fR on the IMAP server.
Note that this option is \fBNOT\fR required.
If no password is specified in the configuration file, \fBisync\fR
will prompt you for it.
.
.TP
\fBAlias\fR \fIstring\fR
Defines an alias for the mailbox which can be used as a shortcut on the
command line.
.
.TP
\fBCopyDeletedTo\fR \fImailbox\fR
Specifies the remote IMAP mailbox to copy deleted messages to prior to
expunging (Default: none).
.
.TP
\fBDelete\fR \fIyes\fR|\fIno\fR
Specifies whether message deletions are propagated. (Default: no).
\fBNOTE:\fR The \fI-d\fR command line option overrides this setting when
set to \fIno\fR.
.
.TP
\fBExpunge\fR \fIyes\fR|\fIno\fR
Specifies whether deleted messages are expunged. (Default: no).
\fBNOTE:\fR The \fI-e\fR command line option overrides this setting when
set to \fIno\fR.
.
.TP
\fBMailDir\fR \fIdirectory\fR
Specifies the location of your local mailboxes if a relative path is
specified in a \fIMailbox\fR command (Default: \fI~\fR).
\fBNOTE:\fR This directive is allowed only in the \fIglobal\fR
section (see below).
.
.TP
\fBFolder\fR \fIdirectory\fR/
Specifies the location of your IMAP mailboxes
specified in \fIBox\fR commands (Default: \fI""\fR).
\fBNOTE:\fR You \fBmust\fR append the hierarchy delimiter (usually
a slash) to this specification.
\fBNOTE 2:\fR This directive is allowed only in the \fIglobal\fR
section (see below).
.
.TP
\fBMaxMessages\fR \fIcount\fR
Sets the number of messages \fBisync\fR should keep in the local copy of a
mailbox.
This is useful for mailboxes where you keep a complete archive on the server,
but want to mirror only the last messages (for instance, for mailing lists).
The messages that were the first to arrive in the mailbox (independently of the
actual date of the message) will be deleted first.
Messages that are flagged (marked as important) and unread messages will not be
automatically deleted.
If \fIcount\fR is 0, the maximum number of messages is \fBunlimited\fR.
(Default: 0)
.
.TP
\fBMaxSize\fR \fIbytes\fR
Messages larger than that many bytes will not be transferred over the wire.
This is useful for weeding out messages with large attachments.
If \fIbytes\fR is 0, the maximum file size is \fBunlimited\fR.
(Default: 0)
.
.TP
\fBTunnel\fR \fIcommand\fR
Specify a command to run to establish a connection rather than opening a TCP
socket. This allows you to run an IMAP session over an SSH tunnel, for
example.
.TP
\fBUseNamespace\fR \fIyes\fR|\fIno\fR
Selects whether the server's first "personal" NAMESPACE should be prefixed to
mailbox names. Disabling this makes sense for some broken IMAP servers.
This option is meaningless if a \fIFolder\fR was specified.
(Default: \fIyes\fR)
.
.TP
\fBRequireCRAM\fR \fIyes\fR|\fIno\fR
If set to \fIyes\fR, \fBisync\fR will abort the connection if no CRAM-MD5
authentication is possible. (Default: \fIno\fR)
.
.TP
\fBRequireSSL\fR \fIyes\fR|\fIno\fR
\fBisync\fR will abort the connection if a TLS/SSL session cannot be
established with the IMAP server. (Default: \fIyes\fR)
.
.TP
\fBCertificateFile\fR \fIpath\fR
File containing additional X.509 certificates used to verify server
identities. Directly matched peer certificates are always trusted,
regardless of validity.
.br
Note that the system's default certificate store is always used
and should not be specified here.
.
.TP
\fBUseSSLv2\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use SSLv2 for communication with the IMAP server over SSL?
(Default: \fIno\fR)
.
.TP
\fBUseSSLv3\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use SSLv3 for communication with the IMAP server over SSL?
(Default: \fIyes\fR if the imaps port is used, otherwise \fIno\fR)
.
.TP
\fBUseTLSv1\fR \fIyes\fR|\fIno\fR
Should \fBisync\fR use TLSv1.x for communication with the IMAP server over SSL?
(Default: \fIyes\fR)
.
.TP
\fBOneToOne\fR
\fBisync\fR will ignore any \fIMailbox\fR specifications and instead pick up
all mailboxes from the local \fIMailDir\fR and remote \fIFolder\fR and map
them 1:1 onto each other according to their names.
\fBNOTE:\fR This directive is allowed only in the \fIglobal\fR
section (see below).
.
.TP
\fBInbox\fR \fImailbox\fR
Exception to the OneToOne mapping: the special IMAP mailbox \fIINBOX\fR
is mapped to the local \fImailbox\fR (relative to the \fIMailDir\fR).
\fBNOTE:\fR This directive is only meaningful in the \fIglobal\fR
section (see below).
.
.P
Configuration commands that appear prior to the first \fBMailbox\fR
command are considered to be \fIglobal\fR
options which are used as defaults when those specific options are not
specifically set for a defined Mailbox. For example, if you use the same
login name for several IMAP servers, you can put a \fBUser\fR command before
the first \fBMailbox\fR command, and then leave out the \fBUser\fR command
in the sections for each mailbox.
\fBisync\fR will then use the global value by default.
.
.SH FILES
.TP
.B ~/.isyncrc
Default configuration file
.
.SH BUGS
The configuration file takes precedence over command line options.
.br
Use -c /dev/null to work around.
.P
See the \fBINHERENT PROBLEMS\fR section in the \fBmbsync\fR man page, too.
.
.SH SEE ALSO
mbsync(1), mdconvert(1), mutt(1), maildir(5)
.P
Up to date information on \fBisync\fR can be found at http://isync.sf.net/
.
.SH AUTHORS
Originally written by Michael R. Elkins,
currently maintained by Oswald Buddenhagen.

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

@ -0,0 +1,116 @@
/*
* isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <autodefs.h>
#include <sys/types.h>
#include <stdarg.h>
#define as(ar) (sizeof(ar)/sizeof(ar[0]))
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_UNUSED __attribute__((unused))
# define ATTR_NORETURN __attribute__((noreturn))
# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
#else
# define ATTR_UNUSED
# define ATTR_NORETURN
# define ATTR_PRINTFLIKE(fmt,var)
#endif
#if __GNUC__ >= 7
# define FALLTHROUGH __attribute__((fallthrough));
#else
# define FALLTHROUGH
#endif
typedef struct config {
struct config *next;
const char *server_name;
const char *old_server_name;
int servers;
int old_servers;
char *host;
int port;
char *user;
char *pass;
char *tunnel;
unsigned int require_cram:1;
unsigned int require_ssl:1;
unsigned int use_imaps:1;
unsigned int use_sslv3:1;
unsigned int use_tlsv1:1;
char *cert_file;
const char *store_name;
int stores;
const char *local_store_name;
const char *local_store_path;
int local_stores;
char *copy_deleted_to;
unsigned int use_namespace:1;
const char *channel_name;
int channels;
const char *alias;
const char *box;
const char *path; /* path relative to .maildir, or absolute path */
int max_size;
unsigned int max_messages;
unsigned int expunge:1;
unsigned int delete:1;
} config_t;
extern int Quiet, Verbose, Debug;
extern const char *Home;
extern int HomeLen;
extern config_t global, *boxes;
extern const char *maildir, *xmaildir, *folder, *inbox;
extern int o2o, altmap, delete, expunge;
/* config.c */
void load_config( const char *, config_t *** );
void write_config( int );
char *expand_strdup( const char * );
config_t *find_box( const char * );
/* convert.c */
void convert( config_t * );
/* util.c */
char *next_arg( char ** );
void *nfmalloc( size_t sz );
void *nfrealloc( void *mem, size_t sz );
char *nfstrdup( const char *str );
int nfvasprintf( char **str, const char *fmt, va_list va );
int nfasprintf( char **str, const char *fmt, ... );
int nfsnprintf( char *buf, int blen, const char *fmt, ... );
void sys_error( const char *, ... );
void ATTR_NORETURN oob( void );
#ifndef HAVE_MEMRCHR
void *memrchr( const void *s, int c, size_t n );
#endif
int starts_with( const char *str, int strl, const char *cmp, int cmpl );
int equals( const char *str, int strl, const char *cmp, int cmpl );

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

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

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

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

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

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

View file

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

View file

@ -32,7 +32,6 @@ typedef struct {
int bufl;
int line;
int err;
int ms_warn;
char *cmd, *val, *rest;
} conffile_t;
@ -41,11 +40,11 @@ typedef struct {
char *get_arg( conffile_t *cfile, int required, int *comment );
char parse_bool( conffile_t *cfile );
int parse_bool( conffile_t *cfile );
int parse_int( conffile_t *cfile );
uint parse_size( conffile_t *cfile );
int parse_size( conffile_t *cfile );
int getcline( conffile_t *cfile );
int merge_ops( int cops, int ops[] );
int load_config( const char *filename );
int load_config( const char *filename, int pseudo );
#endif

View file

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

View file

@ -31,65 +31,51 @@ typedef struct driver driver_t;
#define FAIL_WAIT 1 /* Retry after some time (if at all) */
#define FAIL_FINAL 2 /* Don't retry until store reconfiguration */
#define STORE_CONF \
struct store_conf *next; \
char *name; \
driver_t *driver; \
const char *flat_delim; \
const char *map_inbox; \
const char *trash; \
uint max_size; /* off_t is overkill */ \
char trash_remote_new, trash_only_new;
typedef struct store_conf {
STORE_CONF
struct store_conf *next;
char *name;
driver_t *driver;
const char *path; /* should this be here? its interpretation is driver-specific */
const char *flat_delim;
const char *map_inbox;
const char *trash;
int max_size; /* off_t is overkill */
char trash_remote_new, trash_only_new;
} store_conf_t;
/* For message->flags */
/* Keep the mailbox driver flag definitions in sync: */
/* grep for MAILBOX_DRIVER_FLAG */
/* Keep the mailbox driver flag definitions in sync! */
/* The order is according to alphabetical maildir flag sort */
#define F_DRAFT (1<<0) /* Draft */
#define F_FLAGGED (1<<1) /* Flagged */
#define F_FORWARDED (1<<2) /* Passed */
#define F_ANSWERED (1<<3) /* Replied */
#define F_SEEN (1<<4) /* Seen */
#define F_DELETED (1<<5) /* Trashed */
#define NUM_FLAGS 6
#define F_ANSWERED (1<<2) /* Replied */
#define F_SEEN (1<<3) /* Seen */
#define F_DELETED (1<<4) /* Trashed */
#define NUM_FLAGS 5
/* For message->status */
#define M_RECENT (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
#define M_DEAD (1<<1) /* expunged */
#define M_FLAGS (1<<2) /* flags fetched */
// The following are only for IMAP FETCH response parsing
#define M_DATE (1<<3)
#define M_SIZE (1<<4)
#define M_BODY (1<<5)
#define M_HEADER (1<<6)
#define TUIDL 12
#define MESSAGE(message) \
message *next; \
struct sync_rec *srec; \
char *msgid; /* owned */ \
/* string_list_t *keywords; */ \
uint size; /* zero implies "not fetched" */ \
uint uid; \
uchar flags, status; \
char tuid[TUIDL];
typedef struct message {
MESSAGE(struct message)
struct message *next;
struct sync_rec *srec;
char *msgid; /* owned */
/* string_list_t *keywords; */
int size; /* zero implies "not fetched" */
uint uid;
uchar flags, status;
char tuid[TUIDL];
} message_t;
// For driver_t->prepare_load_box(), which may amend the passed flags.
// The drivers don't use the first two, but may set them if loading the
// particular range is required to handle some other flag; note that these
// ranges may overlap.
#define OPEN_OLD (1<<0) // Paired messages *in* this store.
#define OPEN_NEW (1<<1) // Messages (possibly) not yet propagated *from* this store.
#define OPEN_FLAGS (1<<2) // Note that fetch_msg() gets the flags regardless.
/* 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_OLD_SIZE (1<<3)
#define OPEN_NEW_SIZE (1<<4)
#define OPEN_EXPUNGE (1<<5)
#define OPEN_SETFLAGS (1<<6)
@ -99,18 +85,15 @@ typedef struct message {
#define UIDVAL_BAD ((uint)-1)
#define STORE(store) \
store *next; \
driver_t *driver; \
store##_conf *conf; /* foreign */
typedef struct store {
STORE(struct store)
struct store *next;
driver_t *driver;
store_conf_t *conf; /* foreign */
} store_t;
typedef struct {
char *data;
uint len;
int len;
time_t date;
uchar flags;
} msg_data_t;
@ -126,9 +109,6 @@ typedef struct {
#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,
@ -140,16 +120,12 @@ typedef struct {
This flag says that the driver will act upon (DFlags & VERBOSE).
*/
#define DRV_VERBOSE 2
/*
This flag says that the driver operates asynchronously.
*/
#define DRV_ASYNC 4
#define LIST_INBOX 1
#define LIST_PATH 2
#define LIST_PATH_MAYBE 4
#define xint uint // For auto-generation of appropriate printf() formats.
#define xint int // For auto-generation of appropriate printf() formats.
struct driver {
/* Return driver capabilities. */
@ -198,13 +174,10 @@ struct driver {
/* Open the selected mailbox.
* Note that this should not directly complain about failure to open. */
void (*open_box)( store_t *ctx,
void (*cb)( int sts, uint uidvalidity, void *aux ), void *aux );
void (*cb)( int sts, int 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 );
int (*get_uidnext)( store_t *ctx );
/* Confirm that the open mailbox is empty. */
int (*confirm_box_empty)( store_t *ctx );
@ -228,18 +201,16 @@ struct driver {
* 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.
* Messages starting with newuid need to have the TUID populated when OPEN_FIND is set.
* Messages up to seenuid need to have the Message-Id populated when OPEN_OLD_IDS is set.
* Messages up to seenuid need to have the size populated when OPEN_OLD_SIZE is set;
* likewise messages above seenuid 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 (*load_box)( store_t *ctx, uint minuid, uint maxuid, uint newuid, uint seenuid, 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,
/* Fetch the contents and flags of the given message from the current mailbox. */
void (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data,
void (*cb)( int sts, void *aux ), void *aux );
/* Store the given message to either the current mailbox or the trash folder.
@ -258,17 +229,17 @@ struct driver {
* a pre-fetched one (in which case the in-memory representation is updated),
* or it may be identifed by UID only. The operation may be delayed until commit()
* is called. */
void (*set_msg_flags)( store_t *ctx, message_t *msg, uint uid, int add, int del,
void (*set_msg_flags)( store_t *ctx, message_t *msg, uint uid, int add, int del, /* msg can be null, therefore uid as a fallback */
void (*cb)( int sts, void *aux ), void *aux );
/* Move the given message from the current mailbox to the trash folder.
* This may expunge the original message immediately, but it needn't to. */
void (*trash_msg)( store_t *ctx, message_t *msg,
void (*trash_msg)( store_t *ctx, message_t *msg, /* This may expunge the original message immediately, but it needn't to */
void (*cb)( int sts, void *aux ), void *aux );
/* Expunge deleted messages from the current mailbox and close it.
* There is no need to explicitly close a mailbox if no expunge is needed. */
void (*close_box)( store_t *ctx,
void (*close_box)( store_t *ctx, /* IMAP-style: expunge inclusive */
void (*cb)( int sts, void *aux ), void *aux );
/* Cancel queued commands which are not in flight yet; they will have their
@ -282,16 +253,16 @@ struct driver {
void (*commit_cmds)( store_t *ctx );
/* Get approximate amount of memory occupied by the driver. */
uint (*get_memory_usage)( store_t *ctx );
int (*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 * );
int 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 );
void parse_generic_store( store_conf_t *store, conffile_t *cfg );
store_t *proxy_alloc_store( store_t *real_ctx, const char *label );

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -21,27 +21,18 @@
#include "driver.h"
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
typedef struct gen_cmd gen_cmd_t;
typedef union proxy_store {
typedef struct {
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;
const char *label; // foreign
int ref_count;
driver_t *real_driver;
store_t *real_store;
void (*bad_callback)( void *aux );
void *bad_callback_aux;
};
void (*bad_callback)( void *aux );
void *bad_callback_aux;
} proxy_store_t;
static void ATTR_PRINTFLIKE(1, 2)
@ -64,13 +55,10 @@ debugn( const char *msg, ... )
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 const char Flags[] = { 'D', 'F', 'R', 'S', 'T' };
static char *
proxy_make_flags( uchar flags, char *buf )
proxy_make_flags( int flags, char *buf )
{
uint i, d;
@ -84,38 +72,20 @@ proxy_make_flags( uchar flags, char *buf )
static void
proxy_store_deref( proxy_store_t *ctx )
{
if (!--ctx->ref_count) {
assert( !pending_wakeup( &ctx->wakeup ) );
if (!--ctx->ref_count)
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;
typedef struct {
int ref_count;
int tag;
proxy_store_t *ctx;
} gen_cmd_t;
static gen_cmd_t *
proxy_cmd_new( proxy_store_t *ctx, uint sz )
proxy_cmd_new( proxy_store_t *ctx, int sz )
{
gen_cmd_t *cmd = nfmalloc( sz );
cmd->ref_count = 2;
@ -134,67 +104,6 @@ proxy_cmd_done( gen_cmd_t *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 )
@ -222,7 +131,7 @@ static @type@proxy_@name@( store_t *gctx@decl_args@ )
//# END
//# TEMPLATE REGULAR_VOID
static @type@proxy_@name@( store_t *gctx@decl_args@ )
static void proxy_@name@( store_t *gctx@decl_args@ )
{
proxy_store_t *ctx = (proxy_store_t *)gctx;
@ -236,39 +145,28 @@ static @type@proxy_@name@( store_t *gctx@decl_args@ )
//# 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@
};
typedef struct {
gen_cmd_t gen;
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@" );
@pre_print_cb_args@
debug( "%s[% 2d] Callback enter @name@@print_fmt_cb_args@\n", cmd->gen.ctx->label, cmd->gen.tag@print_pass_cb_args@ );
@print_cb_args@
cmd->callback( @pass_cb_args@cmd->callback_aux );
debug( "%s[% 2d] Callback leave @name@\n", cmd->gen.ctx->label, cmd->gen.tag );
proxy_cmd_done( &cmd->gen );
}
static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), void *aux )
static void
proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), void *aux )
{
proxy_store_t *ctx = (proxy_store_t *)gctx;
@ -277,19 +175,19 @@ static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@v
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@ );
debug( "%s[% 2d] Enter @name@@print_fmt_args@\n", ctx->label, cmd->gen.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@ );
debug( "%s[% 2d] Leave @name@\n", ctx->label, cmd->gen.tag );
proxy_cmd_done( &cmd->gen );
}
//# 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)
if (sts == DRV_OK) {
for (string_list_t *box = boxes; box; box = box->next)
debug( " %s\n", box->string );
}
//# END
@ -297,33 +195,35 @@ static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@v
//# 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_fmt_args , [%u,%s] (new >= %u, seen <= %u)
//# DEFINE load_box_print_pass_args , minuid, (maxuid == UINT_MAX) ? "inf" : (nfsnprintf( ubuf, sizeof(ubuf), "%u", maxuid ), ubuf), newuid, seenuid
//# DEFINE load_box_print_args
if (excs.size) {
debugn( " excs:" );
for (uint t = 0; t < excs.size; t++)
debugn( " %u", excs.data[t] );
for (int t = 0; t < excs.size; t++)
debugn( " %d", excs.data[t] );
debug( "\n" );
}
//# END
//# DEFINE load_box_pre_print_cb_args
static char fbuf[as(Flags) + 1];
//# 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_pass_cb_args , sts, total_msgs, 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",
if (sts == DRV_OK) {
for (message_t *msg = msgs; msg; msg = msg->next)
debug( " uid=%5u, flags=%4s, size=%6d, 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_pass_cb_args , 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 );
if (sts == DRV_OK) {
for (message_t *msg = msgs; msg; msg = msg->next)
debug( " uid=%5u, tuid=%." stringify(TUIDL) "s\n", msg->uid, msg->tuid );
}
//# END
@ -339,13 +239,13 @@ static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@v
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_fmt_cb_args , flags=%s, date=%lld, size=%d
//# 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 );
if (sts == DRV_OK && (DFlags & DEBUG_DRV_ALL)) {
printf( "%s=========\n", cmd->gen.ctx->label );
fwrite( cmd->data->data, cmd->data->len, 1, stdout );
printf( "%s=========\n", cmd->ctx->label );
printf( "%s=========\n", cmd->gen.ctx->label );
fflush( stdout );
}
//# END
@ -354,7 +254,7 @@ static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@v
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_fmt_args , flags=%s, date=%lld, size=%d, 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) {
@ -372,34 +272,27 @@ static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@v
//# 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 commit_cmds
static void
proxy_commit_cmds( store_t *gctx )
{
// Currently a dummy in all real drivers.
(void) gctx;
}
//# SPECIAL set_bad_callback
static void
proxy_set_bad_callback( store_t *gctx, void (*cb)( void *aux ), void *aux )
@ -413,11 +306,9 @@ proxy_set_bad_callback( store_t *gctx, void (*cb)( void *aux ), void *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 );
debug( "%sCallback leave bad store\n", ctx->label ); \
}
//# EXCLUDE alloc_store
@ -427,16 +318,13 @@ proxy_alloc_store( store_t *real_ctx, const char *label )
proxy_store_t *ctx;
ctx = nfcalloc( sizeof(*ctx) );
ctx->driver = &proxy_driver;
ctx->gen.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;
}

View file

@ -50,7 +50,6 @@ while (<$ins>) {
$defines{$define} = $conts;
$define = undef;
} else {
($_ eq "\n") or s/^\t// or die("DEFINE content is not indented: $_");
$conts .= $_;
}
} else {
@ -123,19 +122,13 @@ sub make_format($)
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";
push @cmd_table, "0";
next;
}
push @cmd_table, "proxy_$cmd_name";
@ -144,55 +137,34 @@ for (@ptypes) {
$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_/) {
if ($cmd_type eq "void " && $cmd_args =~ s/, void \(\*cb\)\( (.*)void \*aux \), void \*aux$//) {
my $cmd_cb_args = $1;
$replace{'decl_cb_args'} = $cmd_cb_args;
$replace{'pass_cb_args'} = make_args($cmd_cb_args);
my $cmd_print_cb_args = $cmd_cb_args =~ s/(.*), $/, $1/r;
$replace{'print_pass_cb_args'} = make_args($cmd_print_cb_args);
$replace{'print_fmt_cb_args'} = make_format($cmd_print_cb_args);
$template = "CALLBACK";
} elsif ($cmd_name =~ /^get_/) {
$template = "GETTER";
$replace{'fmt'} = type_to_format($cmd_type);
} elsif ($cmd_type eq "void ") {
$template = "REGULAR_VOID";
} 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);
$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}_(.*)$/);
$replace{$1} = $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;
$text =~ s/^\h*\@(\w+)\@\n/$replace{$1} \/\/ ""/smeg;
$text =~ s/\@(\w+)\@/$replace{$1} \/\/ ""/eg;
print $outh $text."\n";
my @not_used = grep { !defined($used{$_}) } keys %replace;
die("Fatal: unconsumed replacements in $cmd_name: ".join(" ", @not_used)."\n") if (@not_used);
}
die("Fatal: unconsumed DEFINEs: ".join(" ", keys %defines)."\n") if (%defines);
print $outh "struct driver proxy_driver = {\n".join("", map { "\t$_,\n" } @cmd_table)."};\n";
close $outh;

View file

@ -47,10 +47,10 @@ int Pid; /* for maildir and imap */
char Hostname[256]; /* for maildir */
const char *Home; /* for config */
uint BufferLimit = 10 * 1024 * 1024;
int BufferLimit = 10 * 1024 * 1024;
static int chans_total, chans_done;
static int boxes_total, boxes_done;
int chans_total, chans_done;
int boxes_total, boxes_done;
int new_total[2], new_done[2];
int flags_total[2], flags_done[2];
int trash_total[2], trash_done[2];
@ -78,8 +78,8 @@ PACKAGE " " VERSION " - mailbox synchronizer\n"
" -d, --delete propagate message deletions\n"
" -f, --flags propagate message flag changes\n"
" -N, --renew propagate previously not propagated new messages\n"
" -L, --pull propagate from far to near side\n"
" -H, --push propagate from near to far side\n"
" -L, --pull propagate from master to slave\n"
" -H, --push propagate from slave to master\n"
" -C, --create propagate creations of mailboxes\n"
" -R, --remove propagate deletions of mailboxes\n"
" -X, --expunge expunge deleted messages\n"
@ -92,7 +92,7 @@ PACKAGE " " VERSION " - mailbox synchronizer\n"
"\nIf neither --pull nor --push are specified, both are active.\n"
"If neither --new, --delete, --flags nor --renew are specified, all are active.\n"
"Direction and operation can be concatenated like --pull-new, etc.\n"
"--create, --remove, and --expunge can be suffixed with -far/-near.\n"
"--create, --remove, and --expunge can be suffixed with -master/-slave.\n"
"See the man page for details.\n"
"\nSupported mailbox formats are: IMAP4rev1, Maildir\n"
"\nCompile time options:\n"
@ -136,7 +136,7 @@ debug( const char *msg, ... )
}
#ifdef __linux__
static void ATTR_NORETURN
static void
crashHandler( int n )
{
int dpid;
@ -175,7 +175,7 @@ crashHandler( int n )
close( pip[1] );
close( pip[0] );
#endif
waitpid( dpid, NULL, 0 );
waitpid( dpid, 0, 0 );
break;
}
exit( 3 );
@ -205,7 +205,7 @@ stats( void )
if (l > cls)
buf[t][cls - 1] = '~';
}
progress( "\r%s F: %.*s N: %.*s", buf[2], cls, buf[0], cls, buf[1] );
progress( "\r%s M: %.*s S: %.*s", buf[2], cls, buf[0], cls, buf[1] );
}
static int
@ -248,8 +248,8 @@ is_inbox( const char *name )
static int
cmp_box_names( const void *a, const void *b )
{
const char *as = *(const char * const *)a;
const char *bs = *(const char * const *)b;
const char *as = *(const char **)a;
const char *bs = *(const char **)b;
int ai = is_inbox( as );
int bi = is_inbox( bs );
int di = bi - ai;
@ -262,9 +262,9 @@ static char **
filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns )
{
string_list_t *cpat;
char **boxarr = NULL;
char **boxarr = 0;
const char *ps;
uint not, fnot, pfxl, num = 0, rnum = 0;
int not, fnot, pfxl, num = 0, rnum = 0;
pfxl = prefix ? strlen( prefix ) : 0;
for (; boxes; boxes = boxes->next) {
@ -287,7 +287,7 @@ filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns
if (num + 1 >= rnum)
boxarr = nfrealloc( boxarr, (rnum = (rnum + 10) * 2) * sizeof(*boxarr) );
boxarr[num++] = nfstrdup( boxes->string + pfxl );
boxarr[num] = NULL;
boxarr[num] = 0;
}
}
qsort( boxarr, num, sizeof(*boxarr), cmp_box_names );
@ -297,18 +297,18 @@ filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns
static void
merge_actions( channel_conf_t *chan, int ops[], int have, int mask, int def )
{
if (ops[F] & have) {
chan->ops[F] &= ~mask;
chan->ops[F] |= ops[F] & mask;
chan->ops[N] &= ~mask;
chan->ops[N] |= ops[N] & mask;
} else if (!(chan->ops[F] & have)) {
if (global_conf.ops[F] & have) {
chan->ops[F] |= global_conf.ops[F] & mask;
chan->ops[N] |= global_conf.ops[N] & mask;
if (ops[M] & have) {
chan->ops[M] &= ~mask;
chan->ops[M] |= ops[M] & mask;
chan->ops[S] &= ~mask;
chan->ops[S] |= ops[S] & mask;
} else if (!(chan->ops[M] & have)) {
if (global_conf.ops[M] & have) {
chan->ops[M] |= global_conf.ops[M] & mask;
chan->ops[S] |= global_conf.ops[S] & mask;
} else {
chan->ops[F] |= def;
chan->ops[N] |= def;
chan->ops[M] |= def;
chan->ops[S] |= def;
}
}
}
@ -348,10 +348,9 @@ add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
{
channel_conf_t *chan;
chan_ent_t *ce;
box_ent_t *boxes = NULL, **mboxapp = &boxes, *mbox;
box_ent_t *boxes = 0, **mboxapp = &boxes, *mbox;
char *boxp, *nboxp;
size_t boxl;
char boxlist = 0;
int boxl, boxlist = 0;
if ((boxp = strchr( channame, ':' )))
*boxp++ = 0;
@ -359,18 +358,18 @@ add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
if (!strcmp( chan->name, channame ))
goto gotchan;
error( "No channel or group named '%s' defined.\n", channame );
return NULL;
return 0;
gotchan:
if (boxp) {
if (!chan->patterns) {
error( "Cannot override mailbox in channel '%s' - no Patterns.\n", channame );
return NULL;
return 0;
}
boxlist = 1;
do {
nboxp = strpbrk( boxp, ",\n" );
if (nboxp) {
boxl = (size_t)(nboxp - boxp);
boxl = nboxp - boxp;
*nboxp++ = 0;
} else {
boxl = strlen( boxp );
@ -380,8 +379,8 @@ add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
mbox->name = nfstrndup( boxp, boxl );
else
mbox->name = nfstrndup( "INBOX", 5 );
mbox->present[F] = mbox->present[N] = BOX_POSSIBLE;
mbox->next = NULL;
mbox->present[M] = mbox->present[S] = BOX_POSSIBLE;
mbox->next = 0;
*mboxapp = mbox;
mboxapp = &mbox->next;
boxes_total++;
@ -426,12 +425,12 @@ int
main( int argc, char **argv )
{
main_vars_t mvars[1];
chan_ent_t *chans = NULL, **chanapp = &chans;
chan_ent_t *chans = 0, **chanapp = &chans;
group_conf_t *group;
channel_conf_t *chan;
string_list_t *channame;
char *config = NULL, *opt, *ochar;
int oind, cops = 0, op, ops[2] = { 0, 0 }, ms_warn = 0;
char *config = 0, *opt, *ochar;
int oind, cops = 0, op, ops[2] = { 0, 0 }, pseudo = 0;
tzset();
gethostname( Hostname, sizeof(Hostname) );
@ -447,7 +446,7 @@ main( int argc, char **argv )
memset( mvars, 0, sizeof(*mvars) );
mvars->t[1] = 1;
for (oind = 1, ochar = NULL; ; ) {
for (oind = 1, ochar = 0; ; ) {
if (!ochar || !*ochar) {
if (oind >= argc)
break;
@ -504,26 +503,22 @@ main( int argc, char **argv )
goto badopt;
DFlags |= op;
} else if (!strcmp( opt, "pull" ))
cops |= XOP_PULL, ops[F] |= XOP_HAVE_TYPE;
cops |= XOP_PULL, ops[M] |= XOP_HAVE_TYPE;
else if (!strcmp( opt, "push" ))
cops |= XOP_PUSH, ops[F] |= XOP_HAVE_TYPE;
cops |= XOP_PUSH, ops[M] |= XOP_HAVE_TYPE;
else if (starts_with( opt, -1, "create", 6 )) {
opt += 6;
op = OP_CREATE|XOP_HAVE_CREATE;
lcop:
if (!*opt)
cops |= op;
else if (!strcmp( opt, "-far" ))
ops[F] |= op;
else if (!strcmp( opt, "-master" )) // Pre-1.4 legacy
ops[F] |= op, ms_warn = 1;
else if (!strcmp( opt, "-near" ))
ops[N] |= op;
else if (!strcmp( opt, "-slave" )) // Pre-1.4 legacy
ops[N] |= op, ms_warn = 1;
else if (!strcmp( opt, "-master" ))
ops[M] |= op;
else if (!strcmp( opt, "-slave" ))
ops[S] |= op;
else
goto badopt;
ops[F] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
} else if (starts_with( opt, -1, "remove", 6 )) {
opt += 6;
op = OP_REMOVE|XOP_HAVE_REMOVE;
@ -533,15 +528,15 @@ main( int argc, char **argv )
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
goto lcop;
} else if (!strcmp( opt, "no-expunge" ))
ops[F] |= XOP_HAVE_EXPUNGE;
ops[M] |= XOP_HAVE_EXPUNGE;
else if (!strcmp( opt, "no-create" ))
ops[F] |= XOP_HAVE_CREATE;
ops[M] |= XOP_HAVE_CREATE;
else if (!strcmp( opt, "no-remove" ))
ops[F] |= XOP_HAVE_REMOVE;
ops[M] |= XOP_HAVE_REMOVE;
else if (!strcmp( opt, "full" ))
ops[F] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH;
ops[M] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH;
else if (!strcmp( opt, "noop" ))
ops[F] |= XOP_HAVE_TYPE;
ops[M] |= XOP_HAVE_TYPE;
else if (starts_with( opt, -1, "pull", 4 )) {
op = XOP_PULL;
lcac:
@ -573,11 +568,11 @@ main( int argc, char **argv )
return 1;
}
switch (op & XOP_MASK_DIR) {
case XOP_PULL: ops[N] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[F] |= op & OP_MASK_TYPE; break;
case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
default: cops |= op; break;
}
ops[F] |= XOP_HAVE_TYPE;
ops[M] |= XOP_HAVE_TYPE;
}
continue;
}
@ -595,6 +590,10 @@ main( int argc, char **argv )
mvars->list = 1;
break;
case 'c':
if (*ochar == 'T') {
ochar++;
pseudo = 1;
}
if (oind >= argc) {
error( "-c requires an argument.\n" );
return 1;
@ -604,19 +603,15 @@ main( int argc, char **argv )
case 'C':
op = OP_CREATE|XOP_HAVE_CREATE;
cop:
if (*ochar == 'f')
ops[F] |= op, ochar++;
else if (*ochar == 'm') // Pre-1.4 legacy
ops[F] |= op, ms_warn = 1, ochar++;
else if (*ochar == 'n')
ops[N] |= op, ochar++;
else if (*ochar == 's') // Pre-1.4 legacy
ops[N] |= op, ms_warn = 1, ochar++;
if (*ochar == 'm')
ops[M] |= op, ochar++;
else if (*ochar == 's')
ops[S] |= op, ochar++;
else if (*ochar == '-')
ochar++;
else
cops |= op;
ops[F] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
break;
case 'R':
op = OP_REMOVE|XOP_HAVE_REMOVE;
@ -628,7 +623,7 @@ main( int argc, char **argv )
cops |= XOP_PULL|XOP_PUSH;
FALLTHROUGH
case '0':
ops[F] |= XOP_HAVE_TYPE;
ops[M] |= XOP_HAVE_TYPE;
break;
case 'n':
case 'd':
@ -651,13 +646,13 @@ main( int argc, char **argv )
}
if (op & OP_MASK_TYPE)
switch (op & XOP_MASK_DIR) {
case XOP_PULL: ops[N] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[F] |= op & OP_MASK_TYPE; break;
case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
default: cops |= op; break;
}
else
cops |= op;
ops[F] |= XOP_HAVE_TYPE;
ops[M] |= XOP_HAVE_TYPE;
break;
case 'L':
op = XOP_PULL;
@ -710,24 +705,12 @@ main( int argc, char **argv )
op = DEBUG_ALL | VERBOSE;
DFlags |= op;
break;
case 'T':
for (; *ochar; ) {
switch (*ochar++) {
case 'a':
DFlags |= FORCEASYNC;
break;
case 'j':
DFlags |= KEEPJOURNAL;
JLimit = strtol( ochar, &ochar, 10 );
break;
case 'z':
DFlags |= ZERODELAY;
break;
default:
error( "Unknown -T flag '%c'\n", *(ochar - 1) );
return 1;
}
}
case 'J':
DFlags |= KEEPJOURNAL;
JLimit = strtol( ochar, &ochar, 10 );
break;
case 'Z':
DFlags |= ZERODELAY;
break;
case 'v':
version();
@ -738,8 +721,6 @@ main( int argc, char **argv )
return 1;
}
}
if (ms_warn)
warn( "Notice: -master/-slave/m/s suffixes are deprecated; use -far/-near/f/n instead.\n" );
if (!(DFlags & (QUIET | DEBUG_ALL)) && isatty( 1 ))
DFlags |= PROGRESS;
@ -755,7 +736,7 @@ main( int argc, char **argv )
if (merge_ops( cops, ops ))
return 1;
if (load_config( config ))
if (load_config( config, pseudo ))
return 1;
if (!channels) {
@ -857,33 +838,30 @@ sync_chans( main_vars_t *mvars, int ent )
int st = mvars->chan->stores[t]->driver->get_fail_state( mvars->chan->stores[t] );
if (st != FAIL_TEMP) {
info( "Skipping due to %sfailed %s store %s.\n",
(st == FAIL_WAIT) ? "temporarily " : "", str_fn[t], mvars->chan->stores[t]->name );
(st == FAIL_WAIT) ? "temporarily " : "", str_ms[t], mvars->chan->stores[t]->name );
mvars->skip = 1;
}
}
if (mvars->skip)
goto next2;
mvars->state[F] = mvars->state[N] = ST_FRESH;
uint dcaps[2];
for (t = 0; t < 2; t++) {
mvars->drv[t] = mvars->chan->stores[t]->driver;
dcaps[t] = mvars->drv[t]->get_caps( NULL );
}
if ((DFlags & DEBUG_DRV) || (dcaps[F] & dcaps[N] & DRV_VERBOSE))
labels[F] = "F: ", labels[N] = "N: ";
mvars->state[M] = mvars->state[S] = ST_FRESH;
if ((DFlags & DEBUG_DRV) || (mvars->chan->stores[M]->driver->get_caps( 0 ) & mvars->chan->stores[S]->driver->get_caps( 0 ) & DRV_VERBOSE))
labels[M] = "M: ", labels[S] = "S: ";
else
labels[F] = labels[N] = "";
labels[M] = labels[S] = "";
for (t = 0; t < 2; t++) {
store_t *ctx = mvars->drv[t]->alloc_store( mvars->chan->stores[t], labels[t] );
if ((DFlags & DEBUG_DRV) || ((DFlags & FORCEASYNC) && !(dcaps[t] & DRV_ASYNC))) {
mvars->drv[t] = &proxy_driver;
driver_t *drv = mvars->chan->stores[t]->driver;
store_t *ctx = drv->alloc_store( mvars->chan->stores[t], labels[t] );
if (DFlags & DEBUG_DRV) {
drv = &proxy_driver;
ctx = proxy_alloc_store( ctx, labels[t] );
}
mvars->drv[t] = drv;
mvars->ctx[t] = ctx;
mvars->drv[t]->set_bad_callback( ctx, store_bad, AUX );
drv->set_bad_callback( ctx, store_bad, AUX );
}
for (t = 0; ; t++) {
info( "Opening %s store %s...\n", str_fn[t], mvars->chan->stores[t]->name );
info( "Opening %s store %s...\n", str_ms[t], mvars->chan->stores[t]->name );
mvars->drv[t]->connect_store( mvars->ctx[t], store_connected, AUX );
if (t || mvars->skip)
break;
@ -893,44 +871,44 @@ sync_chans( main_vars_t *mvars, int ent )
opened:
if (mvars->skip)
goto next;
if (mvars->state[F] != ST_OPEN || mvars->state[N] != ST_OPEN)
if (mvars->state[M] != ST_OPEN || mvars->state[S] != ST_OPEN)
return;
if (!mvars->chanptr->boxlist && mvars->chan->patterns) {
mvars->chanptr->boxlist = 2;
boxes[F] = filter_boxes( mvars->boxes[F], mvars->chan->boxes[F], mvars->chan->patterns );
boxes[N] = filter_boxes( mvars->boxes[N], mvars->chan->boxes[N], mvars->chan->patterns );
boxes[M] = filter_boxes( mvars->boxes[M], mvars->chan->boxes[M], mvars->chan->patterns );
boxes[S] = filter_boxes( mvars->boxes[S], mvars->chan->boxes[S], mvars->chan->patterns );
mboxapp = &mvars->chanptr->boxes;
for (mb = sb = 0; ; ) {
char *mname = boxes[F] ? boxes[F][mb] : NULL;
char *sname = boxes[N] ? boxes[N][sb] : NULL;
char *mname = boxes[M] ? boxes[M][mb] : 0;
char *sname = boxes[S] ? boxes[S][sb] : 0;
if (!mname && !sname)
break;
mbox = nfmalloc( sizeof(*mbox) );
if (!(cmp = !mname - !sname) && !(cmp = cmp_box_names( &mname, &sname ))) {
mbox->name = mname;
free( sname );
mbox->present[F] = mbox->present[N] = BOX_PRESENT;
mbox->present[M] = mbox->present[S] = BOX_PRESENT;
mb++;
sb++;
} else if (cmp < 0) {
mbox->name = mname;
mbox->present[F] = BOX_PRESENT;
mbox->present[N] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[M] = BOX_PRESENT;
mbox->present[S] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mb++;
} else {
mbox->name = sname;
mbox->present[F] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[N] = BOX_PRESENT;
mbox->present[M] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
mbox->present[S] = BOX_PRESENT;
sb++;
}
mbox->next = NULL;
mbox->next = 0;
*mboxapp = mbox;
mboxapp = &mbox->next;
boxes_total++;
}
free( boxes[F] );
free( boxes[N] );
free( boxes[M] );
free( boxes[S] );
if (!mvars->list)
stats();
}
@ -959,14 +937,14 @@ sync_chans( main_vars_t *mvars, int ent )
if (!mvars->skip)
goto syncml;
} else
printf( "%s <=> %s\n", nz( mvars->chan->boxes[F], "INBOX" ), nz( mvars->chan->boxes[N], "INBOX" ) );
printf( "%s <=> %s\n", nz( mvars->chan->boxes[M], "INBOX" ), nz( mvars->chan->boxes[S], "INBOX" ) );
}
next:
mvars->cben = 0;
for (t = 0; t < 2; t++) {
free_string_list( mvars->boxes[t] );
mvars->boxes[t] = NULL;
mvars->boxes[t] = 0;
if (mvars->state[t] == ST_FRESH) {
/* An unconnected store may be only cancelled. */
mvars->state[t] = ST_CLOSED;
@ -977,17 +955,17 @@ sync_chans( main_vars_t *mvars, int ent )
}
}
mvars->cben = 1;
if (mvars->state[F] != ST_CLOSED || mvars->state[N] != ST_CLOSED) {
if (mvars->state[M] != ST_CLOSED || mvars->state[S] != ST_CLOSED) {
mvars->skip = 1;
return;
}
if (mvars->chanptr->boxlist) {
if (mvars->chanptr->boxlist == 2) {
for (nmbox = mvars->chanptr->boxes; (mbox = nmbox); ) {
nmbox = mbox->next;
free( mbox->name );
free( mbox );
}
mvars->chanptr->boxes = NULL;
mvars->chanptr->boxes = 0;
mvars->chanptr->boxlist = 0;
}
next2:
@ -995,10 +973,7 @@ sync_chans( main_vars_t *mvars, int ent )
chans_done++;
stats();
}
chan_ent_t *nchan = mvars->chanptr->next;
free( mvars->chanptr );
mvars->chanptr = nchan;
} while (mvars->chanptr);
} while ((mvars->chanptr = mvars->chanptr->next));
for (t = 0; t < N_DRIVERS; t++)
drivers[t]->cleanup();
}
@ -1088,7 +1063,7 @@ store_listed( int sts, string_list_t *boxes, void *aux )
}
}
if (mvars->ctx[t]->conf->map_inbox) {
debug( "adding mapped inbox to %s store: %s\n", str_fn[t], mvars->ctx[t]->conf->map_inbox );
debug( "adding mapped inbox to %s: %s\n", str_ms[t], mvars->ctx[t]->conf->map_inbox );
add_string_list( &mvars->boxes[t], mvars->ctx[t]->conf->map_inbox );
}
break;
@ -1103,20 +1078,20 @@ store_listed( int sts, string_list_t *boxes, void *aux )
static int
sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox )
{
if (mvars->chan->boxes[F] || mvars->chan->boxes[N]) {
const char *mpfx = nz( mvars->chan->boxes[F], "" );
const char *spfx = nz( mvars->chan->boxes[N], "" );
if (mvars->chan->boxes[M] || mvars->chan->boxes[S]) {
const char *mpfx = nz( mvars->chan->boxes[M], "" );
const char *spfx = nz( mvars->chan->boxes[S], "" );
if (!mvars->list) {
nfasprintf( &mvars->names[F], "%s%s", mpfx, mbox->name );
nfasprintf( &mvars->names[N], "%s%s", spfx, mbox->name );
sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync_2_dyn, mvars );
nfasprintf( &mvars->names[M], "%s%s", mpfx, mbox->name );
nfasprintf( &mvars->names[S], "%s%s", spfx, mbox->name );
sync_boxes( mvars->ctx, (const char **)mvars->names, mbox->present, mvars->chan, done_sync_2_dyn, mvars );
return 1;
}
printf( "%s%s <=> %s%s\n", mpfx, mbox->name, spfx, mbox->name );
} else {
if (!mvars->list) {
mvars->names[F] = mvars->names[N] = mbox->name;
sync_boxes( mvars->ctx, (const char * const *)mvars->names, mbox->present, mvars->chan, done_sync, mvars );
mvars->names[M] = mvars->names[S] = mbox->name;
sync_boxes( mvars->ctx, (const char **)mvars->names, mbox->present, mvars->chan, done_sync, mvars );
return 1;
}
puts( mbox->name );
@ -1129,8 +1104,8 @@ done_sync_2_dyn( int sts, void *aux )
{
main_vars_t *mvars = (main_vars_t *)aux;
free( mvars->names[F] );
free( mvars->names[N] );
free( mvars->names[M] );
free( mvars->names[S] );
done_sync( sts, aux );
}
@ -1144,11 +1119,11 @@ done_sync( int sts, void *aux )
stats();
if (sts) {
mvars->ret = 1;
if (sts & (SYNC_BAD(F) | SYNC_BAD(N))) {
if (sts & SYNC_BAD(F))
mvars->state[F] = ST_CLOSED;
if (sts & SYNC_BAD(N))
mvars->state[N] = ST_CLOSED;
if (sts & (SYNC_BAD(M) | SYNC_BAD(S))) {
if (sts & SYNC_BAD(M))
mvars->state[M] = ST_CLOSED;
if (sts & SYNC_BAD(S))
mvars->state[S] = ST_CLOSED;
mvars->skip = 1;
}
}

View file

@ -57,13 +57,13 @@ line are ignored.
Don't synchronize anything, but list all mailboxes in the selected channels
and exit.
.TP
\fB-C\fR[\fBf\fR][\fBn\fR], \fB--create\fR[\fB-far\fR|\fB-near\fR]
\fB-C\fR[\fBm\fR][\fBs\fR], \fB--create\fR[\fB-master\fR|\fB-slave\fR]
Override any \fBCreate\fR options from the config file. See below.
.TP
\fB-R\fR[\fBf\fR][\fBn\fR], \fB--remove\fR[\fB-far\fR|\fB-near\fR]
\fB-R\fR[\fBm\fR][\fBs\fR], \fB--remove\fR[\fB-master\fR|\fB-slave\fR]
Override any \fBRemove\fR options from the config file. See below.
.TP
\fB-X\fR[\fBf\fR][\fBn\fR], \fB--expunge\fR[\fB-far\fR|\fB-near\fR]
\fB-X\fR[\fBm\fR][\fBs\fR], \fB--expunge\fR[\fB-master\fR|\fB-slave\fR]
Override any \fBExpunge\fR options from the config file. See below.
.TP
{\fB-n\fR|\fB-N\fR|\fB-d\fR|\fB-f\fR|\fB-0\fR|\fB-F\fR},\
@ -119,9 +119,9 @@ and literal double quotes and backslashes (\fB\\\fR) must be backslash-escaped.
All keywords (including those used as arguments) are case-insensitive.
Bash-like home directory expansion using the tilde (\fB~\fR) is supported
in all options which represent local paths.
There are a few global options, the others apply to particular sections.
Sections begin with a section-starting keyword and are terminated by an empty
line or end of file.
There are a few global options, the rest applies to particular sections.
Sections are started by a section keyword and are terminated by an empty line
or end of file.
Every section defines an object with an identifier unique within that
object class.
.P
@ -130,8 +130,8 @@ a collection of mailboxes; basically a folder, either local or remote.
A Channel connects two Stores, describing the way the two are synchronized.
.br
There are two auxiliary object classes: Accounts and Groups. An Account
describes the connection part of network Stores, so server configurations can
be shared between multiple Stores. A Group aggregates multiple Channels to
describes the connection part of remote Stores, so a server connection can be
shared between multiple Stores. A Group aggregates multiple Channels to
save typing on the command line.
.P
File system locations (in particular, \fBPath\fR and \fBInbox\fR) use the
@ -156,23 +156,15 @@ The location of the Store in the (server's) file system.
If this is no absolute path, the reference point is Store type specific.
This string is prepended to the mailbox names addressed in this Store,
but is not considered part of them; this is important for \fBPatterns\fR
and \fBCreate\fR in the Channels section.
in the Channels section.
Note that you \fBmust\fR append a slash if you want to specify an entire
directory.
(Default: none)
.
.TP
\fBMaxSize\fR \fIsize\fR[\fBk\fR|\fBm\fR][\fBb\fR]
Messages larger than \fIsize\fR will have only a small placeholder message
propagated into this Store. To propagate the full message, it must be
flagged in either Store; that can be done retroactively, in which case
the \fBReNew\fR operation needs to be executed instead of \fBNew\fR.
This is useful for avoiding downloading messages with large attachments
unless they are actually needed.
Caveat: Setting a size limit on a Store you never read directly (which is
typically the case for servers) is not recommended, as you may never
notice that affected messages were not propagated to it.
.br
Messages larger than that will not be propagated into this Store.
This is useful for weeding out messages with large attachments.
\fBK\fR and \fBM\fR can be appended to the size to specify KiBytes resp.
MeBytes instead of bytes. \fBB\fR is accepted but superfluous.
If \fIsize\fR is 0, the maximum message size is \fBunlimited\fR.
@ -182,7 +174,7 @@ If \fIsize\fR is 0, the maximum message size is \fBunlimited\fR.
\fBMapInbox\fR \fImailbox\fR
Create a virtual mailbox (relative to \fBPath\fR) which aliases
the \fBINBOX\fR. Makes sense in conjunction with \fBPatterns\fR in the
Channels section, though with a Maildir near side, you probably want to
Channels section, though with a Maildir slave, you probably want to
place \fBInbox\fR under \fBPath\fR instead.
This virtual mailbox does not support subfolders.
.
@ -323,18 +315,6 @@ Zero means unlimited.
Specify the login name on the IMAP server.
.
.TP
\fBUserCmd\fR [\fB+\fR]\fIcommand\fR
Specify a shell command to obtain a user rather than specifying a
user directly. This allows you to script retrieving user names.
.br
The command must produce exactly one line on stdout; the trailing newline
is optional.
Prepend \fB+\fR to the command to indicate that it produces TTY output
(e.g., a prompt); failure to do so will merely produce messier output.
Remember to backslash-escape double quotes and backslashes embedded into
the command.
.
.TP
\fBPass\fR \fIpassword\fR
Specify the password for \fIusername\fR on the IMAP server.
Note that this option is \fInot\fR required.
@ -345,30 +325,11 @@ configuration file, \fBmbsync\fR will prompt you for a password.
\fBPassCmd\fR [\fB+\fR]\fIcommand\fR
Specify a shell command to obtain a password rather than specifying a
password directly. This allows you to use password files and agents.
.br
See \fBUserCmd\fR above for details.
.
.TP
\fBUseKeychain\fR \fByes\fR|\fBno\fR
Whether to use the macOS Keychain to obtain the password.
(Default: \fBno\fR)
.IP
The neccessary keychain item can be created this way:
.RS
.IP
.nh
.B security add-internet-password \-r imap \-s
.I Host
.B \-a
.I User
.B \-w
.I password
[
.B \-T
.I /path/to/mbsync
]
.hy
.RE
The command must produce exactly one line on stdout; the trailing newline is
optional.
Prepend \fB+\fR to the command to indicate that it produces TTY output
(e.g., a decryption password prompt); failure to do so will merely produce
messier output.
.
.TP
\fBTunnel\fR \fIcommand\fR
@ -402,10 +363,10 @@ so it is the default (unless a tunnel is used).
right after connecting the secure IMAP port 993.
.
.TP
\fBSSLVersions\fR [\fBSSLv3\fR] [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR] [\fBTLSv1.3\fR]
\fBSSLVersions\fR [\fBSSLv3\fR] [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR]
Select the acceptable SSL/TLS versions.
Use old versions only when the server has problems with newer ones.
(Default: [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR] [\fBTLSv1.3\fR]).
(Default: [\fBTLSv1\fR] [\fBTLSv1.1\fR] [\fBTLSv1.2\fR]).
.
.TP
\fBSystemCertificates\fR \fByes\fR|\fBno\fR
@ -418,27 +379,17 @@ if you want to trust only hand-picked certificates.
\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.
These certificates are always trusted, regardless of validity.
.br
These certificates may be obtained using the \fBmbsync-get-cert\fR tool;
make sure to verify their fingerprints before trusting them, or transfer
them securely from the server's network (if it can be trusted beyond the
server itself).
.IP CA
These certificates are used as trust anchors when building the certificate
chain for the received server certificate.
They are used to supplant or supersede the system's trust store, depending
on the \fBSystemCertificates\fR setting;
it is not necessary and not recommended to specify the system's trust store
itself here.
The trust chains are fully validated.
.RE
The certificates from this file are matched only against the received
server certificate itself; CA certificates are \fBnot\fR supported here.
Do \fBnot\fR specify the system's CA certificate store here; see
\fBSystemCertificates\fR instead.
.br
The contents for this file may be obtained using the
\fBmbsync-get-cert\fR tool; make sure to verify the fingerprints of the
certificates before trusting them, or transfer them securely from the
server's network (if it is trusted).
.
.TP
\fBClientCertificate\fR \fIpath\fR
@ -453,13 +404,6 @@ so it is unlikely that you need this option.
File containing the private key corresponding to \fBClientCertificate\fR.
.
.TP
\fBCipherString\fR \fIstring\fR
Specify OpenSSL cipher string for connections secured with TLS up to
version 1.2 (but not 1.3 and above).
The format is described in \fBciphers\fR\|(1).
(Default: empty, which implies system wide policy).
.
.TP
\fBPipelineDepth\fR \fIdepth\fR
Maximum number of IMAP commands which can be simultaneously in flight.
Setting this to \fI1\fR disables pipelining.
@ -505,22 +449,14 @@ Specify the server's hierarchy delimiter.
Do \fInot\fR abuse this to re-interpret the hierarchy.
Use \fBFlatten\fR instead.
.
.TP
\fBSubscribedOnly\fR \fByes\fR|\fBno\fR
Selects whether to synchronize only mailboxes that are subscribed to on the
IMAP server. In technical terms, if this option is set, \fBmbsync\fR will use
the IMAP command LSUB instead of LIST to look for mailboxes in this Store.
This option make sense only in conjunction with \fBPatterns\fR.
(Default: \fBno\fR)
.
.SS Channels
.TP
\fBChannel\fR \fIname\fR
Define the Channel \fIname\fR, opening a section for its parameters.
.
.TP
{\fBFar\fR|\fBNear\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
Specify the far resp. near side Store to be connected by this Channel.
{\fBMaster\fR|\fBSlave\fR} \fB:\fIstore\fB:\fR[\fImailbox\fR]
Specify the Master resp. Slave Store to be connected by this Channel.
If \fBPatterns\fR are specified, \fImailbox\fR is interpreted as a
prefix which is not matched against the patterns, and which is not
affected by mailbox list overrides.
@ -529,8 +465,8 @@ Otherwise, if \fImailbox\fR is omitted, \fBINBOX\fR is assumed.
.TP
\fBPattern\fR[\fBs\fR] [\fB!\fR]\fIpattern\fR ...
Instead of synchronizing only one mailbox pair, synchronize all mailboxes
that match the \fIpattern\fR(s). The mailbox names are the same on the far
and near side. Patterns are IMAP4 patterns, i.e., \fB*\fR matches anything
that match the \fIpattern\fR(s). The mailbox names are the same on both
Master and Slave. Patterns are IMAP4 patterns, i.e., \fB*\fR matches anything
and \fB%\fR matches anything up to the next hierarchy delimiter. Prepending
\fB!\fR to a pattern makes it an exclusion. Multiple patterns can be specified
(either by supplying multiple arguments or by using \fBPattern\fR multiple
@ -547,12 +483,12 @@ 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
@ -576,14 +512,15 @@ case you need to enable this option.
\fBSync\fR {\fBNone\fR|[\fBPull\fR] [\fBPush\fR] [\fBNew\fR] [\fBReNew\fR] [\fBDelete\fR] [\fBFlags\fR]|\fBAll\fR}
Select the synchronization operation(s) to perform:
.br
\fBPull\fR - propagate changes from far to near side.
\fBPull\fR - propagate changes from Master to Slave.
.br
\fBPush\fR - propagate changes from near to far side.
\fBPush\fR - propagate changes from Slave to Master.
.br
\fBNew\fR - propagate newly appeared messages.
.br
\fBReNew\fR - upgrade placeholders to full messages. Useful only with
a configured \fBMaxSize\fR.
\fBReNew\fR - previously refused messages are re-evaluated for propagation.
Useful after flagging affected messages in the source Store or enlarging
MaxSize in the destination Store.
.br
\fBDelete\fR - propagate message deletions. This applies only to messages that
are actually gone, i.e., were expunged. The affected messages in the remote
@ -609,10 +546,10 @@ In the first style, the flags select entire rows/colums in the matrix. Only
the cells which are selected both horizontally and vertically are asserted.
Specifying no flags from a class is like specifying all flags from this class.
For example, "\fBSync\fR\ \fBPull\fR\ \fBNew\fR\ \fBFlags\fR" will propagate
new messages and flag changes from the far side to the near side,
new messages and flag changes from the Master to the Slave,
"\fBSync\fR\ \fBNew\fR\ \fBDelete\fR" will propagate message arrivals and
deletions both ways, and "\fBSync\fR\ \fBPush\fR" will propagate all changes
from the near side to the far side.
from the Slave to the Master.
.br
In the second style, direction flags are concatenated with type flags; every
compound flag immediately asserts a cell in the matrix. In addition to at least
@ -620,22 +557,22 @@ one compound flag, the individual flags can be used as well, but as opposed to
the first style, they immediately assert all cells in their respective
row/column. For example,
"\fBSync\fR\ \fBPullNew\fR\ \fBPullDelete\fR\ \fBPush\fR" will propagate
message arrivals and deletions from the far side to the near side and any
changes from the near side to the far side.
message arrivals and deletions from the Master to the Slave and any changes
from the Slave to the Master.
Note that it is not allowed to assert a cell in two ways, e.g.
"\fBSync\fR\ \fBPullNew\fR\ \fBPull\fR" and
"\fBSync\fR\ \fBPullNew\fR\ \fBDelete\fR\ \fBPush\fR" induce error messages.
.
.TP
\fBCreate\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Automatically create missing mailboxes [on the far/near side].
\fBCreate\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Automatically create missing mailboxes [on the Master/Slave].
Otherwise print an error message and skip that mailbox pair if a mailbox
and the corresponding sync state does not exist.
(Global default: \fBNone\fR)
.
.TP
\fBRemove\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Propagate mailbox deletions [to the far/near side].
\fBRemove\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Propagate mailbox deletions [to the Master/Slave].
Otherwise print an error message and skip that mailbox pair if a mailbox
does not exist but the corresponding sync state does.
.br
@ -647,8 +584,8 @@ Note that for safety, non-empty mailboxes are never deleted.
(Global default: \fBNone\fR)
.
.TP
\fBExpunge\fR {\fBNone\fR|\fBFar\fR|\fBNear\fR|\fBBoth\fR}
Permanently remove all messages [on the far/near side] marked for deletion.
\fBExpunge\fR {\fBNone\fR|\fBMaster\fR|\fBSlave\fR|\fBBoth\fR}
Permanently remove all messages [on the Master/Slave] marked for deletion.
See \fBRECOMMENDATIONS\fR below.
(Global default: \fBNone\fR)
.
@ -673,15 +610,15 @@ which in turn are overridden by command line switches.
\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
in the Slave 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
Otherwise this is interpreted as a string to prepend to the Slave mailbox
name to make up a complete path.
.br
This option can be used outside any section for a global effect. In this case
the appended string is made up according to the pattern
\fB:\fIfar-store\fB:\fIfar-box\fB_:\fInear-store\fB:\fInear-box\fR
\fB:\fImaster\fB:\fImaster-box\fB_:\fIslave\fB:\fIslave-box\fR
(see also \fBFieldDelimiter\fR below).
.br
(Global default: \fI~/.mbsync/\fR).
@ -741,11 +678,11 @@ If \fBmbsync\fR's output is connected to a console, it will print progress
counters by default. The output will look like this:
.P
.in +4
C: 1/2 B: 3/4 F: +13/13 *23/42 #0/0 N: +0/7 *0/0 #0/0
C: 1/2 B: 3/4 M: +13/13 *23/42 #0/0 S: +0/7 *0/0 #0/0
.in -4
.P
This represents the cumulative progress over channels, boxes, and messages
affected on the far and near side, respectively.
affected on master and slave, respectively.
The message counts represent added messages, messages with updated flags,
and trashed messages, respectively.
No attempt is made to calculate the totals in advance, so they grow over
@ -810,7 +747,7 @@ Default configuration file
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

View file

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

View file

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

View file

@ -20,36 +20,28 @@ use warnings;
use strict;
use Cwd;
use File::Path;
use File::Temp 'tempdir';
my $use_vg = $ENV{USE_VALGRIND};
my $mbsync = getcwd()."/mbsync";
if (!-d "tmp") {
unlink "tmp";
my $tdir = tempdir();
symlink $tdir, "tmp" or die "Cannot symlink temp directory: $!\n";
}
-d "tmp" or mkdir "tmp";
chdir "tmp" or die "Cannot enter temp direcory.\n";
sub show($$$);
sub test($$$$);
sub test($$$@);
################################################################################
# Format of the test defs: [ far, near, state ]
# far/near: [ maxuid, { seq, uid, flags }... ]
# state: [ MaxPulledUid, MaxExpiredFarUid, MaxPushedUid, { muid, suid, flags }... ]
use enum qw(:=1 A..Z);
sub mn($) { chr(64 + shift) }
# Format of the test defs: [ master, slave, state ]
# master/slave: [ maxuid, { seq, uid, flags }... ]
# state: [ MaxPulledUid, MaxExpiredMasterUid, MaxPushedUid, { muid, suid, flags }... ]
# 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, "" ],
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "" ],
[ 9,
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "", J, 9, "" ],
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "", 10, 9, "" ],
[ 8, 0, 0,
1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 8, 8, "" ],
);
@ -58,184 +50,138 @@ my @O01 = ("", "", "");
#show("01", "01", "01");
my @X01 = (
[ 10,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "FT", G, 7, "FT", I, 9, "", J, 10, "" ],
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "FT", 7, 7, "FT", 9, 9, "", 10, 10, "" ],
[ 10,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", G, 7, "FT", H, 8, "T", J, 9, "", I, 10, "" ],
[ 10, 0, 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 7, 7, "FT", 8, 8, "T", 9, 10, "", 10, 9, "" ],
[ 9, 0, 9,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 0, "", 7, 7, "FT", 0, 8, "", 10, 9, "", 9, 10, "" ],
);
test("full", \@x01, \@X01, \@O01);
test("full", \@x01, \@X01, @O01);
my @O02 = ("", "", "Expunge Both\n");
#show("01", "02", "02");
my @X02 = (
[ 10,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", I, 9, "", J, 10, "" ],
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 9, 9, "", 10, 10, "" ],
[ 10,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", J, 9, "", I, 10, "" ],
[ 10, 0, 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 9, 10, "", 10, 9, "" ],
[ 9, 0, 9,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 10, 9, "", 9, 10, "" ],
);
test("full + expunge both", \@x01, \@X02, \@O02);
test("full + expunge both", \@x01, \@X02, @O02);
my @O03 = ("", "", "Expunge Near\n");
my @O03 = ("", "", "Expunge Slave\n");
#show("01", "03", "03");
my @X03 = (
[ 10,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "FT", G, 7, "FT", I, 9, "", J, 10, "" ],
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "FT", 7, 7, "FT", 9, 9, "", 10, 10, "" ],
[ 10,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", J, 9, "", I, 10, "" ],
[ 10, 0, 10,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 9, 10, "", 10, 9, "" ],
[ 9, 0, 9,
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 0, "T", 6, 0, "", 7, 0, "T", 10, 9, "", 9, 10, "" ],
);
test("full + expunge near side", \@x01, \@X03, \@O03);
test("full + expunge slave", \@x01, \@X03, @O03);
my @O04 = ("", "", "Sync Pull\n");
#show("01", "04", "04");
my @X04 = (
[ 9,
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "" ],
[ 10,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", G, 7, "FT", H, 8, "T", J, 9, "", I, 10, "" ],
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 7, 7, "FT", 8, 8, "T", 9, 10, "", 10, 9, "" ],
[ 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);
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, "" ],
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "" ],
[ 9,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", G, 7, "FT", H, 8, "", J, 9, "" ],
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 7, 7, "FT", 8, 8, "", 10, 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);
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, "" ],
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "FT", 7, 7, "FT", 9, 9, "" ],
[ 9,
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "T", J, 9, "" ],
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "T", 10, 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);
test("deletions", \@x01, \@X06, @O06);
my @O07 = ("", "", "Sync New\n");
#show("01", "07", "07");
my @X07 = (
[ 10,
A, 1, "F", B, 2, "", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "", J, 10, "" ],
1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "", 10, 10, "" ],
[ 10,
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "", J, 9, "", I, 10, "" ],
[ 10, 0, 10,
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "", 9, 10, "", 10, 9, "" ],
[ 9, 0, 9,
1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 8, 8, "", 10, 9, "", 9, 10, "" ],
);
test("new", \@x01, \@X07, \@O07);
test("new", \@x01, \@X07, @O07);
my @O08 = ("", "", "Sync PushFlags PullDelete\n");
#show("01", "08", "08");
my @X08 = (
[ 9,
A, 1, "F", B, 2, "F", C, 3, "FS", D, 4, "", E, 5, "T", F, 6, "F", G, 7, "FT", I, 9, "" ],
1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "F", 7, 7, "FT", 9, 9, "" ],
[ 9,
A, 1, "", B, 2, "F", C, 3, "F", D, 4, "", E, 5, "", G, 7, "", H, 8, "T", J, 9, "" ],
1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 7, 7, "", 8, 8, "T", 10, 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);
test("push flags + pull deletions", \@x01, \@X08, @O08);
# size restriction tests
my @x10 = (
[ 2,
A, 1, "", B, 2, "*" ],
1, 1, "", 2, 2, "*" ],
[ 1,
C, 1, "*" ],
3, 1, "*" ],
[ 0, 0, 0,
],
);
my @O11 = ("MaxSize 1k\n", "MaxSize 1k\n", "Expunge Near");
my @O11 = ("MaxSize 1k\n", "MaxSize 1k\n", "");
#show("10", "11", "11");
my @X11 = (
[ 3,
A, 1, "", B, 2, "*", C, 3, "?" ],
[ 3,
C, 1, "*", A, 2, "", B, 3, "?" ],
[ 3, 0, 3,
3, 1, "<", 1, 2, "", 2, 3, ">" ],
);
test("max size", \@x10, \@X11, \@O11);
my @x22 = (
[ 3,
A, 1, "", B, 2, "*", C, 3, "?" ],
[ 3,
C, 1, "F*", A, 2, "", B, 3, "F?" ],
[ 3, 0, 3,
3, 1, "<", 1, 2, "", 2, 3, ">" ],
);
#show("22", "22", "11");
my @X22 = (
[ 4,
A, 1, "", B, 2, "*", C, 3, "T?", C, 4, "F*" ],
[ 4,
C, 1, "F*", A, 2, "", B, 4, "*" ],
[ 4, 0, 4,
4, 1, "F", 3, 0, "T", 1, 2, "", 2, 4, "" ],
);
test("max size + flagging", \@x22, \@X22, \@O11);
my @x23 = (
[ 2,
A, 1, "", B, 2, "F*" ],
[ 1,
C, 1, "F*" ],
[ 0, 0, 0,
],
1, 1, "", 2, 2, "*" ],
[ 2,
3, 1, "*", 1, 2, "" ],
[ 2, 0, 1,
0, 1, "^", 1, 2, "", 2, 0, "^" ],
);
test("max size", \@x10, \@X11, @O11);
my @X23 = (
my @O22 = ("", "MaxSize 1k\n", "");
#show("11", "22", "22");
my @X22 = (
[ 3,
A, 1, "", B, 2, "F*", C, 3, "F*" ],
[ 3,
C, 1, "F*", A, 2, "", B, 3, "F*" ],
[ 3, 0, 3,
3, 1, "F", 1, 2, "", 2, 3, "F" ]
1, 1, "", 2, 2, "*", 3, 3, "*" ],
[ 2,
3, 1, "*", 1, 2, "" ],
[ 2, 0, 1,
3, 1, "", 1, 2, "", 2, 0, "^" ],
);
test("max size + initial flagging", \@x23, \@X23, \@O11);
my @x24 = (
[ 3,
A, 1, "", B, 2, "*", C, 3, "F*" ],
[ 1,
A, 1, "" ],
[ 3, 0, 1,
1, 1, "", 2, 0, "^", 3, 0, "^" ],
);
my @X24 = (
[ 3,
A, 1, "", B, 2, "*", C, 3, "F*" ],
[ 3,
A, 1, "", B, 2, "?", C, 3, "F*" ],
[ 3, 0, 3,
1, 1, "", 2, 2, ">", 3, 3, "F" ],
);
test("max size (pre-1.4 legacy)", \@x24, \@X24, \@O11);
test("slave max size", \@X11, \@X22, @O22);
# expiration tests
my @x30 = (
[ 6,
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
1, 1, "F", 2, 2, "", 3, 3, "S", 4, 4, "", 5, 5, "S", 6, 6, "" ],
[ 0,
],
[ 0, 0, 0,
@ -246,31 +192,31 @@ my @O31 = ("", "", "MaxMessages 3\n");
#show("30", "31", "31");
my @X31 = (
[ 6,
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
1, 1, "F", 2, 2, "", 3, 3, "S", 4, 4, "", 5, 5, "S", 6, 6, "" ],
[ 5,
A, 1, "F", B, 2, "", D, 3, "", E, 4, "S", F, 5, "" ],
[ 6, 3, 5,
1, 1, "F", 2, 2, "", 4, 3, "", 5, 4, "S", 6, 5, "" ],
[ 6, 3, 0,
1, 1, "F", 2, 2, "", 4, 3, "", 5, 4, "S", 6, 5, "" ],
);
test("max messages", \@x30, \@X31, \@O31);
test("max messages", \@x30, \@X31, @O31);
my @O32 = ("", "", "MaxMessages 3\nExpireUnread yes\n");
#show("30", "32", "32");
my @X32 = (
[ 6,
A, 1, "F", B, 2, "", C, 3, "S", D, 4, "", E, 5, "S", F, 6, "" ],
1, 1, "F", 2, 2, "", 3, 3, "S", 4, 4, "", 5, 5, "S", 6, 6, "" ],
[ 4,
A, 1, "F", D, 2, "", E, 3, "S", F, 4, "" ],
[ 6, 3, 4,
1, 1, "F", 4, 2, "", 5, 3, "S", 6, 4, "" ],
[ 6, 3, 0,
1, 1, "F", 4, 2, "", 5, 3, "S", 6, 4, "" ],
);
test("max messages vs. unread", \@x30, \@X32, \@O32);
test("max messages vs. unread", \@x30, \@X32, @O32);
my @x50 = (
[ 6,
A, 1, "FS", B, 2, "FS", C, 3, "S", D, 4, "", E, 5, "", F, 6, "" ],
1, 1, "FS", 2, 2, "FS", 3, 3, "S", 4, 4, "", 5, 5, "", 6, 6, "" ],
[ 6,
A, 1, "S", B, 2, "ST", D, 4, "", E, 5, "", F, 6, "" ],
1, 1, "S", 2, 2, "ST", 4, 4, "", 5, 5, "", 6, 6, "" ],
[ 6, 3, 0,
1, 1, "FS", 2, 2, "~S", 3, 3, "~S", 4, 4, "", 5, 5, "", 6, 6, "" ],
);
@ -279,17 +225,19 @@ my @O51 = ("", "", "MaxMessages 3\nExpunge Both\n");
#show("50", "51", "51");
my @X51 = (
[ 6,
A, 1, "S", B, 2, "FS", C, 3, "S", D, 4, "", E, 5, "", F, 6, "" ],
1, 1, "S", 2, 2, "FS", 3, 3, "S", 4, 4, "", 5, 5, "", 6, 6, "" ],
[ 6,
B, 2, "FS", D, 4, "", E, 5, "", F, 6, "" ],
[ 6, 3, 6,
2, 2, "FS", 4, 4, "", 5, 5, "", 6, 6, "" ],
[ 6, 3, 0,
2, 2, "FS", 4, 4, "", 5, 5, "", 6, 6, "" ],
);
test("max messages + expunge", \@x50, \@X51, \@O51);
test("max messages + expunge", \@x50, \@X51, @O51);
################################################################################
chdir "..";
rmdir "tmp";
print "OK.\n";
exit 0;
@ -304,29 +252,27 @@ sub qm($)
return $_;
}
# [ $far, $near, $channel ]
sub writecfg($)
# $master, $slave, $channel
sub writecfg($$$)
{
my ($sfx) = @_;
open(FILE, ">", ".mbsyncrc") or
die "Cannot open .mbsyncrc.\n";
print FILE
"FSync no
MaildirStore far
MaildirStore master
Path ./
Inbox ./far
".$$sfx[0]."
MaildirStore near
Inbox ./master
".shift()."
MaildirStore slave
Path ./
Inbox ./near
".$$sfx[1]."
Inbox ./slave
".shift()."
Channel test
Far :far:
Near :near:
Master :master:
Slave :slave:
SyncState *
".$$sfx[2];
".shift();
close FILE;
}
@ -336,11 +282,10 @@ sub killcfg()
unlink ".mbsyncrc";
}
# $run_async, $mbsync_options, $log_file
# Return: $exit_code, \@mbsync_output
sub runsync($$$)
# $options
sub runsync($$)
{
my ($async, $flags, $file) = @_;
my ($flags, $file) = @_;
my $cmd;
if ($use_vg) {
@ -348,8 +293,7 @@ sub runsync($$$)
} else {
$flags .= " -D";
}
$flags .= " -Ta" if ($async);
$cmd .= "$mbsync -Tz $flags -c .mbsyncrc test";
$cmd .= "$mbsync -Z $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");
@ -358,12 +302,11 @@ sub runsync($$$)
print FILE @out;
close FILE;
}
return $?, \@out;
return $?, @out;
}
# $path
# Return: $max_uid, { uid => [ seq, flags ] }
sub readbox($)
{
my $bn = shift;
@ -380,9 +323,11 @@ sub readbox($)
for my $d ("cur", "new") {
opendir(DIR, $bn."/".$d) or next;
for my $f (grep(!/^\.\.?$/, readdir(DIR))) {
my ($uid, $flg, $ph, $num);
my ($uid, $flg, $num);
if ($f =~ /^\d+\.\d+_\d+\.[-[:alnum:]]+,U=(\d+):2,(.*)$/) {
($uid, $flg) = ($1, $2);
} elsif ($f =~ /^\d+\.\d+_(\d+)\.[-[:alnum:]]+:2,(.*)$/) {
($uid, $flg) = (0, $2);
} else {
print STDERR "unrecognided file name '$f' in '$bn'.\n";
exit 1;
@ -390,7 +335,7 @@ sub readbox($)
open(FILE, "<", $bn."/".$d."/".$f) or die "Cannot read message '$f' in '$bn'.\n";
my $sz = 0;
while (<FILE>) {
/^Subject: (\[placeholder\] )?(\d+)$/ && ($ph = defined($1), $num = $2);
/^Subject: (\d+)$/ && ($num = $1);
$sz += length($_);
}
close FILE;
@ -398,10 +343,10 @@ sub readbox($)
print STDERR "message '$f' in '$bn' has no identifier.\n";
exit 1;
}
@{ $ms{$uid} } = ($num, $flg.($sz>1000?"*":"").($ph?"?":""));
@{ $ms{$num} } = ($uid, $flg.($sz>1000?"*":""));
}
}
return $mu, \%ms;
return ($mu, %ms);
}
# $boxname
@ -412,18 +357,18 @@ sub showbox($)
{
my ($bn) = @_;
my ($mu, $ms) = readbox($bn);
my @bc = ($mu);
for my $uid (sort { $a <=> $b } keys %$ms) {
push @bc, $$ms{$uid}[0], $uid, $$ms{$uid}[1];
my ($mu, %ms) = readbox($bn);
my @MS = ($mu);
for my $num (sort { $a <=> $b } keys %ms) {
push @MS, $num, $ms{$num}[0], $ms{$num}[1];
}
printbox(\@bc);
printbox($bn, @MS);
}
# $filename
# Output:
# [ maxuid[F], maxxfuid, maxuid[N],
# uid[F], uid[N], "flags", ... ],
# [ maxuid[M], mmaxxuid, maxuid[S],
# uid[M], uid[S], "flags", ... ],
sub showstate($)
{
my ($fn) = @_;
@ -451,13 +396,13 @@ sub showstate($)
return;
}
my @T = ($hdr{'MaxPulledUid'} // "missing",
$hdr{'MaxExpiredFarUid'} // "0",
$hdr{'MaxExpiredMasterUid'} // "0",
$hdr{'MaxPushedUid'} // "missing");
for (@ls) {
/^(\d+) (\d+) (.*)$/;
push @T, $1, $2, $3;
}
printstate(\@T);
printstate(@T);
}
# $filename
@ -465,8 +410,8 @@ sub showchan($)
{
my ($fn) = @_;
showbox("far");
showbox("near");
showbox("master");
showbox("slave");
showstate($fn);
}
@ -474,105 +419,109 @@ sub showchan($)
sub show($$$)
{
my ($sx, $tx, $sfxn) = @_;
my ($sp, $sfx);
eval "\$sp = \\\@x$sx";
eval "\$sfx = \\\@O$sfxn";
mkchan($$sp[0], $$sp[1], $$sp[2]);
my (@sp, @sfx);
eval "\@sp = \@x$sx";
eval "\@sfx = \@O$sfxn";
mkchan($sp[0], $sp[1], @{ $sp[2] });
print "my \@x$sx = (\n";
showchan("near/.mbsyncstate");
showchan("slave/.mbsyncstate");
print ");\n";
writecfg($sfx);
runsync(0, "", "");
&writecfg(@sfx);
runsync("", "");
killcfg();
print "my \@X$tx = (\n";
showchan("near/.mbsyncstate");
showchan("slave/.mbsyncstate");
print ");\n";
print "test(\"\", \\\@x$sx, \\\@X$tx, \\\@O$sfxn);\n\n";
rmtree "near";
rmtree "far";
print "test(\"\", \\\@x$sx, \\\@X$tx, \@O$sfxn);\n\n";
rmtree "slave";
rmtree "master";
}
# $box_name, \@box_state
sub mkbox($$)
# $boxname, $maxuid, @msgs
sub mkbox($$@)
{
my ($bn, $bs) = @_;
my ($bn, $mu, @ms) = @_;
rmtree($bn);
(mkdir($bn) and mkdir($bn."/tmp") and mkdir($bn."/new") and mkdir($bn."/cur")) or
die "Cannot create mailbox $bn.\n";
open(FILE, ">", $bn."/.uidvalidity") or die "Cannot create UID validity for mailbox $bn.\n";
print FILE "1\n$$bs[0]\n";
print FILE "1\n$mu\n";
close FILE;
for (my $i = 1; $i < @$bs; $i += 3) {
my ($num, $uid, $flg) = ($$bs[$i], $$bs[$i + 1], $$bs[$i + 2]);
while (@ms) {
my ($num, $uid, $flg) = (shift @ms, shift @ms, shift @ms);
if ($uid) {
$uid = ",U=".$uid;
} else {
$uid = "";
}
my $big = $flg =~ s/\*//;
my $ph = $flg =~ s/\?//;
open(FILE, ">", $bn."/".($flg =~ /S/ ? "cur" : "new")."/0.1_".$num.".local,U=".$uid.":2,".$flg) or
die "Cannot create message ".mn($num)." in mailbox $bn.\n";
print FILE "From: foo\nTo: bar\nDate: Thu, 1 Jan 1970 00:00:00 +0000\nSubject: ".($ph?"[placeholder] ":"").$num."\n\n".(("A"x50)."\n")x($big*30);
open(FILE, ">", $bn."/".($flg =~ /S/ ? "cur" : "new")."/0.1_".$num.".local".$uid.":2,".$flg) or
die "Cannot create message $num in mailbox $bn.\n";
print FILE "From: foo\nTo: bar\nDate: Thu, 1 Jan 1970 00:00:00 +0000\nSubject: $num\n\n".(("A"x50)."\n")x($big*30);
close FILE;
}
}
# \@far_state, \@near_state, \@sync_state
sub mkchan($$$)
# \@master, \@slave, @syncstate
sub mkchan($$@)
{
my ($f, $n, $t) = @_;
mkbox("far", $f);
mkbox("near", $n);
open(FILE, ">", "near/.mbsyncstate") or
my ($m, $s, @t) = @_;
&mkbox("master", @{ $m });
&mkbox("slave", @{ $s });
open(FILE, ">", "slave/.mbsyncstate") or
die "Cannot create sync state.\n";
print FILE "FarUidValidity 1\nMaxPulledUid ".$$t[0]."\n".
"NearUidValidity 1\nMaxExpiredFarUid ".$$t[1]."\nMaxPushedUid ".$$t[2]."\n\n";
for (my $i = 3; $i < @$t; $i += 3) {
print FILE $$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2]."\n";
print FILE "MasterUidValidity 1\nMaxPulledUid ".shift(@t)."\n".
"SlaveUidValidity 1\nMaxExpiredMasterUid ".shift(@t)."\nMaxPushedUid ".shift(@t)."\n\n";
while (@t) {
print FILE shift(@t)." ".shift(@t)." ".shift(@t)."\n";
}
close FILE;
}
# $box_name, \@box_state
sub ckbox($$)
# $config, $boxname, $maxuid, @msgs
sub ckbox($$$@)
{
my ($bn, $bs) = @_;
my ($bn, $MU, @MS) = @_;
my ($mu, $ms) = readbox($bn);
if ($mu != $$bs[0]) {
print STDERR "MAXUID mismatch for '$bn' (got $mu, wanted $$bs[0]).\n";
my ($mu, %ms) = readbox($bn);
if ($mu != $MU) {
print STDERR "MAXUID mismatch for '$bn' (got $mu, wanted $MU).\n";
return 1;
}
for (my $i = 1; $i < @$bs; $i += 3) {
my ($num, $uid, $flg) = ($$bs[$i], $$bs[$i + 1], $$bs[$i + 2]);
my $m = delete $$ms{$uid};
if (!defined $m) {
print STDERR "No message $bn:$uid.\n";
while (@MS) {
my ($num, $uid, $flg) = (shift @MS, shift @MS, shift @MS);
if (!defined $ms{$num}) {
print STDERR "No message $bn:$num.\n";
return 1;
}
if ($$m[0] ne $num) {
print STDERR "Subject mismatch for $bn:$uid.\n";
if ($ms{$num}[0] ne $uid) {
print STDERR "UID mismatch for $bn:$num.\n";
return 1;
}
if ($$m[1] ne $flg) {
print STDERR "Flag mismatch for $bn:$uid.\n";
if ($ms{$num}[1] ne $flg) {
print STDERR "Flag mismatch for $bn:$num.\n";
return 1;
}
delete $ms{$num};
}
if (%$ms) {
print STDERR "Excess messages in '$bn': ".join(", ", sort({ $a <=> $b } keys(%$ms))).".\n";
if (%ms) {
print STDERR "Excess messages in '$bn': ".join(", ", sort({$a <=> $b } keys(%ms))).".\n";
return 1;
}
return 0;
}
# $state_file, \@sync_state
sub ckstate($$)
# $filename, @syncstate
sub ckstate($@)
{
my ($fn, $t) = @_;
my ($fn, $mmaxuid, $mmaxxuid, $smaxuid, @T) = @_;
my %hdr;
$hdr{'FarUidValidity'} = "1";
$hdr{'NearUidValidity'} = "1";
$hdr{'MaxPulledUid'} = $$t[0];
$hdr{'MaxPushedUid'} = $$t[2];
$hdr{'MaxExpiredFarUid'} = $$t[1] if ($$t[1] ne 0);
$hdr{'MasterUidValidity'} = "1";
$hdr{'SlaveUidValidity'} = "1";
$hdr{'MaxPulledUid'} = $mmaxuid;
$hdr{'MaxPushedUid'} = $smaxuid;
$hdr{'MaxExpiredMasterUid'} = $mmaxxuid if ($mmaxxuid ne 0);
open(FILE, "<", $fn) or die "Cannot read sync state $fn.\n";
chomp(my @ls = <FILE>);
close FILE;
@ -602,68 +551,65 @@ sub ckstate($$)
print STDERR "Keys missing from sync state header: @ky\n";
return 1;
}
my $i = 3;
for my $l (@ls) {
if ($i == @$t) {
if (!@T) {
print STDERR "Excess sync state entry: '$l'.\n";
return 1;
}
my $xl = $$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2];
my $xl = shift(@T)." ".shift(@T)." ".shift(@T);
if ($l ne $xl) {
print STDERR "Sync state entry mismatch: '$l' instead of '$xl'.\n";
return 1;
}
$i += 3;
}
if ($i < @$t) {
print STDERR "Missing sync state entry: '".$$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2]."'.\n";
if (@T) {
print STDERR "Missing sync state entry: '".shift(@T)." ".shift(@T)." ".shift(@T)."'.\n";
return 1;
}
return 0;
}
# $state_file, \@chan_state
# $statefile, \@chan_state
sub ckchan($$)
{
my ($fn, $cs) = @_;
my $rslt = ckstate($fn, $$cs[2]);
$rslt |= ckbox("far", $$cs[0]);
$rslt |= ckbox("near", $$cs[1]);
my ($F, $cs) = @_;
my $rslt = ckstate($F, @{ $$cs[2] });
$rslt |= &ckbox("master", @{ $$cs[0] });
$rslt |= &ckbox("slave", @{ $$cs[1] });
return $rslt;
}
# \@box_state
sub printbox($)
sub printbox($$@)
{
my ($bs) = @_;
my ($bn, $mu, @ms) = @_;
print " [ $$bs[0],\n ";
print " [ $mu,\n ";
my $frst = 1;
for (my $i = 1; $i < @$bs; $i += 3) {
while (@ms) {
if ($frst) {
$frst = 0;
} else {
print ", ";
}
print mn($$bs[$i]).", ".$$bs[$i + 1].", \"".$$bs[$i + 2]."\"";
print shift(@ms).", ".shift(@ms).", \"".shift(@ms)."\"";
}
print " ],\n";
}
# \@sync_state
sub printstate($)
# @syncstate
sub printstate(@)
{
my ($t) = @_;
my (@t) = @_;
print " [ ".$$t[0].", ".$$t[1].", ".$$t[2].",\n ";
print " [ ".shift(@t).", ".shift(@t).", ".shift(@t).",\n ";
my $frst = 1;
for (my $i = 3; $i < @$t; $i += 3) {
while (@t) {
if ($frst) {
$frst = 0;
} else {
print ", ";
}
print(($$t[$i] // "??").", ".($$t[$i + 1] // "??").", \"".($$t[$i + 2] // "??")."\"");
print((shift(@t) // "??").", ".(shift(@t) // "??").", \"".(shift(@t) // "??")."\"");
}
print " ],\n";
}
@ -673,9 +619,9 @@ sub printchan($)
{
my ($cs) = @_;
printbox($$cs[0]);
printbox($$cs[1]);
printstate($$cs[2]);
&printbox("master", @{ $$cs[0] });
&printbox("slave", @{ $$cs[1] });
printstate(@{ $$cs[2] });
}
sub readfile($)
@ -685,122 +631,112 @@ sub readfile($)
open(FILE, $file) or return;
my @nj = <FILE>;
close FILE;
return \@nj;
return @nj;
}
# $run_async, \@source_state, \@target_state, \@channel_configs
sub test_impl($$$$)
# $title, \@source_state, \@target_state, @channel_configs
sub test($$$@)
{
my ($async, $sx, $tx, $sfx) = @_;
my ($ttl, $sx, $tx, @sfx) = @_;
mkchan($$sx[0], $$sx[1], $$sx[2]);
return 0 if (scalar(@ARGV) && !grep { $_ eq $ttl } @ARGV);
print "Testing: ".$ttl." ...\n";
&writecfg(@sfx);
my ($xc, $ret) = runsync($async, "-Tj", "1-initial.log");
if ($xc || ckchan("near/.mbsyncstate.new", $tx)) {
mkchan($$sx[0], $$sx[1], @{ $$sx[2] });
my ($xc, @ret) = runsync("-J", "1-initial.log");
if ($xc || ckchan("slave/.mbsyncstate.new", $tx)) {
print "Input:\n";
printchan($sx);
print "Options:\n";
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n";
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
if (!$xc) {
print "Expected result:\n";
printchan($tx);
print "Actual result:\n";
showchan("near/.mbsyncstate.new");
showchan("slave/.mbsyncstate.new");
}
print "Debug output:\n";
print @$ret;
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])) {
my @nj = readfile("slave/.mbsyncstate.journal");
my ($jxc, @jret) = runsync("-0 --no-expunge", "2-replay.log");
if ($jxc || ckstate("slave/.mbsyncstate", @{ $$tx[2] })) {
print "Journal replay failed.\n";
print "Options:\n";
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ], [ \"-0\", \"--no-expunge\" ]\n";
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ], [ \"-0\", \"--no-expunge\" ]\n";
print "Old State:\n";
printstate($$sx[2]);
print "Journal:\n".join("", @$nj)."\n";
printstate(@{ $$sx[2] });
print "Journal:\n".join("", @nj)."\n";
if (!$jxc) {
print "Expected New State:\n";
printstate($$tx[2]);
printstate(@{ $$tx[2] });
print "New State:\n";
showstate("near/.mbsyncstate");
showstate("slave/.mbsyncstate");
}
print "Debug output:\n";
print @$jret;
print @jret;
exit 1;
}
my ($ixc, $iret) = runsync($async, "", "3-verify.log");
if ($ixc || ckchan("near/.mbsyncstate", $tx)) {
my ($ixc, @iret) = runsync("", "3-verify.log");
if ($ixc || ckchan("slave/.mbsyncstate", $tx)) {
print "Idempotence verification run failed.\n";
print "Input == Expected result:\n";
printchan($tx);
print "Options:\n";
print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n";
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
if (!$ixc) {
print "Actual result:\n";
showchan("near/.mbsyncstate");
showchan("slave/.mbsyncstate");
}
print "Debug output:\n";
print @$iret;
print @iret;
exit 1;
}
rmtree "near";
rmtree "far";
rmtree "slave";
rmtree "master";
my $njl = (@$nj - 1) * 2;
for (my $l = 1; $l <= $njl; $l++) {
mkchan($$sx[0], $$sx[1], $$sx[2]);
my $njl = (@nj - 1) * 2;
for (my $l = 2; $l < $njl; $l++) {
mkchan($$sx[0], $$sx[1], @{ $$sx[2] });
my ($nxc, $nret) = runsync($async, "-Tj$l", "4-interrupt.log");
my ($nxc, @nret) = runsync("-J$l", "4-interrupt.log");
if ($nxc != (100 + ($l & 1)) << 8) {
print "Interrupting at step $l/$njl failed.\n";
print "Debug output:\n";
print @$nret;
print @nret;
exit 1;
}
($nxc, $nret) = runsync($async, "-Tj", "5-resume.log");
if ($nxc || ckchan("near/.mbsyncstate.new", $tx)) {
($nxc, @nret) = runsync("-J", "5-resume.log");
if ($nxc || ckchan("slave/.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";
print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n";
my @nnj = readfile("slave/.mbsyncstate.journal");
print "Journal:\n".join("", @nnj[0..($l / 2 - 1)])."-------\n".join("", @nnj[($l / 2)..$#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");
showchan("slave/.mbsyncstate");
}
print "Debug output:\n";
print @$nret;
print @nret;
exit 1;
}
rmtree "near";
rmtree "far";
rmtree "slave";
rmtree "master";
}
}
# $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();
}

View file

@ -41,7 +41,7 @@
# include <openssl/err.h>
# include <openssl/x509v3.h>
# if OPENSSL_VERSION_NUMBER < 0x10100000L \
|| (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070100fL)
|| (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x2070100fL)
# define X509_OBJECT_get0_X509(o) ((o)->data.x509)
# define X509_STORE_get0_objects(o) ((o)->objs)
# endif
@ -74,7 +74,7 @@ print_ssl_errors( const char *fmt, ... )
nfvasprintf( &action, fmt, va );
va_end( va );
while ((err = ERR_get_error()))
error( "Error while %s: %s\n", action, ERR_error_string( err, NULL ) );
error( "Error while %s: %s\n", action, ERR_error_string( err, 0 ) );
free( action );
}
@ -85,7 +85,7 @@ print_ssl_socket_errors( const char *func, conn_t *conn )
int num = 0;
while ((err = ERR_get_error())) {
error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, NULL ) );
error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, 0 ) );
num++;
}
return num;
@ -194,6 +194,7 @@ verify_cert_host( const server_conf_t *conf, conn_t *sock )
int i;
long err;
X509 *cert;
STACK_OF(X509_OBJECT) *trusted;
cert = SSL_get_peer_certificate( sock->ssl );
if (!cert) {
@ -201,8 +202,9 @@ verify_cert_host( const server_conf_t *conf, conn_t *sock )
return -1;
}
for (i = 0; i < sk_X509_num( sock->conf->trusted_certs ); i++) {
if (!X509_cmp( cert, sk_X509_value( sock->conf->trusted_certs, i ) )) {
trusted = (STACK_OF(X509_OBJECT) *)sock->conf->trusted_certs;
for (i = 0; i < sk_X509_OBJECT_num( trusted ); i++) {
if (!X509_cmp( cert, X509_OBJECT_get0_X509( sk_X509_OBJECT_value( trusted, i ) ) )) {
X509_free( cert );
return 0;
}
@ -230,10 +232,8 @@ verify_cert_host( const server_conf_t *conf, conn_t *sock )
static int
init_ssl_ctx( const server_conf_t *conf )
{
DIAG_PUSH
DIAG_DISABLE("-Wcast-qual") // C has no 'mutable' or const_cast<> ...
server_conf_t *mconf = (server_conf_t *)conf;
DIAG_POP
int options = 0;
if (conf->SSLContext)
return conf->ssl_ctx_valid;
@ -248,7 +248,8 @@ DIAG_POP
return 0;
}
uint options = SSL_OP_NO_SSLv3;
if (!(conf->ssl_versions & SSLv3))
options |= SSL_OP_NO_SSLv3;
if (!(conf->ssl_versions & TLSv1))
options |= SSL_OP_NO_TLSv1;
#ifdef SSL_OP_NO_TLSv1_1
@ -259,49 +260,18 @@ DIAG_POP
if (!(conf->ssl_versions & TLSv1_2))
options |= SSL_OP_NO_TLSv1_2;
#endif
#ifdef SSL_OP_NO_TLSv1_3
if (!(conf->ssl_versions & TLSv1_3))
options |= SSL_OP_NO_TLSv1_3;
#endif
SSL_CTX_set_options( mconf->SSLContext, options );
if (conf->cipher_string && !SSL_CTX_set_cipher_list( mconf->SSLContext, conf->cipher_string )) {
print_ssl_errors( "setting cipher string '%s'", conf->cipher_string );
if (conf->cert_file && !SSL_CTX_load_verify_locations( mconf->SSLContext, conf->cert_file, 0 )) {
print_ssl_errors( "loading certificate file '%s'", conf->cert_file );
return 0;
}
if (!(mconf->trusted_certs = sk_X509_new_null()))
oom();
if (conf->cert_file) {
X509_STORE *store;
if (!(store = X509_STORE_new()))
oom();
if (!X509_STORE_load_locations( store, conf->cert_file, NULL )) {
print_ssl_errors( "loading certificate file '%s'", conf->cert_file );
return 0;
}
STACK_OF(X509_OBJECT) *objs = X509_STORE_get0_objects( store );
for (int i = 0; i < sk_X509_OBJECT_num( objs ); i++) {
X509 *cert = X509_OBJECT_get0_X509( sk_X509_OBJECT_value( objs, i ) );
if (cert) {
if (X509_check_ca( cert )) {
if (!X509_STORE_add_cert( SSL_CTX_get_cert_store( mconf->SSLContext ), cert ))
oom();
} else {
X509_up_ref( cert ); // Locking failure assumed impossible
if (!sk_X509_push( mconf->trusted_certs, cert ))
oom();
}
}
}
X509_STORE_free( store );
}
mconf->trusted_certs = (_STACK *)sk_X509_OBJECT_dup( X509_STORE_get0_objects( SSL_CTX_get_cert_store( mconf->SSLContext ) ) );
if (mconf->system_certs && !SSL_CTX_set_default_verify_paths( mconf->SSLContext )) {
ulong err;
while ((err = ERR_get_error()))
warn( "Warning: Unable to load default certificate files: %s\n", ERR_error_string( err, NULL ) );
warn( "Warning: Unable to load default certificate files: %s\n", ERR_error_string( err, 0 ) );
}
SSL_CTX_set_verify( mconf->SSLContext, SSL_VERIFY_NONE, NULL );
@ -329,7 +299,6 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) )
static int ssl_inited;
conn->callbacks.starttls = cb;
conn->state = SCK_STARTTLS;
if (!ssl_inited) {
SSL_library_init();
@ -343,7 +312,7 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) )
}
init_wakeup( &conn->ssl_fake, ssl_fake_cb, conn );
if (!(conn->ssl = SSL_new( ((server_conf_t const *)conn->conf)->SSLContext ))) {
if (!(conn->ssl = SSL_new( ((server_conf_t *)conn->conf)->SSLContext ))) {
print_ssl_errors( "initializing SSL connection" );
start_tls_p3( conn, 0 );
return;
@ -359,7 +328,8 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) )
return;
}
SSL_set_mode( conn->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER );
socket_expect_activity( conn, 1 );
socket_expect_read( conn, 1 );
conn->state = SCK_STARTTLS;
start_tls_p2( conn );
}
@ -378,7 +348,7 @@ start_tls_p2( conn_t *conn )
static void start_tls_p3( conn_t *conn, int ok )
{
socket_expect_activity( conn, 0 );
socket_expect_read( conn, 0 );
conn->state = SCK_READY;
conn->callbacks.starttls( ok, conn->callback_aux );
}
@ -460,32 +430,6 @@ socket_close_internal( conn_t *sock )
sock->fd = -1;
}
#ifndef HAVE_IPV6
struct addr_info {
struct addr_info *ai_next;
struct sockaddr_in ai_addr[1];
};
#define freeaddrinfo(ai) free( ai )
static struct addr_info *
init_addrinfo( struct hostent *he )
{
uint naddr = 0;
for (char **addr = he->h_addr_list; *addr; addr++)
naddr++;
struct addr_info *caddr = nfcalloc( naddr * sizeof(struct addrinfo) );
struct addr_info *ret, **caddrp = &ret;
for (char **addr = he->h_addr_list; *addr; addr++, caddr++) {
caddr->ai_addr->sin_family = AF_INET;
memcpy( &caddr->ai_addr->sin_addr.s_addr, *addr, sizeof(struct in_addr) );
*caddrp = caddr;
caddrp = &caddr->ai_next;
}
return ret;
}
#endif
void
socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
{
@ -535,6 +479,8 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
return;
}
info( "\vok\n" );
sock->curr_addr = sock->addrs;
#else
struct hostent *he;
@ -547,9 +493,8 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
}
info( "\vok\n" );
sock->addrs = init_addrinfo( he );
sock->curr_addr = he->h_addr_list;
#endif
sock->curr_addr = sock->addrs;
socket_connect_one( sock );
}
}
@ -561,10 +506,16 @@ socket_connect_one( conn_t *sock )
#ifdef HAVE_IPV6
struct addrinfo *ai;
#else
struct addr_info *ai;
struct {
struct sockaddr_in ai_addr[1];
} ai[1];
#endif
#ifdef HAVE_IPV6
if (!(ai = sock->curr_addr)) {
#else
if (!*sock->curr_addr) {
#endif
error( "No working address found for %s\n", sock->conf->host );
socket_connect_bail( sock );
return;
@ -581,6 +532,11 @@ socket_connect_one( conn_t *sock )
#endif
{
struct sockaddr_in *in = ((struct sockaddr_in *)ai->ai_addr);
#ifndef HAVE_IPV6
memset( in, 0, sizeof(*in) );
in->sin_family = AF_INET;
in->sin_addr.s_addr = *((int *)*sock->curr_addr);
#endif
in->sin_port = htons( sock->conf->port );
nfasprintf( &sock->name, "%s (%s:%hu)",
sock->conf->host, inet_ntoa( in->sin_addr ), sock->conf->port );
@ -608,7 +564,7 @@ socket_connect_one( conn_t *sock )
return;
}
conf_notifier( &sock->notify, 0, POLLOUT );
socket_expect_activity( sock, 1 );
socket_expect_read( sock, 1 );
sock->state = SCK_CONNECTING;
info( "\v\n" );
return;
@ -622,8 +578,12 @@ socket_connect_next( conn_t *conn )
{
sys_error( "Cannot connect to %s", conn->name );
free( conn->name );
conn->name = NULL;
conn->name = 0;
#ifdef HAVE_IPV6
conn->curr_addr = conn->curr_addr->ai_next;
#else
conn->curr_addr++;
#endif
socket_connect_one( conn );
}
@ -637,12 +597,14 @@ socket_connect_failed( conn_t *conn )
static void
socket_connected( conn_t *conn )
{
#ifdef HAVE_IPV6
if (conn->addrs) {
freeaddrinfo( conn->addrs );
conn->addrs = NULL;
conn->addrs = 0;
}
#endif
conf_notifier( &conn->notify, 0, POLLIN );
socket_expect_activity( conn, 0 );
socket_expect_read( conn, 0 );
conn->state = SCK_READY;
conn->callbacks.connect( 1, conn->callback_aux );
}
@ -650,12 +612,14 @@ socket_connected( conn_t *conn )
static void
socket_cleanup_names( conn_t *conn )
{
#ifdef HAVE_IPV6
if (conn->addrs) {
freeaddrinfo( conn->addrs );
conn->addrs = NULL;
conn->addrs = 0;
}
#endif
free( conn->name );
conn->name = NULL;
conn->name = 0;
}
static void
@ -676,7 +640,7 @@ socket_close( conn_t *sock )
#ifdef HAVE_LIBSSL
if (sock->ssl) {
SSL_free( sock->ssl );
sock->ssl = NULL;
sock->ssl = 0;
wipe_wakeup( &sock->ssl_fake );
}
#endif
@ -684,23 +648,23 @@ socket_close( conn_t *sock )
if (sock->in_z) {
inflateEnd( sock->in_z );
free( sock->in_z );
sock->in_z = NULL;
sock->in_z = 0;
deflateEnd( sock->out_z );
free( sock->out_z );
sock->out_z = NULL;
sock->out_z = 0;
wipe_wakeup( &sock->z_fake );
}
#endif
while (sock->write_buf)
dispose_chunk( sock );
free( sock->append_buf );
sock->append_buf = NULL;
sock->append_buf = 0;
}
static int
prepare_read( conn_t *sock, char **buf, uint *len )
prepare_read( conn_t *sock, char **buf, int *len )
{
uint n = sock->offset + sock->bytes;
int n = sock->offset + sock->bytes;
if (!(*len = sizeof(sock->buf) - n)) {
error( "Socket error: receive buffer full. Probably protocol error.\n" );
socket_fail( sock );
@ -711,17 +675,17 @@ prepare_read( conn_t *sock, char **buf, uint *len )
}
static int
do_read( conn_t *sock, char *buf, uint len )
do_read( conn_t *sock, char *buf, int len )
{
int n;
assert( sock->fd >= 0 );
#ifdef HAVE_LIBSSL
if (sock->ssl) {
if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, (int)len ) )) <= 0)
if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, len ) )) <= 0)
return n;
if (n == (int)len && SSL_pending( sock->ssl ))
if (n == len && SSL_pending( sock->ssl ))
conf_wakeup( &sock->ssl_fake, 0 );
} else
#endif
@ -744,8 +708,7 @@ static void
socket_fill_z( conn_t *sock )
{
char *buf;
uint len;
int ret;
int len, ret;
if (prepare_read( sock, &buf, &len ) < 0)
return;
@ -765,7 +728,7 @@ socket_fill_z( conn_t *sock )
if (!sock->in_z->avail_out)
conf_wakeup( &sock->z_fake, 0 );
if ((len = (uint)((char *)sock->in_z->next_out - buf))) {
if ((len = (char *)sock->in_z->next_out - buf)) {
sock->bytes += len;
sock->read_callback( sock->callback_aux );
}
@ -783,37 +746,36 @@ socket_fill( conn_t *sock )
sock->in_z->next_in = (uchar *)sock->z_buf;
if ((ret = do_read( sock, sock->z_buf, sizeof(sock->z_buf) )) <= 0)
return;
sock->in_z->avail_in = (uint)ret;
sock->in_z->avail_in = ret;
socket_fill_z( sock );
} else
#endif
{
char *buf;
uint len;
int len;
if (prepare_read( sock, &buf, &len ) < 0)
return;
int n;
if ((n = do_read( sock, buf, len )) <= 0)
if ((len = do_read( sock, buf, len )) <= 0)
return;
sock->bytes += (uint)n;
sock->bytes += len;
sock->read_callback( sock->callback_aux );
}
}
void
socket_expect_activity( conn_t *conn, int expect )
socket_expect_read( conn_t *conn, int expect )
{
if (conn->conf->timeout > 0 && expect != pending_wakeup( &conn->fd_timeout ))
conf_wakeup( &conn->fd_timeout, expect ? conn->conf->timeout : -1 );
}
int
socket_read( conn_t *conn, char *buf, uint len )
socket_read( conn_t *conn, char *buf, int len )
{
uint n = conn->bytes;
int n = conn->bytes;
if (!n && conn->state == SCK_EOF)
return -1;
if (n > len)
@ -823,14 +785,14 @@ socket_read( conn_t *conn, char *buf, uint len )
conn->offset = 0;
else
conn->offset += n;
return (int)n;
return n;
}
char *
socket_read_line( conn_t *b )
{
char *p, *s;
uint n;
int n;
s = b->buf + b->offset;
p = memchr( s + b->scanoff, '\n', b->bytes - b->scanoff );
@ -842,9 +804,9 @@ socket_read_line( conn_t *b )
}
if (b->state == SCK_EOF)
return (void *)~0;
return NULL;
return 0;
}
n = (uint)(p + 1 - s);
n = p + 1 - s;
b->offset += n;
b->bytes -= n;
b->scanoff = 0;
@ -855,14 +817,14 @@ socket_read_line( conn_t *b )
}
static int
do_write( conn_t *sock, char *buf, uint len )
do_write( conn_t *sock, char *buf, int len )
{
int n;
assert( sock->fd >= 0 );
#ifdef HAVE_LIBSSL
if (sock->ssl)
return ssl_return( "write to", sock, SSL_write( sock->ssl, buf, (int)len ) );
return ssl_return( "write to", sock, SSL_write( sock->ssl, buf, len ) );
#endif
n = write( sock->fd, buf, len );
if (n < 0) {
@ -873,7 +835,7 @@ do_write( conn_t *sock, char *buf, uint len )
n = 0;
conf_notifier( &sock->notify, POLLIN, POLLOUT );
}
} else if (n != (int)len) {
} else if (n != len) {
conf_notifier( &sock->notify, POLLIN, POLLOUT );
}
return n;
@ -898,12 +860,12 @@ do_queued_write( conn_t *conn )
return 0;
while ((bc = conn->write_buf)) {
int n;
uint len = bc->len - conn->write_offset;
int n, len = bc->len - conn->write_offset;
if ((n = do_write( conn, bc->data + conn->write_offset, len )) < 0)
return -1;
if (n != (int)len) {
conn->write_offset += (uint)n;
if (n != len) {
conn->write_offset += n;
conn->writing = 1;
return 0;
}
conn->write_offset = 0;
@ -913,6 +875,7 @@ do_queued_write( conn_t *conn )
if (conn->ssl && SSL_pending( conn->ssl ))
conf_wakeup( &conn->ssl_fake, 0 );
#endif
conn->writing = 0;
conn->write_callback( conn->callback_aux );
return -1;
}
@ -920,7 +883,7 @@ do_queued_write( conn_t *conn )
static void
do_append( conn_t *conn, buff_chunk_t *bc )
{
bc->next = NULL;
bc->next = 0;
conn->buffer_mem += bc->len;
*conn->write_buf_append = bc;
conn->write_buf_append = &bc->next;
@ -936,7 +899,7 @@ do_flush( conn_t *conn )
buff_chunk_t *bc = conn->append_buf;
#ifdef HAVE_LIBZ
if (conn->out_z) {
uint buf_avail = conn->append_avail;
int buf_avail = conn->append_avail;
if (!conn->z_written)
return;
do {
@ -957,10 +920,10 @@ do_flush( conn_t *conn )
error( "Fatal: Compression error: %s\n", z_err_msg( ret, conn->out_z ) );
abort();
}
bc->len = (uint)((char *)conn->out_z->next_out - bc->data);
bc->len = (char *)conn->out_z->next_out - bc->data;
if (bc->len) {
do_append( conn, bc );
bc = NULL;
bc = 0;
buf_avail = 0;
} else {
buf_avail = conn->out_z->avail_out;
@ -973,7 +936,7 @@ do_flush( conn_t *conn )
#endif
if (bc) {
do_append( conn, bc );
conn->append_buf = NULL;
conn->append_buf = 0;
#ifdef HAVE_LIBZ
conn->append_avail = 0;
#endif
@ -983,8 +946,7 @@ do_flush( conn_t *conn )
void
socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
{
int i;
uint buf_avail, len, offset = 0, total = 0;
int i, buf_avail, len, offset = 0, total = 0;
buff_chunk_t *bc;
for (i = 0; i < iovcnt; i++)
@ -1028,7 +990,7 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
error( "Fatal: Compression error: %s\n", z_err_msg( ret, conn->out_z ) );
abort();
}
bc->len = (uint)((char *)conn->out_z->next_out - bc->data);
bc->len = (char *)conn->out_z->next_out - bc->data;
buf_avail = conn->out_z->avail_out;
len -= conn->out_z->avail_in;
conn->z_written = 1;
@ -1051,7 +1013,7 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
}
if (!buf_avail) {
do_append( conn, bc );
bc = NULL;
bc = 0;
break;
}
}
@ -1095,11 +1057,11 @@ socket_fd_cb( int events, void *aux )
conf_wakeup( &conn->fd_timeout, conn->conf->timeout );
#ifdef HAVE_LIBSSL
if (conn->state == SCK_STARTTLS) {
start_tls_p2( conn );
return;
}
if (conn->ssl) {
if (conn->state == SCK_STARTTLS) {
start_tls_p2( conn );
return;
}
if (do_queued_write( conn ) < 0)
return;
socket_fill( conn );
@ -1121,7 +1083,7 @@ socket_fake_cb( void *aux )
/* Ensure that a pending write gets queued. */
do_flush( conn );
/* If no writes are ongoing, start writing now. */
if (!(notifier_config( &conn->notify ) & POLLOUT))
if (!conn->writing)
do_queued_write( conn );
}

View file

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

1175
src/sync.c

File diff suppressed because it is too large Load diff

View file

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

View file

@ -23,11 +23,9 @@
#include "common.h"
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <pwd.h>
@ -44,7 +42,7 @@ flushn( void )
}
}
static void ATTR_PRINTFLIKE(1, 0)
static void
printn( const char *msg, va_list va )
{
if (*msg == '\v')
@ -151,34 +149,25 @@ error( const char *msg, ... )
}
void
vsys_error( const char *msg, va_list va )
sys_error( const char *msg, ... )
{
va_list va;
char buf[1024];
int errno_bak = errno;
flushn();
va_start( va, msg );
if ((uint)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
oob();
errno = errno_bak;
va_end( va );
perror( buf );
}
void
sys_error( const char *msg, ... )
{
va_list va;
va_start( va, msg );
vsys_error( msg, va );
va_end( va );
}
void
add_string_list_n( string_list_t **list, const char *str, uint len )
add_string_list_n( string_list_t **list, const char *str, int len )
{
string_list_t *elem;
elem = nfmalloc( offsetof(string_list_t, string) + len + 1 );
elem = nfmalloc( sizeof(*elem) + len );
elem->next = *list;
*list = elem;
memcpy( elem->string, str, len );
@ -214,7 +203,7 @@ vasprintf( char **strp, const char *fmt, va_list ap )
if (len >= (int)sizeof(tmp))
vsprintf( *strp, fmt, ap );
else
memcpy( *strp, tmp, (size_t)len + 1 );
memcpy( *strp, tmp, len + 1 );
return len;
}
#endif
@ -243,32 +232,34 @@ strnlen( const char *str, size_t maxlen )
#endif
int
starts_with( const char *str, int strl, const char *cmp, uint cmpl )
starts_with( const char *str, int strl, const char *cmp, int cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return ((uint)strl >= cmpl) && !memcmp( str, cmp, cmpl );
return (strl >= cmpl) && !memcmp( str, cmp, cmpl );
}
int
starts_with_upper( const char *str, int strl, const char *cmp, uint cmpl )
starts_with_upper( const char *str, int strl, const char *cmp, int cmpl )
{
int i;
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
if ((uint)strl < cmpl)
if (strl < cmpl)
return 0;
for (uint i = 0; i < cmpl; i++)
for (i = 0; i < cmpl; i++)
if (str[i] != cmp[i] && toupper( str[i] ) != cmp[i])
return 0;
return 1;
}
int
equals( const char *str, int strl, const char *cmp, uint cmpl )
equals( const char *str, int strl, const char *cmp, int cmpl )
{
if (strl < 0)
strl = strnlen( str, cmpl + 1 );
return ((uint)strl == cmpl) && !memcmp( str, cmp, cmpl );
return (strl == cmpl) && !memcmp( str, cmp, cmpl );
}
#ifndef HAVE_TIMEGM
@ -350,13 +341,13 @@ nfsnprintf( char *buf, int blen, const char *fmt, ... )
va_list va;
va_start( va, fmt );
if (blen <= 0 || (uint)(ret = vsnprintf( buf, (size_t)blen, fmt, va )) >= (uint)blen)
if (blen <= 0 || (uint)(ret = vsnprintf( buf, blen, fmt, va )) >= (uint)blen)
oob();
va_end( va );
return ret;
}
void
static void ATTR_NORETURN
oom( void )
{
fputs( "Fatal: Out of memory\n", stderr );
@ -459,20 +450,20 @@ expand_strdup( const char *s )
if (*s == '~') {
s++;
if (!*s) {
p = NULL;
p = 0;
q = Home;
} else if (*s == '/') {
p = s;
q = Home;
} else {
if ((p = strchr( s, '/' ))) {
r = nfstrndup( s, (size_t)(p - s) );
r = nfstrndup( s, (int)(p - s) );
pw = getpwnam( r );
free( r );
} else
pw = getpwnam( s );
if (!pw)
return NULL;
return 0;
q = pw->pw_dir;
}
nfasprintf( &r, "%s%s", q, p ? p : "" );
@ -483,10 +474,10 @@ expand_strdup( const char *s )
/* Return value: 0 = ok, -1 = out found in arg, -2 = in found in arg but no out specified */
int
map_name( const char *arg, char **result, uint reserve, const char *in, const char *out )
map_name( const char *arg, char **result, int reserve, const char *in, const char *out )
{
char *p;
uint i, l, ll, num, inl, outl;
int i, l, ll, num, inl, outl;
assert( arg );
l = strlen( arg );
@ -500,7 +491,7 @@ map_name( const char *arg, char **result, uint reserve, const char *in, const ch
}
assert( out );
outl = strlen( out );
if (equals( in, (int)inl, out, outl ))
if (equals( in, inl, out, outl ))
goto copy;
for (num = 0, i = 0; i < l; ) {
for (ll = 0; ll < inl; ll++)
@ -543,7 +534,7 @@ map_name( const char *arg, char **result, uint reserve, const char *in, const ch
static int
compare_uints( const void *l, const void *r )
{
uint li = *(const uint *)l, ri = *(const uint *)r;
uint li = *(uint *)l, ri = *(uint *)r;
if (li != ri) // Can't subtract, the result might not fit into signed int.
return li > ri ? 1 : -1;
return 0;
@ -558,16 +549,16 @@ sort_uint_array( uint_array_t array )
int
find_uint_array( uint_array_t array, uint value )
{
uint bot = 0, top = array.size;
while (bot < top) {
uint i = (bot + top) / 2;
int bot = 0, top = array.size - 1;
while (bot <= top) {
int i = (bot + top) / 2;
uint elt = array.data[i];
if (elt == value)
return 1;
if (elt < value)
bot = i + 1;
else
top = i;
top = i - 1;
}
return 0;
}
@ -594,7 +585,7 @@ arc4_init( void )
close( fd );
for (i = 0; i < 256; i++)
rs.s[i] = (uchar)i;
rs.s[i] = i;
for (i = j = 0; i < 256; i++) {
si = rs.s[i];
j += si + dat[i & 127];
@ -626,13 +617,13 @@ static const uchar prime_deltas[] = {
1, 29, 3, 21, 7, 17, 15, 9, 43, 35, 15, 0, 0, 0, 0, 0
};
uint
bucketsForSize( uint size )
int
bucketsForSize( int size )
{
uint base = 4, bits = 2;
int base = 4, bits = 2;
for (;;) {
uint prime = base + prime_deltas[bits];
int prime = base + prime_deltas[bits];
if (prime >= size)
return prime;
base <<= 1;
@ -660,14 +651,14 @@ list_unlink( list_head_t *head )
assert( head->prev->next == head);
head->next->prev = head->prev;
head->prev->next = head->next;
head->next = head->prev = NULL;
head->next = head->prev = 0;
}
static notifier_t *notifiers;
static int changed; /* Iterator may be invalid now. */
#ifdef HAVE_POLL_H
#ifdef HAVE_SYS_POLL_H
static struct pollfd *pollfds;
static uint npolls, rpolls;
static int npolls, rpolls;
#else
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
@ -677,8 +668,8 @@ static uint npolls, rpolls;
void
init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux )
{
#ifdef HAVE_POLL_H
uint idx = npolls++;
#ifdef HAVE_SYS_POLL_H
int idx = npolls++;
if (rpolls < npolls) {
rpolls = npolls;
pollfds = nfrealloc( pollfds, npolls * sizeof(*pollfds) );
@ -697,41 +688,31 @@ init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux )
}
void
conf_notifier( notifier_t *sn, short and_events, short or_events )
conf_notifier( notifier_t *sn, int and_events, int or_events )
{
#ifdef HAVE_POLL_H
uint idx = sn->index;
#ifdef HAVE_SYS_POLL_H
int idx = sn->index;
pollfds[idx].events = (pollfds[idx].events & and_events) | or_events;
#else
sn->events = (sn->events & and_events) | or_events;
#endif
}
short
notifier_config( notifier_t *sn )
{
#ifdef HAVE_POLL_H
return pollfds[sn->index].events;
#else
return sn->events;
#endif
}
void
wipe_notifier( notifier_t *sn )
{
notifier_t **snp;
#ifdef HAVE_POLL_H
uint idx;
#ifdef HAVE_SYS_POLL_H
int idx;
#endif
for (snp = &notifiers; *snp != sn; snp = &(*snp)->next)
assert( *snp );
*snp = sn->next;
sn->next = NULL;
sn->next = 0;
changed = 1;
#ifdef HAVE_POLL_H
#ifdef HAVE_SYS_POLL_H
idx = sn->index;
memmove( pollfds + idx, pollfds + idx + 1, (--npolls - idx) * sizeof(*pollfds) );
for (sn = notifiers; sn; sn = sn->next) {
@ -744,7 +725,7 @@ wipe_notifier( notifier_t *sn )
static time_t
get_now( void )
{
return time( NULL );
return time( 0 );
}
static list_head_t timers = { &timers, &timers };
@ -754,7 +735,7 @@ init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux )
{
tmr->cb = cb;
tmr->aux = aux;
tmr->links.next = tmr->links.prev = NULL;
tmr->links.next = tmr->links.prev = 0;
}
void
@ -803,7 +784,7 @@ event_wait( void )
notifier_t *sn;
int m;
#ifdef HAVE_POLL_H
#ifdef HAVE_SYS_POLL_H
int timeout = -1;
if ((head = timers.next) != &timers) {
wakeup_t *tmr = (wakeup_t *)head;
@ -825,7 +806,7 @@ event_wait( void )
break;
}
for (sn = notifiers; sn; sn = sn->next) {
uint n = sn->index;
int n = sn->index;
if ((m = pollfds[n].revents)) {
assert( !(m & POLLNVAL) );
sn->cb( m | shifted_bit( m, POLLHUP, POLLIN ), sn->aux );