Compare commits

...

16 Commits

Author SHA1 Message Date
Oswald Buddenhagen 76e5f223ee add missing trailing newlines in error() calls 2 months ago
Oswald Buddenhagen d54c22d20e fix IMAP INBOX case normalization 2 months ago
Oswald Buddenhagen 4c2031d616 fix initial build from git 2 months ago
Oswald Buddenhagen 4279aea6a0 generalize AUTHORS section of man page 2 months ago
Oswald Buddenhagen 6fbbcbb2c7 substitute version and date in man pages 2 months ago
Oswald Buddenhagen 8421b3cb22 automate setting package version 2 months ago
Oswald Buddenhagen f467b57a95 generalize GPL exception 2 months ago
Oswald Buddenhagen 7bca6967a7 update some email addresses 2 months ago
Husain Alshehhi 5f81162f5e add tag files to .gitignore 2 months ago
Oswald Buddenhagen ee832951e2 revamp automatic enumeration of power-of-two enumerators 2 months ago
Oswald Buddenhagen 31c504d432 remove redundant argument from BIT_FORMATTER_PROTO() 2 months ago
Oswald Buddenhagen 43271d8fad eliminate commit_cmds driver callback 2 months ago
Oswald Buddenhagen 8b8313997c Revert "actually implement imap_commit_cmds()" 2 months ago
Oswald Buddenhagen 84194a7a9f don't try to create already existing boxes 2 months ago
Oswald Buddenhagen ceb0fa9803 don't try to qsort() NULL array 2 months ago
Oswald Buddenhagen 12e30ce560 cap readsz at buffer size 2 months ago
  1. 13
      .gitignore
  2. 7
      AUTHORS
  3. 4
      LICENSES/LicenseRef-isync-GPL-exception.txt
  4. 2
      Makefile.am
  5. 8
      configure.ac
  6. 3
      src/.gitignore
  7. 29
      src/Makefile.am
  8. 70
      src/bit_enum_gen.pl
  9. 137
      src/common.h
  10. 2
      src/config.c
  11. 86
      src/driver.h
  12. 70
      src/drv_imap.c
  13. 9
      src/drv_maildir.c
  14. 40
      src/drv_proxy.c
  15. 1
      src/drv_proxy_gen.pl
  16. 3
      src/main_sync.c
  17. 4
      src/mbsync.1.in
  18. 2
      src/mdconvert.1.in
  19. 5
      src/socket.c
  20. 51
      src/sync.c
  21. 57
      src/sync.h
  22. 43
      src/sync_p.h
  23. 2
      src/sync_state.c
  24. 36
      version.sh

13
.gitignore vendored

@ -1,6 +1,7 @@
/.autoconf_trace /.autoconf_trace
/ChangeLog /ChangeLog
/INSTALL /INSTALL
/VERSION
/autom4te.cache/ /autom4te.cache/
/aclocal.m4 /aclocal.m4
/autodefs.h /autodefs.h
@ -33,3 +34,15 @@
Makefile Makefile
Makefile.in Makefile.in
GPATH
GRTAGS
GSYMS
GTAGS
ID
TAGS
cscope.files
cscope.in.out
cscope.out
cscope.po.out
tags

7
AUTHORS

@ -13,7 +13,7 @@ Lead Developers
Oswald Buddenhagen <ossi@users.sf.net> Oswald Buddenhagen <ossi@users.sf.net>
- Current maintainer - Current maintainer
Michael Elkins <me@mutt.org> Michael Elkins <me@sigpipe.org>
- Original author - Original author
Contributors Contributors
@ -22,10 +22,10 @@ Contributors
(Some of these people also contributed bugfixes and optimizations.) (Some of these people also contributed bugfixes and optimizations.)
(In chronological order.) (In chronological order.)
Jeremy Katz <katzj@linuxpower.org> Jeremy Katz <katzj@fedoraproject.org>
- UseNamespace & UseSSL* options - UseNamespace & UseSSL* options
Daniel Resare <noa@metamatrix.se> Noa (ex. Daniel) Resare <noa@resare.com>
- Numerous SSL handling improvements - Numerous SSL handling improvements
Eivind Eklund <eivind@FreeBSD.org> Eivind Eklund <eivind@FreeBSD.org>
@ -82,6 +82,7 @@ Gergely Risko <gergely@risko.hu>
Sung Pae "guns" <self@sungpae.com> Sung Pae "guns" <self@sungpae.com>
Helmut Grohne <helmut@subdivi.de> Helmut Grohne <helmut@subdivi.de>
Hugo Haas <hugo@larve.net> Hugo Haas <hugo@larve.net>
Husain Alshehhi <husain@alshehhi.io>
Jaroslav Suchanek <jaroslav.suchanek@gmail.com> Jaroslav Suchanek <jaroslav.suchanek@gmail.com>
Jeremie Courreges-Anglas <jca@openbsd.org> Jeremie Courreges-Anglas <jca@openbsd.org>
Klemens Nanni <kn@openbsd.org> Klemens Nanni <kn@openbsd.org>

4
LICENSES/LicenseRef-isync-GPL-exception.txt

@ -8,5 +8,5 @@ Usage-Guide:
SPDX-License-Identifier: <SPDX-License> WITH LicenseRef-isync-GPL-exception SPDX-License-Identifier: <SPDX-License> WITH LicenseRef-isync-GPL-exception
License-Text: License-Text:
As a special exception, mbsync may be linked with the OpenSSL library, As a special exception, mbsync may be linked with libraries with a more
despite that library's more restrictive license. restrictive license if the only incompatibility are advertising clauses.

2
Makefile.am

@ -4,7 +4,7 @@
SUBDIRS = src SUBDIRS = src
bin_SCRIPTS = mbsync-get-cert bin_SCRIPTS = mbsync-get-cert
EXTRA_DIST = LICENSES debian isync.spec $(bin_SCRIPTS) EXTRA_DIST = LICENSES VERSION debian isync.spec $(bin_SCRIPTS)
LOG_PL = \ LOG_PL = \
use POSIX qw(strftime); \ use POSIX qw(strftime); \

8
configure.ac

@ -2,7 +2,8 @@
# SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen <ossi@users.sf.net> # SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen <ossi@users.sf.net>
dnl SPDX-License-Identifier: GPL-2.0-or-later dnl SPDX-License-Identifier: GPL-2.0-or-later
AC_INIT([isync], [1.5.0]) m4_syscmd([./version.sh])
AC_INIT([isync], m4_sinclude([VERSION]))
AC_CONFIG_HEADERS([autodefs.h]) AC_CONFIG_HEADERS([autodefs.h])
AC_CANONICAL_TARGET AC_CANONICAL_TARGET
@ -250,7 +251,10 @@ if test "x$have_macos_keychain" != xno; then
AC_SUBST(KEYCHAIN_LIBS, ["-Wl,-framework,Security,-framework,CoreFoundation"]) AC_SUBST(KEYCHAIN_LIBS, ["-Wl,-framework,Security,-framework,CoreFoundation"])
fi fi
AC_CONFIG_FILES([Makefile src/Makefile isync.spec]) RELEASE_DATE=`date -r $0 +%F`
AC_SUBST(RELEASE_DATE)
AC_CONFIG_FILES([Makefile src/Makefile src/mbsync.1 src/mdconvert.1 isync.spec])
AC_OUTPUT AC_OUTPUT
AC_MSG_RESULT() AC_MSG_RESULT()

3
src/.gitignore vendored

@ -1,7 +1,8 @@
/*_enum.h
/drv_proxy.inc /drv_proxy.inc
/mbsync /mbsync
/mbsync.1
/mdconvert /mdconvert
/mdconvert.1
/tst_imap_msgs /tst_imap_msgs
/tst_imap_utf7 /tst_imap_utf7
/tst_msg_cvt /tst_msg_cvt

29
src/Makefile.am

@ -20,28 +20,6 @@ drv_proxy.$(OBJEXT): drv_proxy.inc
drv_proxy.inc: $(srcdir)/driver.h $(srcdir)/drv_proxy.c $(srcdir)/drv_proxy_gen.pl drv_proxy.inc: $(srcdir)/driver.h $(srcdir)/drv_proxy.c $(srcdir)/drv_proxy_gen.pl
perl $(srcdir)/drv_proxy_gen.pl $(srcdir)/driver.h $(srcdir)/drv_proxy.c drv_proxy.inc perl $(srcdir)/drv_proxy_gen.pl $(srcdir)/driver.h $(srcdir)/drv_proxy.c drv_proxy.inc
ENUM_GEN = $(srcdir)/bit_enum_gen.pl
$(mbsync_OBJECTS): common_enum.h
common_enum.h: common.h $(ENUM_GEN)
perl $(ENUM_GEN) < $< > $@
$(mbsync_OBJECTS): driver_enum.h
driver_enum.h: driver.h $(ENUM_GEN)
perl $(ENUM_GEN) < $< > $@
$(mbsync_OBJECTS): sync_enum.h
sync_enum.h: sync.h $(ENUM_GEN)
perl $(ENUM_GEN) < $< > $@
sync.$(OBJEXT): sync_c_enum.h
sync_c_enum.h: sync.c $(ENUM_GEN)
perl $(ENUM_GEN) < $< > $@
sync.$(OBJEXT) sync_state.$(OBJEXT): sync_p_enum.h
sync_p_enum.h: sync_p.h $(ENUM_GEN)
perl $(ENUM_GEN) < $< > $@
mdconvert_SOURCES = mdconvert.c mdconvert_SOURCES = mdconvert.c
mdconvert_LDADD = $(DB_LIBS) mdconvert_LDADD = $(DB_LIBS)
if with_mdconvert if with_mdconvert
@ -49,7 +27,10 @@ mdconvert_prog = mdconvert
mdconvert_man = mdconvert.1 mdconvert_man = mdconvert.1
endif endif
in_man = mbsync.1.in mdconvert.1.in
bin_PROGRAMS = mbsync $(mdconvert_prog) bin_PROGRAMS = mbsync $(mdconvert_prog)
# don't forget to update AC_CONFIG_FILES in configure.ac!
man_MANS = mbsync.1 $(mdconvert_man) man_MANS = mbsync.1 $(mdconvert_man)
tst_imap_msgs_SOURCES = tst_imap_msgs.c imap_msgs.c util.c tst_imap_msgs_SOURCES = tst_imap_msgs.c imap_msgs.c util.c
@ -69,6 +50,6 @@ EXTRA_PROGRAMS = tst_timers
exampledir = $(docdir)/examples exampledir = $(docdir)/examples
example_DATA = mbsyncrc.sample example_DATA = mbsyncrc.sample
EXTRA_DIST = bit_enum_gen.pl drv_proxy_gen.pl run-tests.pl $(example_DATA) $(man_MANS) EXTRA_DIST = drv_proxy_gen.pl run-tests.pl $(example_DATA) $(in_man)
CLEANFILES = *_enum.h drv_proxy.inc CLEANFILES = drv_proxy.inc

70
src/bit_enum_gen.pl

@ -1,70 +0,0 @@
#!/usr/bin/perl
#
# SPDX-FileCopyrightText: 2022 Oswald Buddenhagen <ossi@users.sf.net>
# SPDX-License-Identifier: GPL-2.0-or-later
#
# mbsync - mailbox synchronizer
#
use strict;
use warnings;
my $in_enum = 0;
my $conts;
while (<>) {
s,\s*(?://.*)?$,,;
if ($in_enum) {
if (/^\)$/) {
$conts =~ s/\s//g;
$conts =~ s/,$//;
my @vals = split(/,/, $conts);
my ($pfx, $pfx1);
for my $e (@vals) {
if (!defined($pfx)) {
$pfx1 = $pfx = ($e =~ /^([A-Z]+_)/) ? $1 : "";
} elsif (length($pfx)) {
$pfx = "" if ((($e =~ /^([A-Z]+_)/) ? $1 : "") ne $pfx);
}
}
my $bit = 1;
my $bitn = 0;
my (@names, @nameos);
my $nameo = 0;
for my $e (@vals) {
my $bits = ($e =~ s/\((\d+)\)$//) ? $1 : 1;
my $n = substr($e, length($pfx));
if ($bits != 1) {
die("Unsupported field size $bits\n") if ($bits != 2);
print "#define $e(b) ($bit << (b))\n";
push @names, "F-".$n, "N-".$n;
my $nl = length($n) + 3;
push @nameos, $nameo, $nameo + $nl;
$nameo += $nl * 2;
} else {
print "#define $e $bit\n";
push @names, $n;
push @nameos, $nameo;
$nameo += length($n) + 1;
}
$bit <<= $bits;
$bitn += $bits;
}
if (length($pfx)) {
print "#define ${pfx}_NUM_BITS $bitn\n";
}
if (length($pfx1)) {
print "#define ${pfx1}_STRINGS \"".join("\\0", @names)."\"\n";
print "#define ${pfx1}_OFFSETS ".join(", ", @nameos)."\n";
}
print "\n";
$in_enum = 0;
} else {
$conts .= $_;
}
} else {
if (/^BIT_ENUM\($/) {
$conts = "";
$in_enum = 1;
}
}
}

137
src/common.h

@ -21,8 +21,6 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include "common_enum.h"
typedef unsigned char uchar; typedef unsigned char uchar;
typedef unsigned short ushort; typedef unsigned short ushort;
typedef unsigned int uint; typedef unsigned int uint;
@ -43,10 +41,55 @@ typedef unsigned long ulong;
#define shifted_bit(in, from, to) \ #define shifted_bit(in, from, to) \
((int)(((uint)(in) / (from > to ? from / to : 1) * (to > from ? to / from : 1)) & to)) ((int)(((uint)(in) / (from > to ? from / to : 1) * (to > from ? to / from : 1)) & to))
#define BIT_ENUM(...) #define BIT_ENUM_VAL(name) \
name, \
name##_dummy = 2 * name - 1,
#define DEFINE_BIT_ENUM(list) \
enum list { \
list##_dummy, \
list(BIT_ENUM_VAL) \
};
#define PFX_BIT_ENUM_VAL(pfx, name) \
pfx##_##name, \
pfx##_##name##_dummy = 2 * pfx##_##name - 1,
#define PFX_BIT_ENUM_NUM(pfx, name) \
pfx##_##name##_bit,
#define DEFINE_PFX_BIT_ENUM(list) \
enum list { \
list##_dummy, \
list(PFX_BIT_ENUM_VAL) \
}; \
enum { \
list(PFX_BIT_ENUM_NUM) \
list##_num_bits \
};
#define BIT_ENUM_STR(pfx, name) \
stringify(name) "\0"
#define BIT_ENUM_STR_OFF(pfx, name) \
name##_str_off, \
name##_str_off_dummy = name##_str_off + sizeof(stringify(name)) - 1,
#define GET_BIT_ENUM_STR_OFF(pfx, name) \
name##_str_off,
#define static_assert_bits(pfx, type, field) \ #define PFX_BIT_ENUM_STR(pfx, name) \
static_assert( pfx##__NUM_BITS <= sizeof(((type){ 0 }).field) * 8, \ stringify(pfx) "_" stringify(name) "\0"
#define PFX_BIT_ENUM_STR_OFF(pfx, name) \
pfx##_##name##_str_off, \
pfx##_##name##_str_end = pfx##_##name##_str_off + sizeof(stringify(pfx) "_" stringify(name)) - 1,
#define GET_PFX_BIT_ENUM_STR_OFF(pfx, name) \
pfx##_##name##_str_off,
#define static_assert_bits(list, type, field) \
static_assert( list##_num_bits <= sizeof(((type){ 0 }).field) * 8, \
stringify(type) "::" stringify(field) " is too small" ) stringify(type) "::" stringify(field) " is too small" )
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
@ -100,30 +143,32 @@ enum {
VERBOSE, VERBOSE,
}; };
BIT_ENUM( #define options_enum(fn) \
DEBUG_MAILDIR, fn(DEBUG_MAILDIR) \
DEBUG_NET, fn(DEBUG_NET) \
DEBUG_NET_ALL, fn(DEBUG_NET_ALL) \
DEBUG_SYNC, fn(DEBUG_SYNC) \
DEBUG_MAIN, fn(DEBUG_MAIN) \
DEBUG_DRV, fn(DEBUG_DRV) \
DEBUG_DRV_ALL, fn(DEBUG_DRV_ALL) \
\
DEBUG_CRASH, fn(DEBUG_CRASH) \
\
PROGRESS, fn(PROGRESS) \
\
DRYRUN, fn(DRYRUN) \
\
EXT_EXIT, fn(EXT_EXIT) \
\
ZERODELAY, fn(ZERODELAY) \
KEEPJOURNAL, fn(KEEPJOURNAL) \
FORCEJOURNAL, fn(FORCEJOURNAL) \
FORCEASYNC(2), fn(FORCEASYNC_F) \
FAKEEXPUNGE, fn(FORCEASYNC_N) \
FAKEDUMBSTORE, fn(FAKEEXPUNGE) \
) fn(FAKEDUMBSTORE)
DEFINE_BIT_ENUM(options_enum)
#define FORCEASYNC(b) (FORCEASYNC_F << (b))
#define DEBUG_ANY (DEBUG_MAILDIR | DEBUG_NET | DEBUG_SYNC | DEBUG_MAIN | DEBUG_DRV) #define DEBUG_ANY (DEBUG_MAILDIR | DEBUG_NET | DEBUG_SYNC | DEBUG_MAIN | DEBUG_DRV)
#define DEBUG_ALL (DEBUG_ANY | DEBUG_CRASH) #define DEBUG_ALL (DEBUG_ANY | DEBUG_CRASH)
@ -209,34 +254,38 @@ time_t timegm( struct tm *tm );
void fmt_bits( uint bits, uint num_bits, const char *bit_str, const int *bit_off, char *buf ); void fmt_bits( uint bits, uint num_bits, const char *bit_str, const int *bit_off, char *buf );
#define BIT_FORMATTER_RET(name, pfx) \ #define BIT_FORMATTER_RET(name, list, fn) \
struct name##_str { char str[sizeof(pfx##__STRINGS)]; }; enum { list(fn##_OFF) name##_str_len }; \
struct name##_str { char str[name##_str_len]; };
#define BIT_FORMATTER_PROTO(name, pfx, storage) \ #define BIT_FORMATTER_PROTO(name, storage) \
storage struct name##_str ATTR_OPTIMIZE /* force RVO */ \ storage struct name##_str ATTR_OPTIMIZE /* force RVO */ \
fmt_##name( uint bits ) fmt_##name( uint bits )
#define BIT_FORMATTER_IMPL(name, pfx, storage) \ #define BIT_FORMATTER_IMPL(name, list, fn, storage) \
BIT_FORMATTER_PROTO(name, pfx, storage) \ BIT_FORMATTER_PROTO(name, storage) \
{ \ { \
static const char strings[] = pfx##__STRINGS; \ static const char strings[] = list(fn); \
static const int offsets[] = { pfx##__OFFSETS }; \ static const int offsets[] = { list(GET_##fn##_OFF) }; \
\ \
struct name##_str buf; \ struct name##_str buf; \
fmt_bits( bits, as(offsets), strings, offsets, buf.str ); \ fmt_bits( bits, as(offsets), strings, offsets, buf.str ); \
return buf; \ return buf; \
} }
#define BIT_FORMATTER_FUNCTION(name, pfx) \ // Note that this one uses enum labels without prefix ...
BIT_FORMATTER_RET(name, pfx) \ #define BIT_FORMATTER_FUNCTION(name, list) \
BIT_FORMATTER_IMPL(name, pfx, static) BIT_FORMATTER_RET(name, list, BIT_ENUM_STR) \
BIT_FORMATTER_IMPL(name, list, BIT_ENUM_STR, static)
#define DECL_BIT_FORMATTER_FUNCTION(name, pfx) \ // ... while these ones use enum labels with prefix - this
BIT_FORMATTER_RET(name, pfx) \ // is not fundamental, but simply because of the use cases.
BIT_FORMATTER_PROTO(name, pfx, ); #define DECL_BIT_FORMATTER_FUNCTION(name, list) \
BIT_FORMATTER_RET(name, list, PFX_BIT_ENUM_STR) \
BIT_FORMATTER_PROTO(name, );
#define DEF_BIT_FORMATTER_FUNCTION(name, pfx) \ #define DEF_BIT_FORMATTER_FUNCTION(name, list) \
BIT_FORMATTER_IMPL(name, pfx, ) BIT_FORMATTER_IMPL(name, list, PFX_BIT_ENUM_STR, )
void *nfmalloc( size_t sz ); void *nfmalloc( size_t sz );
void *nfzalloc( size_t sz ); void *nfzalloc( size_t sz );

2
src/config.c

@ -22,7 +22,7 @@ char FieldDelimiter = ';';
char FieldDelimiter = ':'; char FieldDelimiter = ':';
#endif #endif
DEF_BIT_FORMATTER_FUNCTION(ops, OP) DEF_BIT_FORMATTER_FUNCTION(ops, sync_op_enum)
char * char *
expand_strdup( const char *s, const conffile_t *cfile ) expand_strdup( const char *s, const conffile_t *cfile )

86
src/driver.h

@ -9,7 +9,6 @@
#define DRIVER_H #define DRIVER_H
#include "config.h" #include "config.h"
#include "driver_enum.h"
typedef struct driver driver_t; typedef struct driver driver_t;
@ -36,32 +35,32 @@ extern store_conf_t *stores;
/* For message->flags */ /* For message->flags */
// Keep the MESSAGE_FLAGS in sync (grep that)! // Keep the MESSAGE_FLAGS in sync (grep that)!
/* The order is according to alphabetical maildir flag sort */ /* The order is according to alphabetical maildir flag sort */
BIT_ENUM( #define msg_flags_enum(fn) \
F_DRAFT, // Draft fn(F, DRAFT) /* Draft */ \
F_FLAGGED, // Flagged fn(F, FLAGGED) /* Flagged */ \
F_FORWARDED, // Passed fn(F, FORWARDED) /* Passed */ \
F_ANSWERED, // Replied fn(F, ANSWERED) /* Replied */ \
F_SEEN, // Seen fn(F, SEEN) /* Seen */ \
F_DELETED, // Trashed fn(F, DELETED) /* Trashed */
) DEFINE_PFX_BIT_ENUM(msg_flags_enum)
extern const char MsgFlags[F__NUM_BITS]; extern const char MsgFlags[msg_flags_enum_num_bits];
typedef struct { char str[F__NUM_BITS + 1]; } flag_str_t; typedef struct { char str[msg_flags_enum_num_bits + 1]; } flag_str_t;
flag_str_t ATTR_OPTIMIZE /* force RVO */ fmt_flags( uchar flags ); flag_str_t ATTR_OPTIMIZE /* force RVO */ fmt_flags( uchar flags );
flag_str_t ATTR_OPTIMIZE /* force RVO */ fmt_lone_flags( uchar flags ); flag_str_t ATTR_OPTIMIZE /* force RVO */ fmt_lone_flags( uchar flags );
/* For message->status */ /* For message->status */
BIT_ENUM( #define msg_sts_enum(fn) \
M_RECENT, // unsyncable flag; maildir_*() depend on this being bit 0 fn(M, RECENT) /* unsyncable flag; maildir_*() depend on this being bit 0 */ \
M_DEAD, // expunged fn(M, DEAD) /* expunged */ \
M_EXPUNGE, // for driver_t->close_box() fn(M, EXPUNGE) /* for driver_t->close_box() */ \
M_FLAGS, // flags are valid fn(M, FLAGS) /* flags are valid */ \
// The following are only for IMAP FETCH response parsing /* The following are only for IMAP FETCH response parsing */ \
M_DATE, fn(M, DATE) \
M_SIZE, fn(M, SIZE) \
M_BODY, fn(M, BODY) \
M_HEADER, fn(M, HEADER)
) DEFINE_PFX_BIT_ENUM(msg_sts_enum)
#define TUIDL 12 #define TUIDL 12
@ -79,29 +78,29 @@ typedef struct message {
MESSAGE(struct message) MESSAGE(struct message)
} message_t; } message_t;
static_assert_bits(F, message_t, flags); static_assert_bits(msg_flags_enum, message_t, flags);
static_assert_bits(M, message_t, status); static_assert_bits(msg_sts_enum, message_t, status);
// For driver_t->prepare_load_box(), which may amend the passed flags. // For driver_t->prepare_load_box(), which may amend the passed flags.
// The drivers don't use the first three, but may set them if loading the // The drivers don't use the first three, but may set them if loading the
// particular range is required to handle some other flag; note that these // particular range is required to handle some other flag; note that these
// ranges may overlap. // ranges may overlap.
BIT_ENUM( #define open_flags_enum(fn) \
OPEN_PAIRED, // Paired messages *in* this store. fn(OPEN, PAIRED) /* Paired messages *in* this store. */ \
OPEN_OLD, // Messages that should be already propagated *from* this store. fn(OPEN, OLD) /* Messages that should be already propagated *from* this store. */ \
OPEN_NEW, // Messages (possibly) not yet propagated *from* this store. fn(OPEN, NEW) /* Messages (possibly) not yet propagated *from* this store. */ \
OPEN_FIND, fn(OPEN, FIND) \
OPEN_FLAGS, // Note that fetch_msg() gets the flags regardless. fn(OPEN, FLAGS) /* Note that fetch_msg() gets the flags regardless. */ \
OPEN_OLD_SIZE, fn(OPEN, OLD_SIZE) \
OPEN_NEW_SIZE, fn(OPEN, NEW_SIZE) \
OPEN_PAIRED_IDS, fn(OPEN, PAIRED_IDS) \
OPEN_APPEND, fn(OPEN, APPEND) \
OPEN_SETFLAGS, fn(OPEN, SETFLAGS) \
OPEN_EXPUNGE, fn(OPEN, EXPUNGE) \
// Expunge only deleted messages we know about. Relies on OPEN_{OLD,NEW,FLAGS} /* Expunge only deleted messages we know about. Relies on OPEN_{OLD,NEW,FLAGS} */ \
// being set externally. The driver may unset it if it can't handle it. /* being set externally. The driver may unset it if it can't handle it. */ \
OPEN_UID_EXPUNGE, fn(OPEN, UID_EXPUNGE)
) DEFINE_PFX_BIT_ENUM(open_flags_enum)
#define UIDVAL_BAD ((uint)-1) #define UIDVAL_BAD ((uint)-1)
@ -122,7 +121,7 @@ typedef struct {
uchar flags; uchar flags;
} msg_data_t; } msg_data_t;
static_assert_bits(F, msg_data_t, flags); static_assert_bits(msg_flags_enum, msg_data_t, flags);
#define DRV_OK 0 #define DRV_OK 0
/* Message went missing, or mailbox is full, etc. */ /* Message went missing, or mailbox is full, etc. */
@ -295,9 +294,6 @@ struct driver {
void (*cancel_cmds)( store_t *ctx, void (*cancel_cmds)( store_t *ctx,
void (*cb)( void *aux ), void *aux ); void (*cb)( void *aux ), void *aux );
/* Commit any pending set_msg_flags() commands. */
void (*commit_cmds)( store_t *ctx );
/* Get approximate amount of memory occupied by the driver. */ /* Get approximate amount of memory occupied by the driver. */
uint (*get_memory_usage)( store_t *ctx ); uint (*get_memory_usage)( store_t *ctx );

70
src/drv_imap.c

@ -163,8 +163,7 @@ union imap_store {
// Command queue // Command queue
imap_cmd_t *pending, **pending_append; imap_cmd_t *pending, **pending_append;
imap_cmd_t *in_progress, **in_progress_append; imap_cmd_t *in_progress, **in_progress_append;
imap_cmd_t *wait_check, **wait_check_append; int nexttag, num_in_progress;
int nexttag, num_in_progress, num_wait_check;
uint buffer_mem; // Memory currently occupied by buffers in the queue uint buffer_mem; // Memory currently occupied by buffers in the queue
// Used during sequential operations like connect // Used during sequential operations like connect
@ -204,7 +203,6 @@ union imap_store {
uint data_len; \ uint data_len; \
uint uid; /* to identify fetch responses */ \ uint uid; /* to identify fetch responses */ \
char high_prio; /* if command is queued, put it at the front of the queue. */ \ char high_prio; /* if command is queued, put it at the front of the queue. */ \
char wait_check; /* Don't report success until subsequent CHECK success. */ \
char to_trash; /* we are storing to trash, not current. */ \ char to_trash; /* we are storing to trash, not current. */ \
char create; /* create the mailbox if we get an error which suggests so. */ \ char create; /* create the mailbox if we get an error which suggests so. */ \
char failok; /* Don't complain about NO (@1) / BAD (@2) response. */ \ char failok; /* Don't complain about NO (@1) / BAD (@2) response. */ \
@ -352,8 +350,6 @@ new_imap_cmd( uint size )
static void static void
done_imap_cmd( imap_store_t *ctx, imap_cmd_t *cmd, int response ) done_imap_cmd( imap_store_t *ctx, imap_cmd_t *cmd, int response )
{ {
if (cmd->param.wait_check)
ctx->num_wait_check--;
if (cmd->param.data) { if (cmd->param.data) {
free( cmd->param.data ); free( cmd->param.data );
cmd->param.data = NULL; cmd->param.data = NULL;
@ -482,18 +478,6 @@ flush_imap_cmds( imap_store_t *ctx )
} }
} }
static void
finalize_checked_imap_cmds( imap_store_t *ctx, int resp )
{
imap_cmd_t *cmd;
while ((cmd = ctx->wait_check)) {
if (!(ctx->wait_check = cmd->next))
ctx->wait_check_append = &ctx->wait_check;
done_imap_cmd( ctx, cmd, resp );
}
}
static void static void
cancel_pending_imap_cmds( imap_store_t *ctx ) cancel_pending_imap_cmds( imap_store_t *ctx )
{ {
@ -527,8 +511,6 @@ submit_imap_cmd( imap_store_t *ctx, imap_cmd_t *cmd )
assert( cmd ); assert( cmd );
assert( cmd->param.done ); assert( cmd->param.done );
if (cmd->param.wait_check)
ctx->num_wait_check++;
if ((ctx->pending && !cmd->param.high_prio) || !cmd_sendable( ctx, cmd )) { if ((ctx->pending && !cmd->param.high_prio) || !cmd_sendable( ctx, cmd )) {
if (ctx->pending && cmd->param.high_prio) { if (ctx->pending && cmd->param.high_prio) {
cmd->next = ctx->pending; cmd->next = ctx->pending;
@ -1548,7 +1530,7 @@ is_inbox( imap_store_t *ctx, const char *arg, int argl )
{ {
if (!starts_with_upper( arg, argl, "INBOX", 5 )) if (!starts_with_upper( arg, argl, "INBOX", 5 ))
return 0; return 0;
if (arg[5] && arg[5] != ctx->delimiter[0]) if (argl > 5 && arg[5] != ctx->delimiter[0])
return 0; return 0;
return 1; return 1;
} }
@ -1700,7 +1682,7 @@ prepare_box( char **buf, const imap_store_t *ctx )
if (!memcmp( name, "INBOX", 5 )) { if (!memcmp( name, "INBOX", 5 )) {
pfx = ""; pfx = "";
} else if (!*pfx) { } else if (!*pfx) {
error( "IMAP error: cannot use unqualified '%s'. Did you mean INBOX?", name ); error( "IMAP error: cannot use unqualified '%s'. Did you mean INBOX?\n", name );
return -1; return -1;
} }
} }
@ -1943,13 +1925,7 @@ imap_socket_read( void *aux )
imap_ref( ctx ); imap_ref( ctx );
if (resp == RESP_CANCEL) if (resp == RESP_CANCEL)
imap_invoke_bad_callback( ctx ); imap_invoke_bad_callback( ctx );
if (resp == RESP_OK && cmdp->param.wait_check) { done_imap_cmd( ctx, cmdp, resp );
cmdp->next = NULL;
*ctx->wait_check_append = cmdp;
ctx->wait_check_append = &cmdp->next;
} else {
done_imap_cmd( ctx, cmdp, resp );
}
if (imap_deref( ctx )) if (imap_deref( ctx ))
return; return;
if (ctx->canceling && !ctx->in_progress) { if (ctx->canceling && !ctx->in_progress) {
@ -1972,7 +1948,6 @@ get_cmd_result_p2( imap_store_t *ctx, imap_cmd_t *cmd, int response )
if (response != RESP_OK) { if (response != RESP_OK) {
done_imap_cmd( ctx, ocmd, response ); done_imap_cmd( ctx, ocmd, response );
} else { } else {
assert( !ocmd->param.wait_check );
ctx->uidnext = 1; ctx->uidnext = 1;
if (ocmd->param.to_trash) if (ocmd->param.to_trash)
ctx->trashnc = TrashKnown; ctx->trashnc = TrashKnown;
@ -1993,7 +1968,6 @@ imap_cancel_store( store_t *gctx )
sasl_dispose( &ctx->sasl ); sasl_dispose( &ctx->sasl );
#endif #endif
socket_close( &ctx->conn ); socket_close( &ctx->conn );
finalize_checked_imap_cmds( ctx, RESP_CANCEL );
cancel_sent_imap_cmds( ctx ); cancel_sent_imap_cmds( ctx );
cancel_pending_imap_cmds( ctx ); cancel_pending_imap_cmds( ctx );
free( ctx->ns_prefix ); free( ctx->ns_prefix );
@ -2053,7 +2027,7 @@ imap_free_store( store_t *gctx )
{ {
imap_store_t *ctx = (imap_store_t *)gctx; imap_store_t *ctx = (imap_store_t *)gctx;
assert( !ctx->pending && !ctx->in_progress && !ctx->wait_check ); assert( !ctx->pending && !ctx->in_progress );
if (ctx->state == SST_BAD) { if (ctx->state == SST_BAD) {
imap_cancel_store( gctx ); imap_cancel_store( gctx );
@ -2164,7 +2138,6 @@ imap_alloc_store( store_conf_t *conf, const char *label )
imap_socket_read, (void (*)(void *))flush_imap_cmds, ctx ); imap_socket_read, (void (*)(void *))flush_imap_cmds, ctx );
ctx->in_progress_append = &ctx->in_progress; ctx->in_progress_append = &ctx->in_progress;
ctx->pending_append = &ctx->pending; ctx->pending_append = &ctx->pending;
ctx->wait_check_append = &ctx->wait_check;
gotsrv: gotsrv:
ctx->conf = cfg; ctx->conf = cfg;
@ -2878,7 +2851,7 @@ imap_select_box( store_t *gctx, const char *name )
{ {
imap_store_t *ctx = (imap_store_t *)gctx; imap_store_t *ctx = (imap_store_t *)gctx;
assert( !ctx->pending && !ctx->in_progress && !ctx->wait_check ); assert( !ctx->pending && !ctx->in_progress );
reset_imap_messages( &ctx->msgs ); reset_imap_messages( &ctx->msgs );
@ -3137,7 +3110,7 @@ imap_load_box( store_t *gctx, uint minuid, uint maxuid, uint finduid, uint pairu
if (i != j) if (i != j)
bl += sprintf( buf + bl, ":%u", excs.data[i] ); bl += sprintf( buf + bl, ":%u", excs.data[i] );
} }
imap_submit_load( ctx, buf, shifted_bit( ctx->opts, OPEN_PAIRED_IDS, WantMsgids ), sts ); imap_submit_load( ctx, buf, shifted_bit( ctx->opts, (int)OPEN_PAIRED_IDS, WantMsgids ), sts );
} }
if (maxuid == UINT_MAX) if (maxuid == UINT_MAX)
maxuid = ctx->uidnext - 1; maxuid = ctx->uidnext - 1;
@ -3278,9 +3251,7 @@ imap_flags_helper( imap_store_t *ctx, uint uid, char what, int flags,
char buf[256]; char buf[256];
buf[imap_make_flags( flags, buf )] = 0; buf[imap_make_flags( flags, buf )] = 0;
imap_cmd_t *cmd = imap_refcounted_new_cmd( &sts->gen ); imap_exec( ctx, imap_refcounted_new_cmd( &sts->gen ), imap_set_flags_p2,
cmd->param.wait_check = 1;
imap_exec( ctx, cmd, imap_set_flags_p2,
"UID STORE %u %cFLAGS.SILENT %s", uid, what, buf ); "UID STORE %u %cFLAGS.SILENT %s", uid, what, buf );
} }
@ -3352,7 +3323,7 @@ imap_close_box( store_t *gctx,
{ {
imap_store_t *ctx = (imap_store_t *)gctx; imap_store_t *ctx = (imap_store_t *)gctx;
assert( !ctx->num_wait_check ); assert( !ctx->pending && !ctx->in_progress );
if (ctx->opts & OPEN_UID_EXPUNGE) { if (ctx->opts & OPEN_UID_EXPUNGE) {
INIT_REFCOUNTED_STATE(imap_expunge_state_t, sts, cb, aux) INIT_REFCOUNTED_STATE(imap_expunge_state_t, sts, cb, aux)
@ -3631,7 +3602,7 @@ imap_list_store( store_t *gctx, int flags,
// path | P [i] | i [P] | P // path | P [i] | i [P] | P
// //
int pfx_is_empty = !*ctx->prefix; int pfx_is_empty = !*ctx->prefix;
int pfx_is_inbox = !pfx_is_empty && is_inbox( ctx, ctx->prefix, -1 ); int pfx_is_inbox = !pfx_is_empty && is_inbox( ctx, ctx->prefix, strlen( ctx->prefix ) );
if (((flags & (LIST_PATH | LIST_PATH_MAYBE)) || pfx_is_empty) && !pfx_is_inbox && !(ctx->listed & LIST_PATH)) { if (((flags & (LIST_PATH | LIST_PATH_MAYBE)) || pfx_is_empty) && !pfx_is_inbox && !(ctx->listed & LIST_PATH)) {
ctx->listed |= LIST_PATH; ctx->listed |= LIST_PATH;
if (pfx_is_empty) if (pfx_is_empty)
@ -3672,7 +3643,6 @@ imap_cancel_cmds( store_t *gctx,
{ {
imap_store_t *ctx = (imap_store_t *)gctx; imap_store_t *ctx = (imap_store_t *)gctx;
finalize_checked_imap_cmds( ctx, RESP_CANCEL );
cancel_pending_imap_cmds( ctx ); cancel_pending_imap_cmds( ctx );
if (ctx->in_progress) { if (ctx->in_progress) {
ctx->canceling = 1; ctx->canceling = 1;
@ -3683,25 +3653,6 @@ imap_cancel_cmds( store_t *gctx,
} }
} }
/******************* imap_commit_cmds *******************/
static void imap_commit_cmds_p2( imap_store_t *, imap_cmd_t *, int );
static void
imap_commit_cmds( store_t *gctx )
{
imap_store_t *ctx = (imap_store_t *)gctx;
if (ctx->num_wait_check)
imap_exec( ctx, NULL, imap_commit_cmds_p2, "CHECK" );
}
static void
imap_commit_cmds_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response )
{
finalize_checked_imap_cmds( ctx, response );
}
/******************* imap_get_memory_usage *******************/ /******************* imap_get_memory_usage *******************/
static uint static uint
@ -4080,7 +4031,6 @@ struct driver imap_driver = {
imap_trash_msg, imap_trash_msg,
imap_close_box, imap_close_box,
imap_cancel_cmds, imap_cancel_cmds,
imap_commit_cmds,
imap_get_memory_usage, imap_get_memory_usage,
imap_get_fail_state, imap_get_fail_state,
}; };

9
src/drv_maildir.c

@ -1533,7 +1533,7 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data, int minimal
} }
fstat( fd, &st ); fstat( fd, &st );
if (st.st_size > INT_MAX) { if (st.st_size > INT_MAX) {
error( "Maildir error: %s is too big", buf ); error( "Maildir error: %s is too big\n", buf );
goto mbad; goto mbad;
} }
data->len = st.st_size; data->len = st.st_size;
@ -1853,12 +1853,6 @@ maildir_cancel_cmds( store_t *gctx ATTR_UNUSED,
cb( aux ); cb( aux );
} }
static void
maildir_commit_cmds( store_t *gctx )
{
(void) gctx;
}
static uint static uint
maildir_get_memory_usage( store_t *gctx ATTR_UNUSED ) maildir_get_memory_usage( store_t *gctx ATTR_UNUSED )
{ {
@ -1983,7 +1977,6 @@ struct driver maildir_driver = {
maildir_trash_msg, maildir_trash_msg,
maildir_close_box, maildir_close_box,
maildir_cancel_cmds, maildir_cancel_cmds,
maildir_commit_cmds,
maildir_get_memory_usage, maildir_get_memory_usage,
maildir_get_fail_state, maildir_get_fail_state,
}; };

40
src/drv_proxy.c

@ -8,7 +8,7 @@
#include "driver.h" #include "driver.h"
BIT_FORMATTER_FUNCTION(opts, OPEN) BIT_FORMATTER_FUNCTION(opts, open_flags_enum)
typedef struct gen_cmd gen_cmd_t; typedef struct gen_cmd gen_cmd_t;
@ -21,7 +21,6 @@ typedef union proxy_store {
driver_t *real_driver; driver_t *real_driver;
store_t *real_store; store_t *real_store;
gen_cmd_t *pending_cmds, **pending_cmds_append; gen_cmd_t *pending_cmds, **pending_cmds_append;
gen_cmd_t *check_cmds, **check_cmds_append;
wakeup_t wakeup; wakeup_t wakeup;
uint fake_nextuid; uint fake_nextuid;
char is_fake; // Was "created" by dry-run char is_fake; // Was "created" by dry-run
@ -91,42 +90,25 @@ proxy_wakeup( void *aux )
} }
static void static void
proxy_invoke( gen_cmd_t *cmd, int checked, const char *name ) proxy_invoke( gen_cmd_t *cmd, const char *name )
{ {
proxy_store_t *ctx = cmd->ctx; proxy_store_t *ctx = cmd->ctx;
if (ctx->force_async) { if (ctx->force_async) {
debug( "%s[% 2d] Queue %s%s\n", ctx->label, cmd->tag, name, checked ? " (checked)" : "" ); debug( "%s[% 2d] Queue %s\n", ctx->label, cmd->tag, name );
cmd->next = NULL; cmd->next = NULL;
if (checked) { *ctx->pending_cmds_append = cmd;
*ctx->check_cmds_append = cmd; ctx->pending_cmds_append = &cmd->next;
ctx->check_cmds_append = &cmd->next; conf_wakeup( &ctx->wakeup, 0 );
} else {
*ctx->pending_cmds_append = cmd;
ctx->pending_cmds_append = &cmd->next;
conf_wakeup( &ctx->wakeup, 0 );
}
} else { } else {
cmd->queued_cb( cmd ); cmd->queued_cb( cmd );
proxy_cmd_done( cmd ); proxy_cmd_done( cmd );
} }
} }
static void
proxy_flush_checked_cmds( proxy_store_t *ctx )
{
if (ctx->check_cmds) {
*ctx->pending_cmds_append = ctx->check_cmds;
ctx->pending_cmds_append = ctx->check_cmds_append;
ctx->check_cmds_append = &ctx->check_cmds;
ctx->check_cmds = NULL;
conf_wakeup( &ctx->wakeup, 0 );
}
}
static void static void
proxy_cancel_queued_cmds( proxy_store_t *ctx ) proxy_cancel_queued_cmds( proxy_store_t *ctx )
{ {
if (ctx->pending_cmds || ctx->check_cmds) { if (ctx->pending_cmds) {
// This would involve directly invoking the result callbacks with // This would involve directly invoking the result callbacks with
// DRV_CANCEL, for which we'd need another set of dispatch functions. // DRV_CANCEL, for which we'd need another set of dispatch functions.
// The autotest doesn't need that, so save the effort. // The autotest doesn't need that, so save the effort.
@ -253,7 +235,7 @@ static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@v
cmd->callback = cb; cmd->callback = cb;
cmd->callback_aux = aux; cmd->callback_aux = aux;
@assign_state@ @assign_state@
proxy_invoke( &cmd->gen, @checked@, "@name@" ); proxy_invoke( &cmd->gen, "@name@" );
} }
//# END //# END
@ -382,7 +364,6 @@ static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@v
//# DEFINE store_msg_fake_cb_args , cmd->to_trash ? 0 : ctx->fake_nextuid++ //# DEFINE store_msg_fake_cb_args , cmd->to_trash ? 0 : ctx->fake_nextuid++
//# DEFINE store_msg_counted 1 //# DEFINE store_msg_counted 1
//# DEFINE set_msg_flags_checked 1
//# DEFINE set_msg_flags_print_fmt_args , uid=%u, add=%s, del=%s //# DEFINE set_msg_flags_print_fmt_args , uid=%u, add=%s, del=%s
//# DEFINE set_msg_flags_print_pass_args , cmd->uid, fmt_flags( cmd->add ).str, fmt_flags( cmd->del ).str //# DEFINE set_msg_flags_print_pass_args , cmd->uid, fmt_flags( cmd->add ).str, fmt_flags( cmd->del ).str
//# DEFINE set_msg_flags_driable 1 //# DEFINE set_msg_flags_driable 1
@ -397,10 +378,6 @@ static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@v
//# DEFINE close_box_fake_cb_args , 0 //# DEFINE close_box_fake_cb_args , 0
//# DEFINE close_box_counted 1 //# DEFINE close_box_counted 1
//# DEFINE commit_cmds_print_args
proxy_flush_checked_cmds( ctx );
//# END
//# DEFINE cancel_cmds_print_cb_args //# DEFINE cancel_cmds_print_cb_args
proxy_cancel_queued_cmds( ctx ); proxy_cancel_queued_cmds( ctx );
//# END //# END
@ -465,7 +442,6 @@ proxy_alloc_store( store_t *real_ctx, const char *label, int force_async )
ctx->label = label; ctx->label = label;
ctx->force_async = force_async; ctx->force_async = force_async;
ctx->pending_cmds_append = &ctx->pending_cmds; ctx->pending_cmds_append = &ctx->pending_cmds;
ctx->check_cmds_append = &ctx->check_cmds;
ctx->real_driver = real_ctx->driver; ctx->real_driver = real_ctx->driver;
ctx->real_store = real_ctx; ctx->real_store = real_ctx;
ctx->real_driver->set_callbacks( ctx->real_store, ctx->real_driver->set_callbacks( ctx->real_store,

1
src/drv_proxy_gen.pl

@ -158,7 +158,6 @@ for (@ptypes) {
my $r_pass_args = make_args($r_cmd_args); my $r_pass_args = make_args($r_cmd_args);
$replace{'assign_state'} = $r_pass_args =~ s/([^,]+), /cmd->$1 = $1\;\n/gr; $replace{'assign_state'} = $r_pass_args =~ s/([^,]+), /cmd->$1 = $1\;\n/gr;
$replace{'checked'} = '0';
$template = "CALLBACK"; $template = "CALLBACK";
} else { } else {
$pass_args = make_args($cmd_args); $pass_args = make_args($cmd_args);

3
src/main_sync.c

@ -186,7 +186,8 @@ filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns
boxarr[num] = NULL; boxarr[num] = NULL;
} }
} }
qsort( boxarr, num, sizeof(*boxarr), cmp_box_names ); if (boxarr)
qsort( boxarr, num, sizeof(*boxarr), cmp_box_names );
return boxarr; return boxarr;
} }

4
src/mbsync.1 → src/mbsync.1.in

@ -4,7 +4,7 @@
.\" .\"
.\" mbsync - mailbox synchronizer .\" mbsync - mailbox synchronizer
. .
.TH mbsync 1 "2022 Jun 16" .TH mbsync 1 @RELEASE_DATE@ "@PACKAGE_STRING@" "User Commands"
. .
.SH NAME .SH NAME
mbsync - synchronize IMAP4 and Maildir mailboxes mbsync - synchronize IMAP4 and Maildir mailboxes
@ -884,4 +884,4 @@ http://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml
.SH AUTHORS .SH AUTHORS
Originally written by Michael R. Elkins, Originally written by Michael R. Elkins,
rewritten and currently maintained by Oswald Buddenhagen, rewritten and currently maintained by Oswald Buddenhagen,
contributions by Theodore Y. Ts'o. contributions by many others; see the AUTHORS file for details.

2
src/mdconvert.1 → src/mdconvert.1.in

@ -3,7 +3,7 @@
.\" .\"
.\" mdconvert - Maildir mailbox UID storage scheme converter .\" mdconvert - Maildir mailbox UID storage scheme converter
. .
.TH mdconvert 1 "2004 Mar 27" .TH mdconvert 1 @RELEASE_DATE@ "@PACKAGE_STRING@" "User Commands"
. .
.SH NAME .SH NAME
mdconvert - Maildir mailbox UID storage scheme converter mdconvert - Maildir mailbox UID storage scheme converter

5
src/socket.c

@ -908,8 +908,11 @@ socket_fill( conn_t *sock )
// IIR filter for tracking average size of bulk reads. // IIR filter for tracking average size of bulk reads.
// We use this to optimize the free space at the end of the // We use this to optimize the free space at the end of the
// buffer, hence the factor of 1.5. // buffer, hence the factor of 1.5.
if (n >= MIN_BULK_READ) if (n >= MIN_BULK_READ) {
sock->readsz = (sock->readsz * 3 + n * 3 / 2) / 4; sock->readsz = (sock->readsz * 3 + n * 3 / 2) / 4;
if (sock->readsz > sizeof(sock->buf))
sock->readsz = sizeof(sock->buf);
}
socket_filled( sock, (uint)n ); socket_filled( sock, (uint)n );
} }

51
src/sync.c

@ -6,7 +6,6 @@
*/ */
#include "sync_p.h" #include "sync_p.h"
#include "sync_c_enum.h"
channel_conf_t global_conf; channel_conf_t global_conf;
channel_conf_t *channels; channel_conf_t *channels;
@ -45,24 +44,24 @@ static int check_cancel( sync_vars_t *svars );
cleanup: close(F) & close(N) cleanup: close(F) & close(N)
*/ */
BIT_ENUM( #define sync_sts_enum(fn) \
ST_PRESENT, fn(ST, PRESENT) \
ST_CONFIRMED, fn(ST, CONFIRMED) \
ST_SELECTED, fn(ST, SELECTED) \
ST_FIND_OLD, fn(ST, FIND_OLD) \
ST_LOADED, fn(ST, LOADED) \
ST_SENT_FLAGS, fn(ST, SENT_FLAGS) \
ST_SENDING_NEW, fn(ST, SENDING_NEW) \
ST_SENT_NEW, fn(ST, SENT_NEW) \
ST_FIND_NEW, fn(ST, FIND_NEW) \
ST_FOUND_NEW, fn(ST, FOUND_NEW) \
ST_SENT_TRASH, fn(ST, SENT_TRASH) \
ST_TRASH_BAD, fn(ST, TRASH_BAD) \
ST_CLOSING, fn(ST, CLOSING) \
ST_CLOSED, fn(ST, CLOSED) \
ST_SENT_CANCEL, fn(ST, SENT_CANCEL) \
ST_CANCELED, fn(ST, CANCELED)
) DEFINE_PFX_BIT_ENUM(sync_sts_enum)
static uchar static uchar
sanitize_flags( uchar tflags, sync_vars_t *svars, int t ) sanitize_flags( uchar tflags, sync_vars_t *svars, int t )
@ -377,10 +376,13 @@ sync_boxes( store_t *ctx[], const char * const names[], int present[], channel_c
sync_ref( svars ); sync_ref( svars );
for (t = 0; ; t++) { for (t = 0; ; t++) {
info( "Opening %s box %s...\n", str_fn[t], svars->orig_name[t] ); info( "Opening %s box %s...\n", str_fn[t], svars->orig_name[t] );
if (present[t] == BOX_ABSENT) if (present[t] == BOX_ABSENT) {
box_confirmed2( svars, t ); box_confirmed2( svars, t );
else } else {
if (present[t] == BOX_PRESENT)
svars->state[t] |= ST_PRESENT;
svars->drv[t]->open_box( ctx[t], box_confirmed, AUX ); svars->drv[t]->open_box( ctx[t], box_confirmed, AUX );
}
if (t || check_cancel( svars )) if (t || check_cancel( svars ))
break; break;
} }
@ -399,6 +401,12 @@ box_confirmed( int sts, uint uidvalidity, void *aux )
if (sts == DRV_OK) { if (sts == DRV_OK) {
svars->state[t] |= ST_PRESENT; svars->state[t] |= ST_PRESENT;
svars->newuidval[t] = uidvalidity; svars->newuidval[t] = uidvalidity;
} else if (svars->state[t] & ST_PRESENT) {
error( "Error: channel %s: %s box %s cannot be opened.\n",
svars->chan->name, str_fn[t], svars->orig_name[t] );
svars->ret |= SYNC_FAIL;
cancel_sync( svars );
return;
} }
box_confirmed2( svars, t ); box_confirmed2( svars, t );
} }
@ -1364,7 +1372,6 @@ box_loaded( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux
} }
} }
for (t = 0; t < 2; t++) { for (t = 0; t < 2; t++) {
svars->drv[t]->commit_cmds( svars->ctx[t] );
svars->state[t] |= ST_SENT_FLAGS; svars->state[t] |= ST_SENT_FLAGS;
msgs_flags_set( svars, t ); msgs_flags_set( svars, t );
if (check_cancel( svars )) if (check_cancel( svars ))

57
src/sync.h

@ -9,44 +9,43 @@
#define SYNC_H #define SYNC_H
#include "driver.h" #include "driver.h"
#include "sync_enum.h"
#define F 0 // far side #define F 0 // far side
#define N 1 // near side #define N 1 // near side
BIT_ENUM( #define sync_op_enum(fn) \
OP_NEW, fn(OP, NEW) \
OP_OLD, fn(OP, OLD) \
OP_UPGRADE, fn(OP, UPGRADE) \
OP_GONE, fn(OP, GONE) \
OP_FLAGS, fn(OP, FLAGS) \
OP_EXPUNGE, fn(OP, EXPUNGE) \
OP_EXPUNGE_SOLO, fn(OP, EXPUNGE_SOLO) \
OP_CREATE, fn(OP, CREATE) \
OP_REMOVE, fn(OP, REMOVE) \
\
XOP_PUSH, fn(XOP, PUSH) \
XOP_PULL, fn(XOP, PULL) \
XOP_HAVE_TYPE, // Aka mode; have at least one of dir and type (see below) fn(XOP, HAVE_TYPE) /* Aka mode; have at least one of dir and type (see below) */ \
// The following must all have the same bit shift from the corresponding OP_* flags. /* The following must all have the same bit shift from the corresponding OP_* flags. */ \
XOP_HAVE_EXPUNGE, fn(XOP, HAVE_EXPUNGE) \
XOP_HAVE_EXPUNGE_SOLO, fn(XOP, HAVE_EXPUNGE_SOLO) \
XOP_HAVE_CREATE, fn(XOP, HAVE_CREATE) \
XOP_HAVE_REMOVE, fn(XOP, HAVE_REMOVE) \
// ... until here. /* ... until here. */ \
XOP_TYPE_NOOP, fn(XOP, TYPE_NOOP) \
// ... and here again from scratch. /* ... and here again from scratch. */ \
XOP_EXPUNGE_NOOP, fn(XOP, EXPUNGE_NOOP) \
XOP_EXPUNGE_SOLO_NOOP, fn(XOP, EXPUNGE_SOLO_NOOP) \
XOP_CREATE_NOOP, fn(XOP, CREATE_NOOP) \
XOP_REMOVE_NOOP, fn(XOP, REMOVE_NOOP)
) DEFINE_PFX_BIT_ENUM(sync_op_enum)
#define OP_DFLT_TYPE (OP_NEW | OP_UPGRADE | OP_GONE | OP_FLAGS) #define OP_DFLT_TYPE (OP_NEW | OP_UPGRADE | OP_GONE | OP_FLAGS)
#define OP_MASK_TYPE (OP_DFLT_TYPE | OP_OLD) // Asserted in the target side ops #define OP_MASK_TYPE (OP_DFLT_TYPE | OP_OLD) // Asserted in the target side ops
#define XOP_MASK_DIR (XOP_PUSH | XOP_PULL) #define XOP_MASK_DIR (XOP_PUSH | XOP_PULL)
DECL_BIT_FORMATTER_FUNCTION(ops, OP) DECL_BIT_FORMATTER_FUNCTION(ops, sync_op_enum)
typedef struct channel_conf { typedef struct channel_conf {
struct channel_conf *next; struct channel_conf *next;

43
src/sync_p.h

@ -7,23 +7,28 @@
#define DEBUG_FLAG DEBUG_SYNC #define DEBUG_FLAG DEBUG_SYNC
#include "sync.h" #include "sync.h"
#include "sync_p_enum.h"
#define srec_sts_enum(fn) \
BIT_ENUM( fn(S, DEAD) /* ephemeral: the entry was killed and should be ignored */ \
S_DEAD, // ephemeral: the entry was killed and should be ignored fn(S, EXPIRE) /* the entry is being expired (expire-side message removal scheduled) */ \
S_EXPIRE, // the entry is being expired (expire-side message removal scheduled) fn(S, EXPIRED) /* the entry is expired (expire-side message removal confirmed) */ \
S_EXPIRED, // the entry is expired (expire-side message removal confirmed) fn(S, NEXPIRE) /* temporary: new expiration state */ \
S_NEXPIRE, // temporary: new expiration state fn(S, PENDING) /* the entry is new and awaits propagation (possibly a retry) */ \
S_PENDING, // the entry is new and awaits propagation (possibly a retry) fn(S, DUMMY_F) /* f/n message is only a placeholder */ \
S_DUMMY(2), // f/n message is only a placeholder fn(S, DUMMY_N) \
S_SKIPPED, // pre-1.4 legacy: the entry was not propagated (message is too big) fn(S, SKIPPED) /* pre-1.4 legacy: the entry was not propagated (message is too big) */ \
S_GONE(2), // ephemeral: f/n message has been expunged fn(S, GONE_F) /* ephemeral: f/n message has been expunged */ \
S_DEL(2), // ephemeral: f/n message would be subject to non-selective expunge fn(S, GONE_N) \
S_DELETE, // ephemeral: flags propagation is a deletion fn(S, DEL_F) /* ephemeral: f/n message would be subject to non-selective expunge */ \
S_UPGRADE, // ephemeral: upgrading placeholder, do not apply MaxSize fn(S, DEL_N) \
S_PURGE, // ephemeral: placeholder is being nuked fn(S, DELETE) /* ephemeral: flags propagation is a deletion */ \
S_PURGED, // ephemeral: placeholder was nuked fn(S, UPGRADE) /* ephemeral: upgrading placeholder, do not apply MaxSize */ \
) fn(S, PURGE) /* ephemeral: placeholder is being nuked */ \
fn(S, PURGED) /* ephemeral: placeholder was nuked */
DEFINE_PFX_BIT_ENUM(srec_sts_enum)
#define S_DUMMY(b) (S_DUMMY_F << (b))
#define S_GONE(b) (S_GONE_F << (b))
#define S_DEL(b) (S_DEL_F << (b))
// This is the persistent status of the sync record, with regard to the journal. // This is the persistent status of the sync record, with regard to the journal.
#define S_LOGGED (S_EXPIRE | S_EXPIRED | S_PENDING | S_DUMMY(F) | S_DUMMY(N) | S_SKIPPED) #define S_LOGGED (S_EXPIRE | S_EXPIRED | S_PENDING | S_DUMMY(F) | S_DUMMY(N) | S_SKIPPED)
@ -38,8 +43,8 @@ typedef struct sync_rec {
char tuid[TUIDL]; char tuid[TUIDL];
} sync_rec_t; } sync_rec_t;
static_assert_bits(F, sync_rec_t, flags); static_assert_bits(msg_flags_enum, sync_rec_t, flags);
static_assert_bits(S, sync_rec_t, status); static_assert_bits(srec_sts_enum, sync_rec_t, status);
typedef struct { typedef struct {
int t[2]; int t[2];

2
src/sync_state.c

@ -17,7 +17,7 @@
const char *str_fn[] = { "far side", "near side" }, *str_hl[] = { "push", "pull" }; const char *str_fn[] = { "far side", "near side" }, *str_hl[] = { "push", "pull" };
BIT_FORMATTER_FUNCTION(sts, S) BIT_FORMATTER_FUNCTION(sts, srec_sts_enum)
static char * static char *
clean_strdup( const char *s ) clean_strdup( const char *s )

36
version.sh

@ -0,0 +1,36 @@
#!/bin/sh
# SPDX-FileCopyrightText: (C) 2024 Oswald Buddenhagen <ossi@users.sf.net>
# SPDX-License-Identifier: GPL-2.0-or-later
cd $(dirname $0)
test -e .git || exit
mb=$(git merge-base HEAD "@{upstream}" 2> /dev/null)
if test -z "$mb"; then
# we presume that a failure to find a merge base means no upstream.
# and no upstream may mean detached head in the middle of a rebase
br=$(git branch | sed -n -e 's/^\* (no branch, rebasing \([^\)]*\))$/\1/p')
if test -n "$br"; then
mb=$(git merge-base HEAD "$br@{upstream}" 2> /dev/null)
fi
fi
if test -z "$mb"; then
# still no upstream, so just describe HEAD as-is.
gver=$(git describe --tags HEAD)
else
# find out whether we have local work, and if so, collapse it into
# a single suffix. otherwise, we'd cause pointless rebuilds during
# development.
gver=$(git describe --tags $mb)
lcl=$(git rev-list -n 1 $mb..HEAD)
if test -n "$lcl"; then
gver="$gver-plus"
fi
fi
gver=${gver#v}
pgver=$(cat VERSION 2> /dev/null)
if [ "x$gver" != "x$pgver" ]; then
echo "$gver" > VERSION
fi
Loading…
Cancel
Save