commit 168e5f3282c9c1f8a2d703b9baf1a4c0dc8ea24c
parent bdcc285403a0e0f7c256367b5685082aa9bd027f
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date: Tue, 21 Mar 2006 20:03:21 +0000
make the driver model, sync_chans() and sync_boxes() fully async.
async drivers to follow ...
Diffstat:
M | src/drv_imap.c | | | 183 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- |
M | src/drv_maildir.c | | | 198 | +++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ |
M | src/isync.h | | | 55 | ++++++++++++++++++++++++++++++++++++------------------- |
M | src/main.c | | | 232 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- |
M | src/sync.c | | | 1295 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
M | src/util.c | | | 12 | ++---------- |
6 files changed, 1331 insertions(+), 644 deletions(-)
diff --git a/src/drv_imap.c b/src/drv_imap.c
@@ -1199,8 +1199,9 @@ do_cram_auth( imap_store_t *ctx, struct imap_cmd *cmdp, const char *prompt )
}
#endif
-static store_t *
-imap_open_store( store_conf_t *conf )
+static void
+imap_open_store( store_conf_t *conf,
+ void (*cb)( store_t *srv, void *aux ), void *aux )
{
imap_store_conf_t *cfg = (imap_store_conf_t *)conf;
imap_server_conf_t *srvc = cfg->server;
@@ -1367,12 +1368,12 @@ imap_open_store( store_conf_t *conf )
}
#if HAVE_LIBSSL
if (CAP(CRAM)) {
- struct imap_cmd_cb cb;
+ struct imap_cmd_cb cbd;
info( "Authenticating with CRAM-MD5\n" );
- memset( &cb, 0, sizeof(cb) );
- cb.cont = do_cram_auth;
- if (imap_exec( ctx, &cb, "AUTHENTICATE CRAM-MD5" ) != RESP_OK)
+ memset( &cbd, 0, sizeof(cbd) );
+ cbd.cont = do_cram_auth;
+ if (imap_exec( ctx, &cbd, "AUTHENTICATE CRAM-MD5" ) != RESP_OK)
goto bail;
} else if (srvc->require_cram) {
error( "IMAP error: CRAM-MD5 authentication is not supported by server\n" );
@@ -1402,8 +1403,10 @@ imap_open_store( store_conf_t *conf )
else if (cfg->use_namespace && CAP(NAMESPACE)) {
/* get NAMESPACE info */
if (!ctx->got_namespace) {
- if (imap_exec( ctx, 0, "NAMESPACE" ) != RESP_OK)
- goto bail;
+ if (imap_exec( ctx, 0, "NAMESPACE" ) != RESP_OK) {
+ cb( 0, aux );
+ return;
+ }
ctx->got_namespace = 1;
}
/* XXX for now assume personal namespace */
@@ -1413,11 +1416,13 @@ imap_open_store( store_conf_t *conf )
ctx->prefix = ctx->ns_personal->child->child->val;
}
ctx->trashnc = 1;
- return &ctx->gen;
+ cb( &ctx->gen, aux );
+ return;
bail:
imap_cancel_store( &ctx->gen );
- return 0;
+ cb( 0, aux );
+ return;
}
static void
@@ -1433,13 +1438,14 @@ imap_prepare_opts( store_t *gctx, int opts )
gctx->opts = opts;
}
-static int
-imap_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs )
+static void
+imap_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs,
+ void (*cb)( int sts, void *aux ), void *aux )
{
imap_store_t *ctx = (imap_store_t *)gctx;
const char *prefix;
int ret, i, j, bl;
- struct imap_cmd_cb cb;
+ struct imap_cmd_cb cbd;
char buf[1000];
@@ -1451,10 +1457,10 @@ imap_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs )
prefix = ctx->prefix;
}
- memset( &cb, 0, sizeof(cb) );
- cb.create = (gctx->opts & OPEN_CREATE) != 0;
- cb.trycreate = 1;
- if ((ret = imap_exec_b( ctx, &cb, "SELECT \"%s%s\"", prefix, gctx->name )) != DRV_OK)
+ memset( &cbd, 0, sizeof(cbd) );
+ cbd.create = (gctx->opts & OPEN_CREATE) != 0;
+ cbd.trycreate = 1;
+ if ((ret = imap_exec_b( ctx, &cbd, "SELECT \"%s%s\"", prefix, gctx->name )) != DRV_OK)
goto bail;
if (gctx->count) {
@@ -1489,19 +1495,20 @@ imap_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs )
bail:
if (excs)
free( excs );
- return ret;
+ cb( ret, aux );
}
-static int
-imap_fetch_msg( store_t *ctx, message_t *msg, msg_data_t *data )
+static void
+imap_fetch_msg( store_t *ctx, message_t *msg, msg_data_t *data,
+ void (*cb)( int sts, void *aux ), void *aux )
{
- struct imap_cmd_cb cb;
+ struct imap_cmd_cb cbd;
- memset( &cb, 0, sizeof(cb) );
- cb.uid = msg->uid;
- cb.ctx = data;
- return imap_exec_m( (imap_store_t *)ctx, &cb, "UID FETCH %d (%sBODY.PEEK[])",
- msg->uid, (msg->status & M_FLAGS) ? "" : "FLAGS " );
+ memset( &cbd, 0, sizeof(cbd) );
+ cbd.uid = msg->uid;
+ cbd.ctx = data;
+ cb( imap_exec_m( (imap_store_t *)ctx, &cbd, "UID FETCH %d (%sBODY.PEEK[])",
+ msg->uid, (msg->status & M_FLAGS) ? "" : "FLAGS " ), aux );
}
static int
@@ -1531,8 +1538,9 @@ imap_flags_helper( imap_store_t *ctx, int uid, char what, int flags)
return issue_imap_cmd_w( ctx, 0, "UID STORE %d %cFLAGS.SILENT %s", uid, what, buf ) ? DRV_OK : DRV_STORE_BAD;
}
-static int
-imap_set_flags( store_t *gctx, message_t *msg, int uid, int add, int del )
+static void
+imap_set_flags( store_t *gctx, message_t *msg, int uid, int add, int del,
+ void (*cb)( int sts, void *aux ), void *aux )
{
imap_store_t *ctx = (imap_store_t *)gctx;
int ret;
@@ -1546,35 +1554,38 @@ imap_set_flags( store_t *gctx, message_t *msg, int uid, int add, int del )
}
if ((!add || (ret = imap_flags_helper( ctx, uid, '+', add )) == DRV_OK) &&
(!del || (ret = imap_flags_helper( ctx, uid, '-', del )) == DRV_OK))
- return DRV_OK;
- return ret;
+ ret = DRV_OK;
+ cb( ret, aux );
}
-static int
-imap_close( store_t *ctx )
+static void
+imap_close( store_t *ctx,
+ void (*cb)( int sts, void *aux ), void *aux )
{
- return imap_exec_b( (imap_store_t *)ctx, 0, "CLOSE" );
+ cb( imap_exec_b( (imap_store_t *)ctx, 0, "CLOSE" ), aux );
}
-static int
-imap_trash_msg( store_t *gctx, message_t *msg )
+static void
+imap_trash_msg( store_t *gctx, message_t *msg,
+ void (*cb)( int sts, void *aux ), void *aux )
{
imap_store_t *ctx = (imap_store_t *)gctx;
- struct imap_cmd_cb cb;
+ struct imap_cmd_cb cbd;
- memset( &cb, 0, sizeof(cb) );
- cb.create = 1;
- return imap_exec_m( ctx, &cb, "UID COPY %d \"%s%s\"",
- msg->uid, ctx->prefix, gctx->conf->trash );
+ memset( &cbd, 0, sizeof(cbd) );
+ cbd.create = 1;
+ cb( imap_exec_m( ctx, &cbd, "UID COPY %d \"%s%s\"",
+ msg->uid, ctx->prefix, gctx->conf->trash ), aux );
}
-static int
-imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
+static void
+imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
+ void (*cb)( int sts, int uid, void *aux ), void *aux )
{
imap_store_t *ctx = (imap_store_t *)gctx;
- struct imap_cmd_cb cb;
+ struct imap_cmd_cb cbd;
const char *prefix, *box;
- int ret, d;
+ int ret, d, uid;
char flagstr[128];
d = 0;
@@ -1584,71 +1595,82 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
}
flagstr[d] = 0;
- memset( &cb, 0, sizeof(cb) );
- cb.dlen = data->len;
- cb.data = data->data;
- if (!uid) {
+ memset( &cbd, 0, sizeof(cbd) );
+ cbd.dlen = data->len;
+ cbd.data = data->data;
+ cbd.ctx = &uid;
+ uid = -2;
+
+ if (to_trash) {
box = gctx->conf->trash;
prefix = ctx->prefix;
- cb.create = 1;
+ cbd.create = 1;
if (ctx->trashnc)
ctx->caps = ctx->rcaps & ~(1 << LITERALPLUS);
} else {
box = gctx->name;
prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix;
- cb.create = (gctx->opts & OPEN_CREATE) != 0;
+ cbd.create = (gctx->opts & OPEN_CREATE) != 0;
/*if (ctx->currentnc)
ctx->caps = ctx->rcaps & ~(1 << LITERALPLUS);*/
- *uid = -2;
}
- cb.ctx = uid;
- ret = imap_exec_m( ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr );
+ ret = imap_exec_m( ctx, &cbd, "APPEND \"%s%s\" %s", prefix, box, flagstr );
ctx->caps = ctx->rcaps;
- if (ret != DRV_OK)
- return ret;
- if (!uid)
+ if (ret != DRV_OK) {
+ cb( ret, -1, aux );
+ return;
+ }
+ if (to_trash)
ctx->trashnc = 0;
else {
/*ctx->currentnc = 0;*/
- gctx->count++;
}
- return DRV_OK;
+ cb( DRV_OK, uid, aux );
}
-static int
-imap_find_msg( store_t *gctx, const char *tuid, int *uid )
+static void
+imap_find_msg( store_t *gctx, const char *tuid,
+ void (*cb)( int sts, int uid, void *aux ), void *aux )
{
imap_store_t *ctx = (imap_store_t *)gctx;
- struct imap_cmd_cb cb;
- int ret;
-
- memset( &cb, 0, sizeof(cb) );
- cb.ctx = uid;
- cb.uid = -1; /* we're looking for a UID */
- *uid = -1; /* in case we get no SEARCH response at all */
- if ((ret = imap_exec_m( ctx, &cb, "UID SEARCH HEADER X-TUID %." stringify(TUIDL) "s", tuid )) != DRV_OK)
- return ret;
- return *uid < 0 ? DRV_MSG_BAD : DRV_OK;
+ struct imap_cmd_cb cbd;
+ int ret, uid;
+
+ memset( &cbd, 0, sizeof(cbd) );
+ cbd.uid = -1; /* we're looking for a UID */
+ cbd.ctx = &uid;
+ uid = -1; /* in case we get no SEARCH response at all */
+ if ((ret = imap_exec_m( ctx, &cbd, "UID SEARCH HEADER X-TUID %." stringify(TUIDL) "s", tuid )) != DRV_OK)
+ cb( ret, -1, aux );
+ else
+ cb( uid <= 0 ? DRV_MSG_BAD : DRV_OK, uid, aux );
}
-static int
-imap_list( store_t *gctx )
+static void
+imap_list( store_t *gctx,
+ void (*cb)( int sts, void *aux ), void *aux )
{
imap_store_t *ctx = (imap_store_t *)gctx;
int ret;
if ((ret = imap_exec_b( ctx, 0, "LIST \"\" \"%s%%\"", ctx->prefix )) == DRV_OK)
gctx->listed = 1;
- return ret;
+ cb( ret, aux );
}
-static int
-imap_check( store_t *gctx )
+static void
+imap_cancel( store_t *gctx,
+ void (*cb)( int sts, void *aux ), void *aux )
+{
+ (void)gctx;
+ cb( DRV_OK, aux );
+}
+
+static void
+imap_commit( store_t *gctx )
{
- (void) gctx;
- /* flush queue here */
- return DRV_OK;
+ (void)gctx;
}
imap_server_conf_t *servers, **serverapp = &servers;
@@ -1797,6 +1819,7 @@ struct driver imap_driver = {
imap_find_msg,
imap_set_flags,
imap_trash_msg,
- imap_check,
- imap_close
+ imap_close,
+ imap_cancel,
+ imap_commit,
};
diff --git a/src/drv_maildir.c b/src/drv_maildir.c
@@ -93,20 +93,22 @@ maildir_parse_flags( const char *base )
return flags;
}
-static store_t *
-maildir_open_store( store_conf_t *conf )
+static void
+maildir_open_store( store_conf_t *conf,
+ void (*cb)( store_t *ctx, void *aux ), void *aux )
{
maildir_store_t *ctx;
struct stat st;
if (stat( conf->path, &st ) || !S_ISDIR(st.st_mode)) {
error( "Maildir error: cannot open store %s\n", conf->path );
- return 0;
+ cb( 0, aux );
+ return;
}
ctx = nfcalloc( sizeof(*ctx) );
ctx->gen.conf = conf;
ctx->uvfd = -1;
- return &ctx->gen;
+ cb( &ctx->gen, aux );
}
static void
@@ -159,15 +161,17 @@ maildir_cleanup_drv( void )
{
}
-static int
-maildir_list( store_t *gctx )
+static void
+maildir_list( store_t *gctx,
+ void (*cb)( int sts, void *aux ), void *aux )
{
DIR *dir;
struct dirent *de;
if (!(dir = opendir( gctx->conf->path ))) {
error( "%s: %s\n", gctx->conf->path, strerror(errno) );
- return DRV_STORE_BAD;
+ cb( DRV_STORE_BAD, aux );
+ return;
}
while ((de = readdir( dir ))) {
struct stat st;
@@ -183,7 +187,7 @@ maildir_list( store_t *gctx )
closedir (dir);
gctx->listed = 1;
- return DRV_OK;
+ cb( DRV_OK, aux );
}
static const char *subdirs[] = { "cur", "new", "tmp" };
@@ -760,8 +764,9 @@ maildir_prepare_opts( store_t *gctx, int opts )
gctx->opts = opts;
}
-static int
-maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs )
+static void
+maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs,
+ void (*cb)( int sts, void *aux ), void *aux )
{
maildir_store_t *ctx = (maildir_store_t *)gctx;
message_t **msgapp;
@@ -777,14 +782,17 @@ maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs )
ctx->excs = nfrealloc( excs, nexcs * sizeof(int) );
ctx->nexcs = nexcs;
- if (maildir_validate( gctx->path, "", ctx->gen.opts & OPEN_CREATE ) != DRV_OK)
- return DRV_BOX_BAD;
+ if (maildir_validate( gctx->path, "", ctx->gen.opts & OPEN_CREATE ) != DRV_OK) {
+ cb( DRV_BOX_BAD, aux );
+ return;
+ }
nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", gctx->path );
#ifndef USE_DB
if ((ctx->uvfd = open( uvpath, O_RDWR|O_CREAT, 0600 )) < 0) {
perror( uvpath );
- return DRV_BOX_BAD;
+ cb( DRV_BOX_BAD, aux );
+ return;
}
#else
if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) {
@@ -799,7 +807,8 @@ maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs )
goto fnok;
}
perror( uvpath );
- return DRV_BOX_BAD;
+ cb( DRV_BOX_BAD, aux );
+ return;
}
dbok:
#if SEEK_SET != 0
@@ -811,7 +820,8 @@ maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs )
bork:
close( ctx->uvfd );
ctx->uvfd = -1;
- return DRV_BOX_BAD;
+ cb( DRV_BOX_BAD, aux );
+ return;
}
if (db_create( &ctx->db, 0, 0 )) {
fputs( "Maildir error: db_create() failed\n", stderr );
@@ -841,14 +851,16 @@ maildir_select( store_t *gctx, int minuid, int maxuid, int *excs, int nexcs )
fnok:
#endif /* USE_DB */
- if (maildir_scan( ctx, &msglist ) != DRV_OK)
- return DRV_BOX_BAD;
+ if (maildir_scan( ctx, &msglist ) != DRV_OK) {
+ cb( DRV_BOX_BAD, aux );
+ return;
+ }
msgapp = &ctx->gen.msgs;
for (i = 0; i < msglist.nents; i++)
maildir_app_msg( ctx, &msgapp, msglist.ents + i );
maildir_free_scan( &msglist );
- return DRV_OK;
+ cb( DRV_OK, aux );
}
static int
@@ -916,8 +928,9 @@ maildir_again( maildir_store_t *ctx, maildir_message_t *msg, const char *fn )
return (msg->gen.status & M_DEAD) ? DRV_MSG_BAD : DRV_OK;
}
-static int
-maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data )
+static void
+maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data,
+ void (*cb)( int sts, void *aux ), void *aux )
{
maildir_store_t *ctx = (maildir_store_t *)gctx;
maildir_message_t *msg = (maildir_message_t *)gmsg;
@@ -929,8 +942,10 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data )
nfsnprintf( buf, sizeof(buf), "%s/%s/%s", gctx->path, subdirs[gmsg->status & M_RECENT], msg->base );
if ((fd = open( buf, O_RDONLY )) >= 0)
break;
- if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK)
- return ret;
+ if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) {
+ cb( ret, aux );
+ return;
+ }
}
fstat( fd, &st );
data->len = st.st_size;
@@ -938,12 +953,13 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data )
if (read( fd, data->data, data->len ) != data->len) {
perror( buf );
close( fd );
- return DRV_MSG_BAD;
+ cb( DRV_MSG_BAD, aux );
+ return;
}
close( fd );
if (!(gmsg->status & M_FLAGS))
data->flags = maildir_parse_flags( msg->base );
- return DRV_OK;
+ cb( DRV_OK, aux );
}
static int
@@ -961,30 +977,34 @@ maildir_make_flags( int flags, char *buf )
return d;
}
-static int
-maildir_store_msg( store_t *gctx, msg_data_t *data, int *uid )
+static void
+maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
+ void (*cb)( int sts, int uid, void *aux ), void *aux )
{
maildir_store_t *ctx = (maildir_store_t *)gctx;
const char *prefix, *box;
- int ret, fd, bl;
+ int ret, fd, bl, uid;
char buf[_POSIX_PATH_MAX], nbuf[_POSIX_PATH_MAX], fbuf[NUM_FLAGS + 3], base[128];
bl = nfsnprintf( base, sizeof(base), "%ld.%d_%d.%s", time( 0 ), Pid, ++MaildirCount, Hostname );
- if (uid) {
+ if (!to_trash) {
#ifdef USE_DB
if (ctx->db) {
- if ((ret = maildir_set_uid( ctx, base, uid )) != DRV_OK) {
+ if ((ret = maildir_set_uid( ctx, base, &uid )) != DRV_OK) {
free( data->data );
- return ret;
+ cb( ret, 0, aux );
+ return;
}
} else
#endif /* USE_DB */
{
if ((ret = maildir_uidval_lock( ctx )) != DRV_OK ||
- (ret = maildir_obtain_uid( ctx, uid )) != DRV_OK)
- return ret;
+ (ret = maildir_obtain_uid( ctx, &uid )) != DRV_OK) {
+ cb( ret, 0, aux );
+ return;
+ }
maildir_uidval_unlock( ctx );
- nfsnprintf( base + bl, sizeof(base) - bl, ",U=%d", *uid );
+ nfsnprintf( base + bl, sizeof(base) - bl, ",U=%d", uid );
}
prefix = gctx->path;
box = "";
@@ -999,16 +1019,19 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int *uid )
if (errno != ENOENT) {
perror( buf );
free( data->data );
- return DRV_BOX_BAD;
+ cb( DRV_BOX_BAD, 0, aux );
+ return;
}
if ((ret = maildir_validate( gctx->conf->path, gctx->conf->trash, gctx->opts & OPEN_CREATE )) != DRV_OK) {
free( data->data );
- return ret;
+ cb( ret, 0, aux );
+ return;
}
if ((fd = open( buf, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) {
perror( buf );
free( data->data );
- return DRV_BOX_BAD;
+ cb( DRV_BOX_BAD, 0, aux );
+ return;
}
}
ret = write( fd, data->data, data->len );
@@ -1019,35 +1042,37 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int *uid )
else
error( "Maildir error: %s: partial write\n", buf );
close( fd );
- return DRV_BOX_BAD;
+ cb( DRV_BOX_BAD, 0, aux );
+ return;
}
close( fd );
nfsnprintf( nbuf, sizeof(nbuf), "%s%s/%s/%s%s", prefix, box, subdirs[!(data->flags & F_SEEN)], base, fbuf );
if (rename( buf, nbuf )) {
perror( nbuf );
- return DRV_BOX_BAD;
+ cb( DRV_BOX_BAD, 0, aux );
+ return;
}
- if (uid)
- gctx->count++;
- return DRV_OK;
+ cb( DRV_OK, uid, aux );
}
-static int
-maildir_find_msg( store_t *gctx, const char *tuid, int *uid )
+static void
+maildir_find_msg( store_t *gctx, const char *tuid,
+ void (*cb)( int sts, int uid, void *aux ), void *aux )
{
message_t *msg;
/* using a hash table might turn out to be more appropriate ... */
for (msg = gctx->msgs; msg; msg = msg->next)
if (!(msg->status & M_DEAD) && !memcmp( ((maildir_message_t *)msg)->tuid, tuid, TUIDL )) {
- *uid = msg->uid;
- return DRV_OK;
+ cb( DRV_OK, msg->uid, aux );
+ return;
}
- return DRV_MSG_BAD;
+ cb( DRV_MSG_BAD, -1, aux );
}
-static int
-maildir_set_flags( store_t *gctx, message_t *gmsg, int uid, int add, int del )
+static void
+maildir_set_flags( store_t *gctx, message_t *gmsg, int uid, int add, int del,
+ void (*cb)( int sts, void *aux ), void *aux )
{
maildir_store_t *ctx = (maildir_store_t *)gctx;
maildir_message_t *msg = (maildir_message_t *)gmsg;
@@ -1089,8 +1114,10 @@ maildir_set_flags( store_t *gctx, message_t *gmsg, int uid, int add, int del )
}
if (!rename( buf, nbuf ))
break;
- if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK)
- return ret;
+ if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) {
+ cb( ret, aux );
+ return;
+ }
}
free( msg->base );
msg->base = nfmalloc( tl + 1 );
@@ -1099,7 +1126,7 @@ maildir_set_flags( store_t *gctx, message_t *gmsg, int uid, int add, int del )
msg->gen.flags &= ~del;
gmsg->status &= ~M_RECENT;
- return DRV_OK;
+ cb( DRV_OK, aux );
}
#ifdef USE_DB
@@ -1119,8 +1146,9 @@ maildir_purge_msg( maildir_store_t *ctx, const char *name )
}
#endif /* USE_DB */
-static int
-maildir_trash_msg( store_t *gctx, message_t *gmsg )
+static void
+maildir_trash_msg( store_t *gctx, message_t *gmsg,
+ void (*cb)( int sts, void *aux ), void *aux )
{
maildir_store_t *ctx = (maildir_store_t *)gctx;
maildir_message_t *msg = (maildir_message_t *)gmsg;
@@ -1137,30 +1165,38 @@ maildir_trash_msg( store_t *gctx, message_t *gmsg )
if (!rename( buf, nbuf ))
break;
if (!stat( buf, &st )) {
- if ((ret = maildir_validate( gctx->conf->path, gctx->conf->trash, 1 )) != DRV_OK)
- return ret;
+ if ((ret = maildir_validate( gctx->conf->path, gctx->conf->trash, 1 )) != DRV_OK) {
+ cb( ret, aux );
+ return;
+ }
if (!rename( buf, nbuf ))
break;
if (errno != ENOENT) {
perror( nbuf );
- return DRV_BOX_BAD;
+ cb( DRV_BOX_BAD, aux );
+ return;
}
}
- if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK)
- return ret;
+ if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) {
+ cb( ret, aux );
+ return;
+ }
}
gmsg->status |= M_DEAD;
gctx->count--;
#ifdef USE_DB
- if (ctx->db)
- return maildir_purge_msg( ctx, msg->base );
+ if (ctx->db) {
+ cb( maildir_purge_msg( ctx, msg->base ), aux );
+ return;
+ }
#endif /* USE_DB */
- return DRV_OK;
+ cb( DRV_OK, aux );
}
-static int
-maildir_close( store_t *gctx )
+static void
+maildir_close( store_t *gctx,
+ void (*cb)( int sts, void *aux ), void *aux )
{
#ifdef USE_DB
maildir_store_t *ctx = (maildir_store_t *)gctx;
@@ -1184,23 +1220,36 @@ maildir_close( store_t *gctx )
msg->status |= M_DEAD;
gctx->count--;
#ifdef USE_DB
- if (ctx->db && (ret = maildir_purge_msg( ctx, ((maildir_message_t *)msg)->base )) != DRV_OK)
- return ret;
+ if (ctx->db && (ret = maildir_purge_msg( ctx, ((maildir_message_t *)msg)->base )) != DRV_OK) {
+ cb( ret, aux );
+ return;
+ }
#endif /* USE_DB */
}
}
- if (!retry)
- return DRV_OK;
- if ((ret = maildir_rescan( (maildir_store_t *)gctx )) != DRV_OK)
- return ret;
+ if (!retry) {
+ cb( DRV_OK, aux );
+ return;
+ }
+ if ((ret = maildir_rescan( (maildir_store_t *)gctx )) != DRV_OK) {
+ cb( ret, aux );
+ return;
+ }
}
}
-static int
-maildir_check( store_t *gctx )
+static void
+maildir_cancel( store_t *gctx,
+ void (*cb)( int sts, void *aux ), void *aux )
+{
+ (void)gctx;
+ cb( DRV_OK, aux );
+}
+
+static void
+maildir_commit( store_t *gctx )
{
(void) gctx;
- return DRV_OK;
}
static int
@@ -1248,6 +1297,7 @@ struct driver maildir_driver = {
maildir_find_msg,
maildir_set_flags,
maildir_trash_msg,
- maildir_check,
- maildir_close
+ maildir_close,
+ maildir_cancel,
+ maildir_commit,
};
diff --git a/src/isync.h b/src/isync.h
@@ -166,9 +166,13 @@ typedef struct {
} msg_data_t;
#define DRV_OK 0
-#define DRV_MSG_BAD -1
-#define DRV_BOX_BAD -2
-#define DRV_STORE_BAD -3
+#define DRV_MSG_BAD 1
+#define DRV_BOX_BAD 2
+#define DRV_STORE_BAD 3
+#define DRV_SERVER_BAD 4
+#define DRV_CANCELED 5
+
+/* All memory belongs to the driver's user. */
#define DRV_CRLF 1
@@ -178,21 +182,32 @@ struct driver {
int flags;
int (*parse_store)( conffile_t *cfg, store_conf_t **storep, int *err );
void (*cleanup)( void );
- store_t *(*open_store)( store_conf_t *conf );
+ void (*open_store)( store_conf_t *conf,
+ void (*cb)( store_t *ctx, void *aux ), void *aux );
void (*disown_store)( store_t *ctx );
store_t *(*own_store)( store_conf_t *conf );
void (*cancel_store)( store_t *ctx );
- int (*list)( store_t *ctx );
+ void (*list)( store_t *ctx,
+ void (*cb)( int sts, void *aux ), void *aux );
void (*prepare_paths)( store_t *ctx );
void (*prepare_opts)( store_t *ctx, int opts );
- int (*select)( store_t *ctx, int minuid, int maxuid, int *excs, int nexcs );
- int (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data );
- int (*store_msg)( store_t *ctx, msg_data_t *data, int *uid ); /* if uid is null, store to trash */
- int (*find_msg)( store_t *ctx, const char *tuid, int *uid );
- int (*set_flags)( store_t *ctx, message_t *msg, int uid, int add, int del ); /* msg can be null, therefore uid as a fallback */
- int (*trash_msg)( store_t *ctx, message_t *msg ); /* This may expunge the original message immediately, but it needn't to */
- int (*check)( store_t *ctx ); /* IMAP-style: flush */
- int (*close)( store_t *ctx ); /* IMAP-style: expunge inclusive */
+ void (*select)( store_t *ctx, int minuid, int maxuid, int *excs, int nexcs,
+ void (*cb)( int sts, void *aux ), void *aux );
+ void (*fetch_msg)( store_t *ctx, message_t *msg, msg_data_t *data,
+ void (*cb)( int sts, void *aux ), void *aux );
+ void (*store_msg)( store_t *ctx, msg_data_t *data, int to_trash,
+ void (*cb)( int sts, int uid, void *aux ), void *aux );
+ void (*find_msg)( store_t *ctx, const char *tuid,
+ void (*cb)( int sts, int uid, void *aux ), void *aux );
+ void (*set_flags)( store_t *ctx, message_t *msg, int uid, int add, int del, /* msg can be null, therefore uid as a fallback */
+ void (*cb)( int sts, void *aux ), void *aux );
+ 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 );
+ void (*close)( store_t *ctx, /* IMAP-style: expunge inclusive */
+ void (*cb)( int sts, void *aux ), void *aux );
+ void (*cancel)( store_t *ctx, /* only not yet sent commands */
+ void (*cb)( int sts, void *aux ), void *aux );
+ void (*commit)( store_t *ctx );
};
@@ -217,7 +232,6 @@ void debug( const char *, ... );
void debugn( const char *, ... );
void info( const char *, ... );
void infon( const char *, ... );
-void infoc( char );
void warn( const char *, ... );
void error( const char *, ... );
@@ -248,12 +262,15 @@ unsigned char arc4_getbyte( void );
extern const char *str_ms[2], *str_hl[2];
-#define SYNC_OK 0
-#define SYNC_FAIL 1
-#define SYNC_BAD(ms) (2+(ms))
-#define SYNC_NOGOOD 4 /* internal */
+#define SYNC_OK 0 /* assumed to be 0 */
+#define SYNC_FAIL 1
+#define SYNC_BAD(ms) (2<<(ms))
+#define SYNC_NOGOOD 8 /* internal */
+#define SYNC_CANCELED 16 /* internal */
-int sync_boxes( store_t *ctx[], const char *names[], channel_conf_t * );
+/* All passed pointers must stay alive until cb is called. */
+void sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan,
+ void (*cb)( int sts, void *aux ), void *aux );
/* config.c */
diff --git a/src/main.c b/src/main.c
@@ -24,6 +24,7 @@
#include "isync.h"
#include <stdlib.h>
+#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
@@ -198,7 +199,16 @@ typedef struct {
unsigned done:1, skip:1, cben:1;
} main_vars_t;
-static void sync_chans( main_vars_t *mvars );
+#define AUX &mvars->t[t]
+#define MVARS(aux) \
+ int t = *(int *)aux; \
+ main_vars_t *mvars = (main_vars_t *)(((char *)(&((int *)aux)[-t])) - offsetof(main_vars_t, t));
+
+#define E_START 0
+#define E_OPEN 1
+#define E_SYNC 2
+
+static void sync_chans( main_vars_t *mvars, int ent );
int
main( int argc, char **argv )
@@ -460,19 +470,36 @@ main( int argc, char **argv )
break;
}
mvars->argv = argv;
- sync_chans( mvars );
+ mvars->cben = 1;
+ sync_chans( mvars, E_START );
return mvars->ret;
}
+#define ST_FRESH 0
+#define ST_OPEN 1
+#define ST_CLOSED 2
+
+static void store_opened( store_t *ctx, void *aux );
+static void store_listed( int sts, void *aux );
+static void done_sync_dyn( int sts, void *aux );
+static void done_sync( int sts, void *aux );
+
static void
-sync_chans( main_vars_t *mvars )
+sync_chans( main_vars_t *mvars, int ent )
{
group_conf_t *group;
channel_conf_t *chan;
+ store_t *store;
string_list_t *mbox, *sbox, **mboxp, **sboxp;
char *channame;
int t;
+ if (!mvars->cben)
+ return;
+ switch (ent) {
+ case E_OPEN: goto opened;
+ case E_SYNC: goto syncone;
+ }
for (;;) {
mvars->boxlist = 0;
if (!mvars->all) {
@@ -503,36 +530,32 @@ sync_chans( main_vars_t *mvars )
merge_actions( mvars->chan, mvars->ops, XOP_HAVE_CREATE, OP_CREATE, 0 );
merge_actions( mvars->chan, mvars->ops, XOP_HAVE_EXPUNGE, OP_EXPUNGE, 0 );
+ mvars->state[M] = mvars->state[S] = ST_FRESH;
info( "Channel %s\n", mvars->chan->name );
mvars->boxes[M] = mvars->boxes[S] = mvars->cboxes = 0;
+ mvars->skip = mvars->cben = 0;
for (t = 0; t < 2; t++) {
mvars->drv[t] = mvars->chan->stores[t]->driver;
- mvars->ctx[t] = mvars->drv[t]->own_store( mvars->chan->stores[t] );
+ if ((store = mvars->drv[t]->own_store( mvars->chan->stores[t] )))
+ store_opened( store, AUX );
}
- for (t = 0; t < 2; t++)
- if (!mvars->ctx[t]) {
+ for (t = 0; t < 2 && !mvars->skip; t++)
+ if (mvars->state[t] == ST_FRESH) {
info( "Opening %s %s...\n", str_ms[t], mvars->chan->stores[t]->name );
- if (!(mvars->ctx[t] = mvars->drv[t]->open_store( mvars->chan->stores[t] ))) {
- mvars->ret = 1;
- goto next;
- }
+ mvars->drv[t]->open_store( mvars->chan->stores[t], store_opened, AUX );
}
+ mvars->cben = 1;
+ opened:
+ if (mvars->skip)
+ goto next;
+ if (mvars->state[M] != ST_OPEN || mvars->state[S] != ST_OPEN)
+ return;
+
if (mvars->boxlist)
mvars->boxp = mvars->boxlist;
else if (mvars->chan->patterns) {
- for (t = 0; t < 2; t++) {
- if (!mvars->ctx[t]->listed) {
- if (mvars->drv[t]->list( mvars->ctx[t] ) != DRV_OK) {
- screwt:
- mvars->drv[t]->cancel_store( mvars->ctx[t] );
- mvars->ctx[t] = 0;
- mvars->ret = 1;
- goto next;
- } else if (mvars->ctx[t]->conf->map_inbox)
- add_string_list( &mvars->ctx[t]->boxes, mvars->ctx[t]->conf->map_inbox );
- }
- mvars->boxes[t] = filter_boxes( mvars->ctx[t]->boxes, mvars->chan->patterns );
- }
+ mvars->boxes[M] = filter_boxes( mvars->ctx[M]->boxes, mvars->chan->patterns );
+ mvars->boxes[S] = filter_boxes( mvars->ctx[S]->boxes, mvars->chan->patterns );
for (mboxp = &mvars->boxes[M]; (mbox = *mboxp); ) {
for (sboxp = &mvars->boxes[S]; (sbox = *sboxp); sboxp = &sbox->next)
if (!strcmp( sbox->string, mbox->string )) {
@@ -550,60 +573,70 @@ sync_chans( main_vars_t *mvars )
if (mvars->list && mvars->multiple)
printf( "%s:\n", mvars->chan->name );
+ syncml:
+ mvars->done = mvars->cben = 0;
+ syncmlx:
if (mvars->boxlist) {
- while ((mvars->names[S] = strsep( &mvars->boxp, ",\n" ))) {
- if (mvars->list)
- puts( mvars->names[S] );
- else {
+ if ((mvars->names[S] = strsep( &mvars->boxp, ",\n" ))) {
+ if (!mvars->list) {
mvars->names[M] = mvars->names[S];
- switch (sync_boxes( mvars->ctx, mvars->names, mvars->chan )) {
- case SYNC_BAD(M): t = M; goto screwt;
- case SYNC_BAD(S): t = S; goto screwt;
- case SYNC_FAIL: mvars->ret = 1;
- }
+ sync_boxes( mvars->ctx, mvars->names, mvars->chan, done_sync, mvars );
+ goto syncw;
}
+ puts( mvars->names[S] );
+ goto syncmlx;
}
} else if (mvars->chan->patterns) {
- for (mbox = mvars->cboxes; mbox; mbox = mbox->next)
- if (mvars->list)
- puts( mbox->string );
- else {
+ if ((mbox = mvars->cboxes)) {
+ mvars->cboxes = mbox->next;
+ if (!mvars->list) {
mvars->names[M] = mvars->names[S] = mbox->string;
- switch (sync_boxes( mvars->ctx, mvars->names, mvars->chan )) {
- case SYNC_BAD(M): t = M; goto screwt;
- case SYNC_BAD(S): t = S; goto screwt;
- case SYNC_FAIL: mvars->ret = 1;
- }
+ sync_boxes( mvars->ctx, mvars->names, mvars->chan, done_sync_dyn, mvars );
+ goto syncw;
}
+ puts( mbox->string );
+ free( mbox );
+ goto syncmlx;
+ }
for (t = 0; t < 2; t++)
- if ((mvars->chan->ops[1-t] & OP_MASK_TYPE) && (mvars->chan->ops[1-t] & OP_CREATE)) {
- for (mbox = mvars->boxes[t]; mbox; mbox = mbox->next)
- if (mvars->list)
- puts( mbox->string );
- else {
+ if ((mbox = mvars->boxes[t])) {
+ mvars->boxes[t] = mbox->next;
+ if ((mvars->chan->ops[1-t] & OP_MASK_TYPE) && (mvars->chan->ops[1-t] & OP_CREATE)) {
+ if (!mvars->list) {
mvars->names[M] = mvars->names[S] = mbox->string;
- switch (sync_boxes( mvars->ctx, mvars->names, mvars->chan )) {
- case SYNC_BAD(M): t = M; goto screwt;
- case SYNC_BAD(S): t = S; goto screwt;
- case SYNC_FAIL: mvars->ret = 1;
- }
+ sync_boxes( mvars->ctx, mvars->names, mvars->chan, done_sync_dyn, mvars );
+ goto syncw;
}
+ puts( mbox->string );
+ }
+ free( mbox );
+ goto syncmlx;
}
- } else
- if (mvars->list)
+ } else {
+ if (!mvars->list) {
+ sync_boxes( mvars->ctx, mvars->chan->boxes, mvars->chan, done_sync, mvars );
+ mvars->skip = 1;
+ syncw:
+ mvars->cben = 1;
+ if (!mvars->done)
+ return;
+ syncone:
+ if (!mvars->skip)
+ goto syncml;
+ } else
printf( "%s <=> %s\n", mvars->chan->boxes[M], mvars->chan->boxes[S] );
- else
- switch (sync_boxes( mvars->ctx, mvars->chan->boxes, mvars->chan )) {
- case SYNC_BAD(M): t = M; goto screwt;
- case SYNC_BAD(S): t = S; goto screwt;
- case SYNC_FAIL: mvars->ret = 1;
- }
+ }
next:
- if (mvars->ctx[M])
- mvars->drv[M]->disown_store( mvars->ctx[M] );
- if (mvars->ctx[S])
- mvars->drv[S]->disown_store( mvars->ctx[S] );
+ for (t = 0; t < 2; t++)
+ if (mvars->state[t] == ST_OPEN) {
+ mvars->drv[t]->disown_store( mvars->ctx[t] );
+ mvars->state[t] = ST_CLOSED;
+ }
+ if (mvars->state[M] != ST_CLOSED || mvars->state[S] != ST_CLOSED) {
+ mvars->skip = mvars->cben = 1;
+ return;
+ }
free_string_list( mvars->cboxes );
free_string_list( mvars->boxes[M] );
free_string_list( mvars->boxes[S] );
@@ -621,3 +654,76 @@ sync_chans( main_vars_t *mvars )
for (t = 0; t < N_DRIVERS; t++)
drivers[t]->cleanup();
}
+
+static void
+store_opened( store_t *ctx, void *aux )
+{
+ MVARS(aux)
+
+ if (!ctx) {
+ mvars->state[t] = ST_CLOSED;
+ mvars->ret = mvars->skip = 1;
+ return;
+ }
+ mvars->ctx[t] = ctx;
+ if (mvars->skip) {
+ mvars->state[t] = ST_OPEN;
+ sync_chans( mvars, E_OPEN );
+ return;
+ }
+ if (!mvars->boxlist && mvars->chan->patterns && !ctx->listed)
+ mvars->drv[t]->list( ctx, store_listed, AUX );
+ else {
+ mvars->state[t] = ST_OPEN;
+ sync_chans( mvars, E_OPEN );
+ }
+}
+
+static void
+store_listed( int sts, void *aux )
+{
+ MVARS(aux)
+
+ mvars->state[t] = ST_OPEN;
+ switch (sts) {
+ case DRV_OK:
+ if (mvars->ctx[t]->conf->map_inbox)
+ add_string_list( &mvars->ctx[t]->boxes, mvars->ctx[t]->conf->map_inbox );
+ break;
+ case DRV_STORE_BAD:
+ mvars->drv[t]->cancel_store( mvars->ctx[t] );
+ mvars->state[t] = ST_CLOSED;
+ default:
+ mvars->ret = mvars->skip = 1;
+ break;
+ }
+ sync_chans( mvars, E_OPEN );
+}
+
+static void
+done_sync_dyn( int sts, void *aux )
+{
+ main_vars_t *mvars = (main_vars_t *)aux;
+
+ free( ((char *)mvars->names[S]) - offsetof(string_list_t, string) );
+ done_sync( sts, aux );
+}
+
+static void
+done_sync( int sts, void *aux )
+{
+ main_vars_t *mvars = (main_vars_t *)aux;
+
+ mvars->done = 1;
+ if (sts) {
+ mvars->ret = 1;
+ if (sts & (SYNC_BAD(M) | SYNC_BAD(S))) {
+ mvars->skip = 1;
+ if (sts & SYNC_BAD(M))
+ mvars->state[M] = ST_CLOSED;
+ if (sts & SYNC_BAD(S))
+ mvars->state[S] = ST_CLOSED;
+ }
+ }
+ sync_chans( mvars, E_SYNC );
+}
diff --git a/src/sync.c b/src/sync.c
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
+#include <stddef.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
@@ -87,6 +88,7 @@ make_flags( int flags, char *buf )
return d;
}
+
#define S_DEAD (1<<0)
#define S_DONE (1<<1)
#define S_DEL(ms) (1<<(2+(ms)))
@@ -106,225 +108,6 @@ typedef struct sync_rec {
char tuid[TUIDL];
} sync_rec_t;
-static int
-select_box( sync_rec_t *srecs, store_t *ctx[], int maxuid[], int uidval[], int t, int minwuid, int *mexcs, int nmexcs, FILE *jfp )
-{
- sync_rec_t *srec, *nsrec = 0;
- message_t *msg;
- const char *diag;
- int uid, maxwuid;
- char fbuf[16]; /* enlarge when support for keywords is added */
-
- if (ctx[t]->opts & OPEN_NEW) {
- if (minwuid > maxuid[t] + 1)
- minwuid = maxuid[t] + 1;
- maxwuid = INT_MAX;
- } else if (ctx[t]->opts & OPEN_OLD) {
- maxwuid = 0;
- for (srec = srecs; srec; srec = srec->next)
- if (!(srec->status & S_DEAD) && srec->uid[t] > maxwuid)
- maxwuid = srec->uid[t];
- } else
- maxwuid = 0;
- infon( "Selecting %s %s... ", str_ms[t], ctx[t]->name );
- debug( maxwuid == INT_MAX ? "selecting %s [%d,inf]\n" : "selecting %s [%d,%d]\n", str_ms[t], minwuid, maxwuid );
- switch (ctx[t]->conf->driver->select( ctx[t], minwuid, maxwuid, mexcs, nmexcs )) {
- case DRV_STORE_BAD: return SYNC_BAD(t);
- case DRV_BOX_BAD: return SYNC_FAIL;
- }
- if (uidval[t] && uidval[t] != ctx[t]->uidvalidity) {
- error( "Error: UIDVALIDITY of %s changed (got %d, expected %d)\n", str_ms[t], ctx[t]->uidvalidity, uidval[t] );
- return SYNC_FAIL;
- }
- info( "%d messages, %d recent\n", ctx[M]->count, ctx[M]->recent );
-
- if (jfp) {
- /*
- * Alternatively, the TUIDs could be fetched into the messages and
- * looked up here. This would make the search faster (probably) and
- * save roundtrips. On the downside, quite some additional data would
- * have to be fetched for every message and the IMAP driver would be
- * more complicated. This is a corner case anyway, so why bother.
- */
- debug( "finding previously copied messages\n" );
- for (srec = srecs; srec; srec = srec->next) {
- if (srec->status & S_DEAD)
- continue;
- if (srec->uid[t] == -2 && srec->tuid[0]) {
- debug( " pair(%d,%d): lookup %s, TUID %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], str_ms[t], srec->tuid );
- switch (ctx[t]->conf->driver->find_msg( ctx[t], srec->tuid, &uid )) {
- case DRV_STORE_BAD: return SYNC_BAD(t);
- case DRV_OK:
- debug( " -> new UID %d\n", uid );
- Fprintf( jfp, "%c %d %d %d\n", "<>"[t], srec->uid[M], srec->uid[S], uid );
- srec->uid[t] = uid;
- srec->tuid[0] = 0;
- break;
- default:
- debug( " -> TUID lost\n" );
- Fprintf( jfp, "& %d %d\n", srec->uid[M], srec->uid[S] );
- srec->flags = 0;
- srec->tuid[0] = 0;
- break;
- }
- }
- }
- }
-
- /*
- * Mapping msg -> srec (this variant) is dog slow for new messages.
- * Mapping srec -> msg is dog slow for deleted messages.
- * One solution would be using binary search on an index array.
- * msgs are already sorted by UID, srecs would have to be sorted by uid[t].
- */
- debug( "matching messages against sync records\n" );
- for (msg = ctx[t]->msgs; msg; msg = msg->next) {
- uid = msg->uid;
- if (DFlags & DEBUG) {
- make_flags( msg->flags, fbuf );
- printf( ctx[t]->opts & OPEN_SIZE ? " message %5d, %-4s, %6d: " : " message %5d, %-4s: ", uid, fbuf, msg->size );
- }
- for (srec = nsrec; srec; srec = srec->next) {
- if (srec->status & S_DEAD)
- continue;
- if (srec->uid[t] == uid) {
- diag = srec == nsrec ? "adjacently" : "after gap";
- goto found;
- }
- }
- for (srec = srecs; srec != nsrec; srec = srec->next) {
- if (srec->status & S_DEAD)
- continue;
- if (srec->uid[t] == uid) {
- diag = "after reset";
- goto found;
- }
- }
- msg->srec = 0;
- debug( "new\n" );
- continue;
- found:
- msg->srec = srec;
- srec->msg[t] = msg;
- nsrec = srec->next;
- debug( "pairs %5d %s\n", srec->uid[1-t], diag );
- }
-
- return SYNC_OK;
-}
-
-static int
-copy_msg( store_t *ctx[], int t, message_t *tmsg, const char *tuid, int *uid )
-{
- msg_data_t msgdata;
- char *fmap, *buf;
- int i, len, extra, cra, crd, scr, tcr;
- int start, sbreak = 0, ebreak = 0;
- char c;
-
- msgdata.flags = tmsg->flags;
- switch (ctx[1-t]->conf->driver->fetch_msg( ctx[1-t], tmsg, &msgdata )) {
- case DRV_STORE_BAD: return SYNC_BAD(1-t);
- case DRV_BOX_BAD: return SYNC_FAIL;
- case DRV_MSG_BAD: return SYNC_NOGOOD;
- }
- tmsg->flags = msgdata.flags;
-
- scr = (ctx[1-t]->conf->driver->flags / DRV_CRLF) & 1;
- tcr = (ctx[t]->conf->driver->flags / DRV_CRLF) & 1;
- if (tuid || scr != tcr) {
- fmap = msgdata.data;
- len = msgdata.len;
- cra = crd = 0;
- if (scr > tcr)
- crd = -1;
- else if (scr < tcr)
- cra = 1;
- extra = 0, i = 0;
- if (tuid) {
- extra += 8 + TUIDL + 1 + tcr;
- nloop:
- start = i;
- while (i < len) {
- c = fmap[i++];
- if (c == '\r')
- extra += crd;
- else if (c == '\n') {
- extra += cra;
- if (i - 1 - scr == start) {
- sbreak = ebreak = i - 1 - scr;
- goto oke;
- }
- if (!memcmp( fmap + start, "X-TUID: ", 8 )) {
- extra -= (ebreak = i) - (sbreak = start);
- goto oke;
- }
- goto nloop;
- }
- }
- /* invalid message */
- free( fmap );
- return SYNC_NOGOOD;
- }
- oke:
- if (cra || crd)
- for (; i < len; i++) {
- c = fmap[i];
- if (c == '\r')
- extra += crd;
- else if (c == '\n')
- extra += cra;
- }
-
- msgdata.len = len + extra;
- buf = msgdata.data = nfmalloc( msgdata.len );
- i = 0;
- if (tuid) {
- if (cra) {
- for (; i < sbreak; i++) {
- if (fmap[i] == '\n')
- *buf++ = '\r';
- *buf++ = fmap[i];
- }
- } else if (crd) {
- for (; i < sbreak; i++)
- if (fmap[i] != '\r')
- *buf++ = fmap[i];
- } else {
- memcpy( buf, fmap, sbreak );
- buf += sbreak;
- }
- memcpy( buf, "X-TUID: ", 8 );
- buf += 8;
- memcpy( buf, tuid, TUIDL );
- buf += TUIDL;
- if (tcr)
- *buf++ = '\r';
- *buf++ = '\n';
- i = ebreak;
- }
- if (cra) {
- for (; i < len; i++) {
- if (fmap[i] == '\n')
- *buf++ = '\r';
- *buf++ = fmap[i];
- }
- } else if (crd) {
- for (; i < len; i++)
- if (fmap[i] != '\r')
- *buf++ = fmap[i];
- } else
- memcpy( buf, fmap + i, len - i );
-
- free( fmap );
- }
-
- switch (ctx[t]->conf->driver->store_msg( ctx[t], &msgdata, uid )) {
- case DRV_STORE_BAD: return SYNC_BAD(t);
- case DRV_OK: return SYNC_OK;
- default: return SYNC_FAIL;
- }
-}
/* cases:
a) both non-null
@@ -354,6 +137,8 @@ copy_msg( store_t *ctx[], int t, message_t *tmsg, const char *tuid, int *uid )
*/
typedef struct {
+ int t[2];
+ void (*cb)( int sts, void *aux ), *aux;
char *dname, *jname, *nname, *lname;
FILE *jfp, *nfp;
sync_rec_t *srecs, **srecadd, **osrecadd;
@@ -361,11 +146,300 @@ typedef struct {
store_t *ctx[2];
driver_t *drv[2];
int state[2], ret;
+ int find_old_total[2], find_old_done[2];
+ int new_total[2], new_done[2];
+ int find_new_total[2], find_new_done[2];
+ int flags_total[2], flags_done[2];
+ int trash_total[2], trash_done[2];
int maxuid[2], uidval[2], smaxxuid, lfd;
+ unsigned find:1, cancel:1;
} sync_vars_t;
+#define AUX &svars->t[t]
+#define SVARS(aux) \
+ int t = *(int *)aux; \
+ sync_vars_t *svars = (sync_vars_t *)(((char *)(&((int *)aux)[-t])) - offsetof(sync_vars_t, t));
+
+/* operation dependencies:
+ select(S): -
+ find_old(S): select(S)
+ select(M): find_old(S) | -
+ find_old(M): select(M)
+ new(M), new(S), flags(M): find_old(M) & find_old(S)
+ flags(S): count(new(S))
+ find_new(x): new(x)
+ trash(x): flags(x)
+ close(x): trash(x) & find_new(x) // with expunge
+ cleanup: close(M) & close(S)
+*/
+
+#define ST_SENT_FIND_OLD (1<<0)
+#define ST_SENT_NEW (1<<1)
+#define ST_SENT_FIND_NEW (1<<2)
+#define ST_SENT_FLAGS (1<<3)
+#define ST_SENT_TRASH (1<<4)
+#define ST_CLOSED (1<<5)
+#define ST_CANCELED (1<<6)
+
#define ST_DID_EXPUNGE (1<<16)
+
+typedef struct copy_vars {
+ void (*cb)( int sts, int uid, struct copy_vars *vars );
+ void *aux;
+ sync_rec_t *srec; /* also ->tuid */
+ message_t *msg;
+ msg_data_t data;
+} copy_vars_t;
+
+static void msg_fetched( int sts, void *aux );
+
+static void
+copy_msg( copy_vars_t *vars )
+{
+ SVARS(vars->aux)
+
+ vars->data.flags = vars->msg->flags;
+ svars->drv[1-t]->fetch_msg( svars->ctx[1-t], vars->msg, &vars->data, msg_fetched, vars );
+}
+
+static void msg_stored( int sts, int uid, void *aux );
+
+static void
+msg_fetched( int sts, void *aux )
+{
+ copy_vars_t *vars = (copy_vars_t *)aux;
+ SVARS(vars->aux)
+ char *fmap, *buf;
+ int i, len, extra, cra, crd, scr, tcr;
+ int start, sbreak = 0, ebreak = 0;
+ char c;
+
+ switch (sts) {
+ case DRV_OK:
+ vars->msg->flags = vars->data.flags;
+
+ scr = (svars->drv[1-t]->flags / DRV_CRLF) & 1;
+ tcr = (svars->drv[t]->flags / DRV_CRLF) & 1;
+ if (vars->srec || scr != tcr) {
+ fmap = vars->data.data;
+ len = vars->data.len;
+ cra = crd = 0;
+ if (scr > tcr)
+ crd = -1;
+ else if (scr < tcr)
+ cra = 1;
+ extra = 0, i = 0;
+ if (vars->srec) {
+ extra += 8 + TUIDL + 1 + tcr;
+ nloop:
+ start = i;
+ while (i < len) {
+ c = fmap[i++];
+ if (c == '\r')
+ extra += crd;
+ else if (c == '\n') {
+ extra += cra;
+ if (i - 1 - scr == start) {
+ sbreak = ebreak = i - 1 - scr;
+ goto oke;
+ }
+ if (!memcmp( fmap + start, "X-TUID: ", 8 )) {
+ extra -= (ebreak = i) - (sbreak = start);
+ goto oke;
+ }
+ goto nloop;
+ }
+ }
+ /* invalid message */
+ free( fmap );
+ vars->cb( SYNC_NOGOOD, 0, vars );
+ break;
+ }
+ oke:
+ if (cra || crd)
+ for (; i < len; i++) {
+ c = fmap[i];
+ if (c == '\r')
+ extra += crd;
+ else if (c == '\n')
+ extra += cra;
+ }
+
+ vars->data.len = len + extra;
+ buf = vars->data.data = nfmalloc( vars->data.len );
+ i = 0;
+ if (vars->srec) {
+ if (cra) {
+ for (; i < sbreak; i++) {
+ if (fmap[i] == '\n')
+ *buf++ = '\r';
+ *buf++ = fmap[i];
+ }
+ } else if (crd) {
+ for (; i < sbreak; i++)
+ if (fmap[i] != '\r')
+ *buf++ = fmap[i];
+ } else {
+ memcpy( buf, fmap, sbreak );
+ buf += sbreak;
+ }
+ memcpy( buf, "X-TUID: ", 8 );
+ buf += 8;
+ memcpy( buf, vars->srec->tuid, TUIDL );
+ buf += TUIDL;
+ if (tcr)
+ *buf++ = '\r';
+ *buf++ = '\n';
+ i = ebreak;
+ }
+ if (cra) {
+ for (; i < len; i++) {
+ if (fmap[i] == '\n')
+ *buf++ = '\r';
+ *buf++ = fmap[i];
+ }
+ } else if (crd) {
+ for (; i < len; i++)
+ if (fmap[i] != '\r')
+ *buf++ = fmap[i];
+ } else
+ memcpy( buf, fmap + i, len - i );
+
+ free( fmap );
+ }
+
+ svars->drv[t]->store_msg( svars->ctx[t], &vars->data, !vars->srec, msg_stored, vars );
+ break;
+ case DRV_CANCELED:
+ vars->cb( SYNC_CANCELED, 0, vars );
+ break;
+ case DRV_MSG_BAD:
+ vars->cb( SYNC_NOGOOD, 0, vars );
+ break;
+ case DRV_STORE_BAD:
+ vars->cb( SYNC_BAD(1-t), 0, vars );
+ break;
+ default:
+ vars->cb( SYNC_FAIL, 0, vars );
+ break;
+ }
+}
+
+static void
+msg_stored( int sts, int uid, void *aux )
+{
+ copy_vars_t *vars = (copy_vars_t *)aux;
+ SVARS(vars->aux)
+
+ (void)svars;
+ switch (sts) {
+ case DRV_OK:
+ vars->cb( SYNC_OK, uid, vars );
+ break;
+ case DRV_CANCELED:
+ vars->cb( SYNC_CANCELED, 0, vars );
+ break;
+ case DRV_STORE_BAD:
+ vars->cb( SYNC_BAD(t), 0, vars );
+ break;
+ default:
+ vars->cb( SYNC_FAIL, 0, vars );
+ break;
+ }
+}
+
+
+static void
+stats( sync_vars_t *svars )
+{
+ char buf[2][64];
+ char *cs;
+ int t, l;
+ static int cols = -1;
+
+ if (cols < 0 && (!(cs = getenv( "COLUMNS" )) || !(cols = atoi( cs ) / 2)))
+ cols = 36;
+ if (!(DFlags & QUIET)) {
+ for (t = 0; t < 2; t++) {
+ l = sprintf( buf[t], "?%d/%d +%d/%d *%d/%d #%d/%d",
+ svars->find_old_done[t] + svars->find_new_done[t],
+ svars->find_old_total[t] + svars->find_new_total[t],
+ svars->new_done[t], svars->new_total[t],
+ svars->flags_done[t], svars->flags_total[t],
+ svars->trash_done[t], svars->trash_total[t] );
+ if (l > cols)
+ buf[t][cols - 1] = '~';
+ }
+ infon( "\rM: %.*s S: %.*s", cols, buf[0], cols, buf[1] );
+ }
+}
+
+
+static void sync_bail( sync_vars_t *svars );
+static void sync_bail1( sync_vars_t *svars );
+static void sync_bail2( sync_vars_t *svars );
+static void cancel_done( int sts, void *aux );
+
+static void
+cancel_sync( sync_vars_t *svars )
+{
+ int t;
+
+ svars->cancel = 1;
+ for (t = 0; t < 2; t++)
+ if (svars->ret & SYNC_BAD(t))
+ cancel_done( DRV_STORE_BAD, AUX );
+ else
+ svars->drv[t]->cancel( svars->ctx[t], cancel_done, AUX );
+}
+
+static void
+cancel_done( int sts, void *aux )
+{
+ SVARS(aux)
+
+ if (sts != DRV_OK) {
+ svars->ret |= SYNC_BAD(t);
+ svars->drv[t]->cancel_store( svars->ctx[t] );
+ }
+ svars->state[t] |= ST_CANCELED;
+ if (svars->state[1-t] & ST_CANCELED) {
+ Fclose( svars->nfp );
+ Fclose( svars->jfp );
+ sync_bail( svars );
+ }
+}
+
+
+static int
+check_ret( int sts, sync_vars_t *svars, int t )
+{
+ switch (sts) {
+ case DRV_CANCELED:
+ return 1;
+ case DRV_STORE_BAD:
+ svars->ret |= SYNC_BAD(t);
+ cancel_sync( svars );
+ return 1;
+ case DRV_BOX_BAD:
+ svars->ret |= SYNC_FAIL;
+ cancel_sync( svars );
+ return 1;
+ }
+ return 0;
+}
+
+static int
+check_ret_aux( int sts, sync_vars_t *svars, int t, void *aux )
+{
+ if (!check_ret( sts, svars, t ))
+ return 0;
+ free( aux );
+ return 1;
+}
+
+
static char *
clean_strdup( const char *s )
{
@@ -379,26 +453,29 @@ clean_strdup( const char *s )
return cs;
}
+
#define JOURNAL_VERSION "2"
-int
-sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
+static void select_box( sync_vars_t *svars, int t, int minwuid, int *mexcs, int nmexcs );
+
+void
+sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan,
+ void (*cb)( int sts, void *aux ), void *aux )
{
- sync_vars_t svars[1];
- message_t *tmsg;
+ sync_vars_t *svars;
sync_rec_t *srec, *nsrec;
char *s, *cmname, *csname;
FILE *jfp;
- int no[2], del[2], nex, minwuid, uid, nmsgs;
- int todel, *mexcs, nmexcs, rmexcs;
int opts[2], line, t1, t2, t3, t;
- unsigned char nflags, sflags, aflags, dflags;
struct stat st;
struct flock lck;
char fbuf[16]; /* enlarge when support for keywords is added */
char buf[64];
- memset( svars, 0, sizeof(svars[0]) );
+ svars = nfcalloc( sizeof(*svars) );
+ svars->t[1] = 1;
+ svars->cb = cb;
+ svars->aux = aux;
svars->ctx[0] = ctx[0];
svars->ctx[1] = ctx[1];
svars->chan = chan;
@@ -416,7 +493,8 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
if (!strcmp( chan->sync_state ? chan->sync_state : global_sync_state, "*" )) {
if (!ctx[S]->path) {
error( "Error: store '%s' does not support in-box sync state\n", chan->stores[S]->name );
- return SYNC_BAD(S);
+ cb( SYNC_BAD(S), aux );
+ return;
}
nfasprintf( &svars->dname, "%s/." EXE "state", ctx[S]->path );
} else {
@@ -434,13 +512,15 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
if (!(s = strrchr( svars->dname, '/' ))) {
error( "Error: invalid SyncState '%s'\n", svars->dname );
free( svars->dname );
- return SYNC_BAD(S);
+ cb( SYNC_BAD(S), aux );
+ return;
}
*s = 0;
if (mkdir( svars->dname, 0700 ) && errno != EEXIST) {
error( "Error: cannot create SyncState directory '%s': %s\n", svars->dname, strerror(errno) );
free( svars->dname );
- return SYNC_BAD(S);
+ cb( SYNC_BAD(S), aux );
+ return;
}
*s = '/';
nfasprintf( &svars->jname, "%s.journal", svars->dname );
@@ -456,13 +536,15 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
if ((svars->lfd = open( svars->lname, O_WRONLY|O_CREAT, 0666 )) < 0) {
error( "Error: cannot create lock file %s: %s\n", svars->lname, strerror(errno) );
svars->ret = SYNC_FAIL;
- goto bail2;
+ sync_bail2( svars );
+ return;
}
if (fcntl( svars->lfd, F_SETLK, &lck )) {
error( "Error: channel :%s:%s-:%s:%s is locked\n",
chan->stores[M]->name, ctx[M]->name, chan->stores[S]->name, ctx[S]->name );
svars->ret = SYNC_FAIL;
- goto bail1;
+ sync_bail1( svars );
+ return;
}
if ((jfp = fopen( svars->dname, "r" ))) {
debug( "reading sync state %s ...\n", svars->dname );
@@ -470,13 +552,15 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
error( "Error: incomplete sync state header in %s\n", svars->dname );
fclose( jfp );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
if (sscanf( buf, "%d:%d %d:%d:%d", &svars->uidval[M], &svars->maxuid[M], &svars->uidval[S], &svars->smaxxuid, &svars->maxuid[S]) != 5) {
error( "Error: invalid sync state header in %s\n", svars->dname );
fclose( jfp );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
line = 1;
while (fgets( buf, sizeof(buf), jfp )) {
@@ -485,14 +569,16 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
error( "Error: incomplete sync state entry at %s:%d\n", svars->dname, line );
fclose( jfp );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
fbuf[0] = 0;
if (sscanf( buf, "%d %d %15s", &t1, &t2, fbuf ) < 2) {
error( "Error: invalid sync state entry at %s:%d\n", svars->dname, line );
fclose( jfp );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
srec = nfmalloc( sizeof(*srec) );
srec->uid[M] = t1;
@@ -516,7 +602,8 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
if (errno != ENOENT) {
error( "Error: cannot read sync state %s\n", svars->dname );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
}
line = 0;
@@ -527,14 +614,16 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
error( "Error: incomplete journal header in %s\n", svars->jname );
fclose( jfp );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
if (memcmp( buf, JOURNAL_VERSION "\n", strlen(JOURNAL_VERSION) + 1 )) {
error( "Error: incompatible journal version "
"(got %.*s, expected " JOURNAL_VERSION ")\n", t - 1, buf );
fclose( jfp );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
srec = 0;
line = 1;
@@ -544,7 +633,8 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
error( "Error: incomplete journal entry at %s:%d\n", svars->jname, line );
fclose( jfp );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
if (buf[0] == '#' ?
(t3 = 0, (sscanf( buf + 2, "%d %d %n", &t1, &t2, &t3 ) < 2) || !t3 || (t - t3 != TUIDL + 3)) :
@@ -557,7 +647,8 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
error( "Error: malformed journal entry at %s:%d\n", svars->jname, line );
fclose( jfp );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
if (buf[0] == '(')
svars->maxuid[M] = t1;
@@ -588,7 +679,8 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
error( "Error: journal entry at %s:%d refers to non-existing sync state entry\n", svars->jname, line );
fclose( jfp );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
syncfnd:
debugn( " entry(%d,%d,%u) ", srec->uid[M], srec->uid[S], srec->flags );
switch (buf[0]) {
@@ -648,7 +740,8 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
error( "Error: unrecognized journal entry at %s:%d\n", svars->jname, line );
fclose( jfp );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
}
}
@@ -658,19 +751,22 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
if (errno != ENOENT) {
error( "Error: cannot read journal %s\n", svars->jname );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
}
if (!(svars->nfp = fopen( svars->nname, "w" ))) {
error( "Error: cannot write new sync state %s\n", svars->nname );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
if (!(svars->jfp = fopen( svars->jname, "a" ))) {
error( "Error: cannot write journal %s\n", svars->jname );
fclose( svars->nfp );
svars->ret = SYNC_FAIL;
- goto bail;
+ sync_bail( svars );
+ return;
}
setlinebuf( svars->jfp );
if (!line)
@@ -725,14 +821,192 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
svars->drv[M]->prepare_opts( ctx[M], opts[M] );
svars->drv[S]->prepare_opts( ctx[S], opts[S] );
- if ((svars->ret = select_box( svars->srecs, svars->ctx, svars->maxuid, svars->uidval, S, (ctx[S]->opts & OPEN_OLD) ? 1 : INT_MAX, 0, 0, line ? svars->jfp : 0 )) != SYNC_OK)
- goto finish;
+ svars->find = line != 0;
+ if (!svars->smaxxuid)
+ select_box( svars, M, (ctx[M]->opts & OPEN_OLD) ? 1 : INT_MAX, 0, 0 );
+ select_box( svars, S, (ctx[S]->opts & OPEN_OLD) ? 1 : INT_MAX, 0, 0 );
+}
+
+static void box_selected( int sts, void *aux );
+
+static void
+select_box( sync_vars_t *svars, int t, int minwuid, int *mexcs, int nmexcs )
+{
+ sync_rec_t *srec;
+ int maxwuid;
+
+ if (svars->ctx[t]->opts & OPEN_NEW) {
+ if (minwuid > svars->maxuid[t] + 1)
+ minwuid = svars->maxuid[t] + 1;
+ maxwuid = INT_MAX;
+ } else if (svars->ctx[t]->opts & OPEN_OLD) {
+ maxwuid = 0;
+ for (srec = svars->srecs; srec; srec = srec->next)
+ if (!(srec->status & S_DEAD) && srec->uid[t] > maxwuid)
+ maxwuid = srec->uid[t];
+ } else
+ maxwuid = 0;
+ info( "Selecting %s %s...\n", str_ms[t], svars->ctx[t]->name );
+ debug( maxwuid == INT_MAX ? "selecting %s [%d,inf]\n" : "selecting %s [%d,%d]\n", str_ms[t], minwuid, maxwuid );
+ svars->drv[t]->select( svars->ctx[t], minwuid, maxwuid, mexcs, nmexcs, box_selected, AUX );
+}
+
+typedef struct {
+ void *aux;
+ sync_rec_t *srec;
+} find_vars_t;
+
+static void msg_found_sel( int sts, int uid, void *aux );
+static void msgs_found_sel( sync_vars_t *svars, int t );
+
+static void
+box_selected( int sts, void *aux )
+{
+ SVARS(aux)
+ find_vars_t *fv;
+ sync_rec_t *srec;
+
+ if (check_ret( sts, svars, t ))
+ return;
+ if (svars->uidval[t] && svars->uidval[t] != svars->ctx[t]->uidvalidity) {
+ error( "Error: UIDVALIDITY of %s changed (got %d, expected %d)\n",
+ str_ms[t], svars->ctx[t]->uidvalidity, svars->uidval[t] );
+ svars->ret |= SYNC_FAIL;
+ cancel_sync( svars );
+ return;
+ }
+ info( "%s: %d messages, %d recent\n", str_ms[t], svars->ctx[t]->count, svars->ctx[t]->recent );
- mexcs = 0;
- nmexcs = rmexcs = 0;
- minwuid = INT_MAX;
- if (svars->smaxxuid) {
+ if (svars->find) {
+ /*
+ * Alternatively, the TUIDs could be fetched into the messages and
+ * looked up here. This would make the search faster (probably) and
+ * save roundtrips. On the downside, quite some additional data would
+ * have to be fetched for every message and the IMAP driver would be
+ * more complicated. This is a corner case anyway, so why bother.
+ */
+ debug( "finding previously copied messages\n" );
+ for (srec = svars->srecs; srec; srec = srec->next) {
+ if (srec->status & S_DEAD)
+ continue;
+ if (srec->uid[t] == -2 && srec->tuid[0]) {
+ debug( " pair(%d,%d): lookup %s, TUID %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], str_ms[t], srec->tuid );
+ svars->find_old_total[t]++;
+ stats( svars );
+ fv = nfmalloc( sizeof(*fv) );
+ fv->aux = AUX;
+ fv->srec = srec;
+ svars->drv[t]->find_msg( svars->ctx[t], srec->tuid, msg_found_sel, fv );
+ if (svars->cancel)
+ return;
+ }
+ }
+ }
+ svars->state[t] |= ST_SENT_FIND_OLD;
+ msgs_found_sel( svars, t );
+}
+
+static void
+msg_found_sel( int sts, int uid, void *aux )
+{
+ find_vars_t *vars = (find_vars_t *)aux;
+ SVARS(vars->aux)
+
+ if (check_ret_aux( sts, svars, t, vars ))
+ return;
+ switch (sts) {
+ case DRV_OK:
+ debug( " -> new UID %d\n", uid );
+ Fprintf( svars->jfp, "%c %d %d %d\n", "<>"[t], vars->srec->uid[M], vars->srec->uid[S], uid );
+ vars->srec->uid[t] = uid;
+ vars->srec->tuid[0] = 0;
+ break;
+ default:
+ debug( " -> TUID lost\n" );
+ Fprintf( svars->jfp, "& %d %d\n", vars->srec->uid[M], vars->srec->uid[S] );
+ vars->srec->flags = 0;
+ vars->srec->tuid[0] = 0;
+ break;
+ }
+ free( vars );
+ svars->find_old_done[t]++;
+ stats( svars );
+ msgs_found_sel( svars, t );
+}
+
+typedef struct {
+ void *aux;
+ sync_rec_t *srec;
+ int aflags, dflags;
+} flag_vars_t;
+
+static void flags_set_del( int sts, void *aux );
+static void flags_set_sync( int sts, void *aux );
+static void flags_set_sync_p2( sync_vars_t *svars, sync_rec_t *srec, int t );
+static void msgs_flags_set( sync_vars_t *svars, int t );
+static void msg_copied( int sts, int uid, copy_vars_t *vars );
+static void msg_copied_p2( sync_vars_t *svars, sync_rec_t *srec, int t, message_t *tmsg, int uid );
+static void msgs_copied( sync_vars_t *svars, int t );
+
+static void
+msgs_found_sel( sync_vars_t *svars, int t )
+{
+ sync_rec_t *srec, *nsrec = 0;
+ message_t *tmsg;
+ copy_vars_t *cv;
+ flag_vars_t *fv;
+ const char *diag;
+ int uid, minwuid, *mexcs, nmexcs, rmexcs, no[2], del[2], todel, nmsgs, t1, t2;
+ int sflags, nflags, aflags, dflags, nex;
+ char fbuf[16]; /* enlarge when support for keywords is added */
+
+ if (!(svars->state[t] & ST_SENT_FIND_OLD) || svars->find_old_done[t] < svars->find_new_total[t])
+ return;
+
+ /*
+ * Mapping tmsg -> srec (this variant) is dog slow for new messages.
+ * Mapping srec -> tmsg is dog slow for deleted messages.
+ * One solution would be using binary search on an index array.
+ * msgs are already sorted by UID, srecs would have to be sorted by uid[t].
+ */
+ debug( "matching messages against sync records\n" );
+ for (tmsg = svars->ctx[t]->msgs; tmsg; tmsg = tmsg->next) {
+ uid = tmsg->uid;
+ if (DFlags & DEBUG) {
+ make_flags( tmsg->flags, fbuf );
+ printf( svars->ctx[t]->opts & OPEN_SIZE ? " message %5d, %-4s, %6d: " : " message %5d, %-4s: ", uid, fbuf, tmsg->size );
+ }
+ for (srec = nsrec; srec; srec = srec->next) {
+ if (srec->status & S_DEAD)
+ continue;
+ if (srec->uid[t] == uid) {
+ diag = srec == nsrec ? "adjacently" : "after gap";
+ goto found;
+ }
+ }
+ for (srec = svars->srecs; srec != nsrec; srec = srec->next) {
+ if (srec->status & S_DEAD)
+ continue;
+ if (srec->uid[t] == uid) {
+ diag = "after reset";
+ goto found;
+ }
+ }
+ tmsg->srec = 0;
+ debug( "new\n" );
+ continue;
+ found:
+ tmsg->srec = srec;
+ srec->msg[t] = tmsg;
+ nsrec = srec->next;
+ debug( "pairs %5d %s\n", srec->uid[1-t], diag );
+ }
+
+ if ((t == S) && svars->smaxxuid) {
debug( "preparing master selection - max expired slave uid is %d\n", svars->smaxxuid );
+ mexcs = 0;
+ nmexcs = rmexcs = 0;
+ minwuid = INT_MAX;
for (srec = svars->srecs; srec; srec = srec->next) {
if (srec->status & S_DEAD)
continue;
@@ -783,10 +1057,12 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
for (t = 0; t < nmexcs; t++)
debugn( " %d", mexcs[t] );
debug( "\n" );
- } else if (ctx[M]->opts & OPEN_OLD)
- minwuid = 1;
- if ((svars->ret = select_box( svars->srecs, svars->ctx, svars->maxuid, svars->uidval, M, minwuid, mexcs, nmexcs, line ? svars->jfp : 0 )) != SYNC_OK)
- goto finish;
+ select_box( svars, M, minwuid, mexcs, nmexcs );
+ return;
+ }
+
+ if (!(svars->state[1-t] & ST_SENT_FIND_OLD) || svars->find_old_done[1-t] < svars->find_new_total[1-t])
+ return;
if (!svars->uidval[M] || !svars->uidval[S]) {
svars->uidval[M] = svars->ctx[M]->uidvalidity;
@@ -823,11 +1099,6 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
debug( " -> pair(%d,%d) created\n", srec->uid[M], srec->uid[S] );
}
if ((tmsg->flags & F_FLAGGED) || !svars->chan->stores[t]->max_size || tmsg->size <= svars->chan->stores[t]->max_size) {
- if (!nmsgs)
- infon( t ? "Pulling new messages..." : "Pushing new messages..." );
- else
- infoc( '.' );
- nmsgs++;
if (tmsg->flags) {
srec->flags = tmsg->flags;
Fprintf( svars->jfp, "* %d %d %u\n", srec->uid[M], srec->uid[S], srec->flags );
@@ -837,65 +1108,30 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
t2 = arc4_getbyte() & 0x3f;
srec->tuid[t1] = t2 < 26 ? t2 + 'A' : t2 < 52 ? t2 + 'a' - 26 : t2 < 62 ? t2 + '0' - 52 : t2 == 62 ? '+' : '/';
}
+ svars->new_total[t]++;
+ stats( svars );
+ cv = nfmalloc( sizeof(*cv) );
+ cv->cb = msg_copied;
+ cv->aux = AUX;
+ cv->srec = srec;
+ cv->msg = tmsg;
Fprintf( svars->jfp, "# %d %d %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], srec->tuid );
debug( " -> %sing message, TUID %." stringify(TUIDL) "s\n", str_hl[t], srec->tuid );
- switch ((svars->ret = copy_msg( svars->ctx, t, tmsg, srec->tuid, &uid ))) {
- case SYNC_OK: break;
- case SYNC_NOGOOD:
- /* The error is either transient or the message is gone. */
- debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] );
- srec->status = S_DEAD;
- Fprintf( svars->jfp, "- %d %d\n", srec->uid[M], srec->uid[S] );
- continue;
- default: goto finish;
- }
+ copy_msg( cv );
+ if (svars->cancel)
+ return;
} else {
if (tmsg->srec) {
debug( " -> not %sing - still too big\n", str_hl[t] );
continue;
}
debug( " -> not %sing - too big\n", str_hl[t] );
- uid = -1;
- }
- if (srec->uid[t] != uid) {
- debug( " -> new UID %d\n", uid );
- Fprintf( svars->jfp, "%c %d %d %d\n", "<>"[t], srec->uid[M], srec->uid[S], uid );
- srec->uid[t] = uid;
- srec->tuid[0] = 0;
- }
- if (!tmsg->srec) {
- tmsg->srec = srec;
- if (svars->maxuid[1-t] < tmsg->uid) {
- svars->maxuid[1-t] = tmsg->uid;
- Fprintf( svars->jfp, "%c %d\n", ")("[t], tmsg->uid );
- }
+ msg_copied_p2( svars, srec, t, tmsg, -1 );
}
}
}
- if (nmsgs)
- info( " %d messages\n", nmsgs );
- }
- debug( "finding just copied messages\n" );
- for (srec = svars->srecs; srec; srec = srec->next) {
- if (srec->status & S_DEAD)
- continue;
- if (srec->tuid[0]) {
- t = (srec->uid[M] == -2) ? M : S;
- debug( " pair(%d,%d): lookup %s, TUID %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], str_ms[t], srec->tuid );
- switch (svars->drv[t]->find_msg( svars->ctx[t], srec->tuid, &uid )) {
- case DRV_STORE_BAD: svars->ret = SYNC_BAD(t); goto finish;
- case DRV_OK:
- debug( " -> new UID %d\n", uid );
- break;
- default:
- warn( "Warning: cannot find newly stored message %." stringify(TUIDL) "s on %s.\n", srec->tuid, str_ms[t] );
- uid = 0;
- break;
- }
- Fprintf( svars->jfp, "%c %d %d %d\n", "<>"[t], srec->uid[M], srec->uid[S], uid );
- srec->uid[t] = uid;
- srec->tuid[0] = 0;
- }
+ svars->state[t] |= ST_SENT_NEW;
+ msgs_copied( svars, t );
}
debug( "synchronizing old entries\n" );
@@ -928,15 +1164,14 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
info( "Info: conflicting changes in (%d,%d)\n", srec->uid[M], srec->uid[S] );
if (svars->chan->ops[t] & OP_DELETE) {
debug( " %sing delete\n", str_hl[t] );
- switch (svars->drv[t]->set_flags( svars->ctx[t], srec->msg[t], srec->uid[t], F_DELETED, 0 )) {
- case DRV_STORE_BAD: svars->ret = SYNC_BAD(t); goto finish;
- case DRV_BOX_BAD: svars->ret = SYNC_FAIL; goto finish;
- default: /* ok */ break;
- case DRV_OK:
- srec->status |= S_DEL(t);
- Fprintf( svars->jfp, "%c %d %d 0\n", "><"[t], srec->uid[M], srec->uid[S] );
- srec->uid[1-t] = 0;
- }
+ svars->flags_total[t]++;
+ stats( svars );
+ fv = nfmalloc( sizeof(*fv) );
+ fv->aux = AUX;
+ fv->srec = srec;
+ svars->drv[t]->set_flags( svars->ctx[t], srec->msg[t], srec->uid[t], F_DELETED, 0, flags_set_del, fv );
+ if (svars->cancel)
+ return;
} else
debug( " not %sing delete\n", str_hl[t] );
} else if (!srec->msg[1-t])
@@ -969,7 +1204,7 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
if ((svars->chan->ops[S] & (OP_NEW|OP_RENEW|OP_FLAGS)) && svars->chan->max_messages) {
/* Flagged and not yet synced messages older than the first not
* expired message are not counted. */
- todel = svars->ctx[S]->count - svars->chan->max_messages;
+ todel = svars->ctx[S]->count + svars->new_total[S] - svars->chan->max_messages;
debug( "scheduling %d excess messages for expiration\n", todel );
for (tmsg = svars->ctx[S]->msgs; tmsg && todel > 0; tmsg = tmsg->next)
if (!(tmsg->status & M_DEAD) && (srec = tmsg->srec) &&
@@ -1038,83 +1273,329 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
aflags &= ~srec->msg[t]->flags;
dflags &= srec->msg[t]->flags;
}
- switch ((aflags | dflags) ? svars->drv[t]->set_flags( svars->ctx[t], srec->msg[t], srec->uid[t], aflags, dflags ) : DRV_OK) {
- case DRV_STORE_BAD: svars->ret = SYNC_BAD(t); goto finish;
- case DRV_BOX_BAD: svars->ret = SYNC_FAIL; goto finish;
- default: /* ok */ srec->aflags[t] = srec->dflags[t] = 0; break;
- case DRV_OK:
- if (aflags & F_DELETED)
- srec->status |= S_DEL(t);
- else if (dflags & F_DELETED)
- srec->status &= ~S_DEL(t);
- if (t) {
- nex = (srec->status / S_NEXPIRE) & 1;
- if (nex != ((srec->status / S_EXPIRED) & 1)) {
- if (nex && (svars->smaxxuid < srec->uid[S]))
- svars->smaxxuid = srec->uid[S];
- Fprintf( svars->jfp, "/ %d %d\n", srec->uid[M], srec->uid[S] );
- debug( " pair(%d,%d): expired %d (commit)\n", srec->uid[M], srec->uid[S], nex );
- srec->status = (srec->status & ~S_EXPIRED) | (nex * S_EXPIRED);
- } else if (nex != ((srec->status / S_EXPIRE) & 1)) {
- Fprintf( svars->jfp, "\\ %d %d\n", srec->uid[M], srec->uid[S] );
- debug( " pair(%d,%d): expire %d (cancel)\n", srec->uid[M], srec->uid[S], nex );
- srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE);
- }
- }
- }
+ if (aflags | dflags) {
+ svars->flags_total[t]++;
+ stats( svars );
+ fv = nfmalloc( sizeof(*fv) );
+ fv->aux = AUX;
+ fv->srec = srec;
+ fv->aflags = aflags;
+ fv->dflags = dflags;
+ svars->drv[t]->set_flags( svars->ctx[t], srec->msg[t], srec->uid[t], aflags, dflags, flags_set_sync, fv );
+ if (svars->cancel)
+ return;
+ } else
+ flags_set_sync_p2( svars, srec, t );
}
- nflags = (srec->flags | srec->aflags[M] | srec->aflags[S]) & ~(srec->dflags[M] | srec->dflags[S]);
- if (srec->flags != nflags) {
- debug( " pair(%d,%d): updating flags (%u -> %u)\n", srec->uid[M], srec->uid[S], srec->flags, nflags );
- srec->flags = nflags;
- Fprintf( svars->jfp, "* %d %d %u\n", srec->uid[M], srec->uid[S], nflags );
+ }
+ for (t = 0; t < 2; t++) {
+ svars->drv[t]->commit( svars->ctx[t] );
+ svars->state[t] |= ST_SENT_FLAGS;
+ msgs_flags_set( svars, t );
+ }
+}
+
+static void
+msg_copied( int sts, int uid, copy_vars_t *vars )
+{
+ SVARS(vars->aux)
+
+ switch (sts) {
+ case SYNC_OK:
+ msg_copied_p2( svars, vars->srec, t, vars->msg, uid );
+ break;
+ case SYNC_NOGOOD:
+ debug( " -> killing (%d,%d)\n", vars->srec->uid[M], vars->srec->uid[S] );
+ vars->srec->status = S_DEAD;
+ Fprintf( svars->jfp, "- %d %d\n", vars->srec->uid[M], vars->srec->uid[S] );
+ break;
+ default:
+ cancel_sync( svars );
+ case SYNC_CANCELED:
+ free( vars );
+ return;
+ }
+ free( vars );
+ svars->new_done[t]++;
+ stats( svars );
+ msgs_copied( svars, t );
+}
+
+static void
+msg_copied_p2( sync_vars_t *svars, sync_rec_t *srec, int t, message_t *tmsg, int uid )
+{
+ if (srec->uid[t] != uid) {
+ debug( " -> new UID %d\n", uid );
+ Fprintf( svars->jfp, "%c %d %d %d\n", "<>"[t], srec->uid[M], srec->uid[S], uid );
+ srec->uid[t] = uid;
+ srec->tuid[0] = 0;
+ }
+ if (!tmsg->srec) {
+ tmsg->srec = srec;
+ if (svars->maxuid[1-t] < tmsg->uid) {
+ svars->maxuid[1-t] = tmsg->uid;
+ Fprintf( svars->jfp, "%c %d\n", ")("[t], tmsg->uid );
}
}
+}
- for (t = 0; t < 2; t++) {
- if (svars->chan->ops[t] & OP_EXPUNGE) {
- if (svars->ctx[t]->conf->trash || (svars->ctx[1-t]->conf->trash && svars->ctx[1-t]->conf->trash_remote_new)) {
- debug( "trashing in %s\n", str_ms[t] );
- for (tmsg = svars->ctx[t]->msgs; tmsg; tmsg = tmsg->next)
- if (tmsg->flags & F_DELETED) {
- if (svars->ctx[t]->conf->trash) {
- if (!svars->ctx[t]->conf->trash_only_new || !tmsg->srec || tmsg->srec->uid[1-t] < 0) {
- debug( " trashing message %d\n", tmsg->uid );
- switch (svars->drv[t]->trash_msg( svars->ctx[t], tmsg )) {
- case DRV_OK: break;
- case DRV_STORE_BAD: svars->ret = SYNC_BAD(t); goto finish;
- default: svars->ret = SYNC_FAIL; goto nexex;
- }
- } else
- debug( " not trashing message %d - not new\n", tmsg->uid );
- } else {
- if (!tmsg->srec || tmsg->srec->uid[1-t] < 0) {
- if (!svars->ctx[1-t]->conf->max_size || tmsg->size <= svars->ctx[1-t]->conf->max_size) {
- debug( " remote trashing message %d\n", tmsg->uid );
- switch ((svars->ret = copy_msg( svars->ctx, 1 - t, tmsg, 0, 0 ))) {
- case SYNC_OK: break;
- case SYNC_NOGOOD: svars->ret = SYNC_FAIL; goto nexex;
- case SYNC_FAIL: goto nexex;
- default: goto finish;
- }
- } else
- debug( " not remote trashing message %d - too big\n", tmsg->uid );
- } else
- debug( " not remote trashing message %d - not new\n", tmsg->uid );
- }
- }
- }
+static void msg_found_new( int sts, int uid, void *aux );
+static void sync_close( sync_vars_t *svars, int t );
- info( "Expunging %s...\n", str_ms[t] );
- debug( "expunging %s\n", str_ms[t] );
- switch (svars->drv[t]->close( svars->ctx[t] )) {
- case DRV_OK: svars->state[t] |= ST_DID_EXPUNGE; break;
- case DRV_STORE_BAD: svars->ret = SYNC_BAD(t); goto finish;
- default: break;
- }
+static void
+msgs_copied( sync_vars_t *svars, int t )
+{
+ sync_rec_t *srec;
+ find_vars_t *fv;
+
+ if (!(svars->state[t] & ST_SENT_NEW) || svars->new_done[t] < svars->new_total[t])
+ return;
+
+ debug( "finding just copied messages on %s\n", str_ms[t] );
+ for (srec = svars->srecs; srec; srec = srec->next) {
+ if (srec->status & S_DEAD)
+ continue;
+ if (srec->tuid[0] && srec->uid[t] == -2) {
+ debug( " pair(%d,%d): lookup %s, TUID %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], str_ms[t], srec->tuid );
+ svars->find_new_total[t]++;
+ stats( svars );
+ fv = nfmalloc( sizeof(*fv) );
+ fv->aux = AUX;
+ fv->srec = srec;
+ svars->drv[t]->find_msg( svars->ctx[t], srec->tuid, msg_found_new, fv );
+ if (svars->cancel)
+ return;
}
- nexex: ;
}
+ svars->state[t] |= ST_SENT_FIND_NEW;
+ sync_close( svars, t );
+}
+
+static void
+msg_found_new( int sts, int uid, void *aux )
+{
+ find_vars_t *vars = (find_vars_t *)aux;
+ SVARS(vars->aux)
+
+ if (check_ret_aux( sts, svars, t, vars ))
+ return;
+ switch (sts) {
+ case DRV_OK:
+ debug( " -> new UID %d\n", uid );
+ break;
+ default:
+ warn( "Warning: cannot find newly stored message %." stringify(TUIDL) "s on %s.\n", vars->srec->tuid, str_ms[t] );
+ uid = 0;
+ break;
+ }
+ Fprintf( svars->jfp, "%c %d %d %d\n", "<>"[t], vars->srec->uid[M], vars->srec->uid[S], uid );
+ vars->srec->uid[t] = uid;
+ vars->srec->tuid[0] = 0;
+ free( vars );
+ svars->find_new_done[t]++;
+ stats( svars );
+ sync_close( svars, t );
+}
+
+static void
+flags_set_del( int sts, void *aux )
+{
+ flag_vars_t *vars = (flag_vars_t *)aux;
+ SVARS(vars->aux)
+
+ if (check_ret_aux( sts, svars, t, vars ))
+ return;
+ switch (sts) {
+ case DRV_OK:
+ vars->srec->status |= S_DEL(t);
+ Fprintf( svars->jfp, "%c %d %d 0\n", "><"[t], vars->srec->uid[M], vars->srec->uid[S] );
+ vars->srec->uid[1-t] = 0;
+ break;
+ }
+ free( vars );
+ svars->flags_done[t]++;
+ stats( svars );
+ msgs_flags_set( svars, t );
+}
+
+static void
+flags_set_sync( int sts, void *aux )
+{
+ flag_vars_t *vars = (flag_vars_t *)aux;
+ SVARS(vars->aux)
+
+ if (check_ret_aux( sts, svars, t, vars ))
+ return;
+ switch (sts) {
+ case DRV_OK:
+ if (vars->aflags & F_DELETED)
+ vars->srec->status |= S_DEL(t);
+ else if (vars->dflags & F_DELETED)
+ vars->srec->status &= ~S_DEL(t);
+ flags_set_sync_p2( svars, vars->srec, t );
+ break;
+ }
+ free( vars );
+ svars->flags_done[t]++;
+ stats( svars );
+ msgs_flags_set( svars, t );
+}
+
+static void
+flags_set_sync_p2( sync_vars_t *svars, sync_rec_t *srec, int t )
+{
+ int nflags, nex;
+
+ nflags = (srec->flags | srec->aflags[t]) & ~srec->dflags[t];
+ if (srec->flags != nflags) {
+ debug( " pair(%d,%d): updating flags (%u -> %u)\n", srec->uid[M], srec->uid[S], srec->flags, nflags );
+ srec->flags = nflags;
+ Fprintf( svars->jfp, "* %d %d %u\n", srec->uid[M], srec->uid[S], nflags );
+ }
+ if (t == S) {
+ nex = (srec->status / S_NEXPIRE) & 1;
+ if (nex != ((srec->status / S_EXPIRED) & 1)) {
+ if (nex && (svars->smaxxuid < srec->uid[S]))
+ svars->smaxxuid = srec->uid[S];
+ Fprintf( svars->jfp, "/ %d %d\n", srec->uid[M], srec->uid[S] );
+ debug( " pair(%d,%d): expired %d (commit)\n", srec->uid[M], srec->uid[S], nex );
+ srec->status = (srec->status & ~S_EXPIRED) | (nex * S_EXPIRED);
+ } else if (nex != ((srec->status / S_EXPIRE) & 1)) {
+ Fprintf( svars->jfp, "\\ %d %d\n", srec->uid[M], srec->uid[S] );
+ debug( " pair(%d,%d): expire %d (cancel)\n", srec->uid[M], srec->uid[S], nex );
+ srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE);
+ }
+ }
+}
+
+static void msg_trashed( int sts, void *aux );
+static void msg_rtrashed( int sts, int uid, copy_vars_t *vars );
+
+static void
+msgs_flags_set( sync_vars_t *svars, int t )
+{
+ message_t *tmsg;
+ copy_vars_t *cv;
+
+ if (!(svars->state[t] & ST_SENT_FLAGS) || svars->flags_done[t] < svars->flags_total[t])
+ return;
+
+ if ((svars->chan->ops[t] & OP_EXPUNGE) &&
+ (svars->ctx[t]->conf->trash || (svars->ctx[1-t]->conf->trash && svars->ctx[1-t]->conf->trash_remote_new))) {
+ debug( "trashing in %s\n", str_ms[t] );
+ for (tmsg = svars->ctx[t]->msgs; tmsg; tmsg = tmsg->next)
+ if (tmsg->flags & F_DELETED) {
+ if (svars->ctx[t]->conf->trash) {
+ if (!svars->ctx[t]->conf->trash_only_new || !tmsg->srec || tmsg->srec->uid[1-t] < 0) {
+ debug( "%s: trashing message %d\n", str_ms[t], tmsg->uid );
+ svars->trash_total[t]++;
+ stats( svars );
+ svars->drv[t]->trash_msg( svars->ctx[t], tmsg, msg_trashed, AUX );
+ if (svars->cancel)
+ return;
+ } else
+ debug( "%s: not trashing message %d - not new\n", str_ms[t], tmsg->uid );
+ } else {
+ if (!tmsg->srec || tmsg->srec->uid[1-t] < 0) {
+ if (!svars->ctx[1-t]->conf->max_size || tmsg->size <= svars->ctx[1-t]->conf->max_size) {
+ debug( "%s: remote trashing message %d\n", str_ms[t], tmsg->uid );
+ svars->trash_total[t]++;
+ stats( svars );
+ cv = nfmalloc( sizeof(*cv) );
+ cv->cb = msg_rtrashed;
+ cv->aux = AUX;
+ cv->srec = 0;
+ cv->msg = tmsg;
+ copy_msg( cv );
+ if (svars->cancel)
+ return;
+ } else
+ debug( "%s: not remote trashing message %d - too big\n", str_ms[t], tmsg->uid );
+ } else
+ debug( "%s: not remote trashing message %d - not new\n", str_ms[t], tmsg->uid );
+ }
+ }
+ }
+ svars->state[t] |= ST_SENT_TRASH;
+ sync_close( svars, t );
+}
+
+static void
+msg_trashed( int sts, void *aux )
+{
+ SVARS(aux)
+
+ if (sts == DRV_MSG_BAD)
+ sts = DRV_BOX_BAD;
+ if (check_ret( sts, svars, t ))
+ return;
+ svars->trash_done[t]++;
+ stats( svars );
+ sync_close( svars, t );
+}
+
+static void
+msg_rtrashed( int sts, int uid, copy_vars_t *vars )
+{
+ SVARS(vars->aux)
+
+ (void)uid;
+ switch (sts) {
+ case SYNC_OK:
+ case SYNC_NOGOOD: /* the message is gone or heavily busted */
+ break;
+ default:
+ cancel_sync( svars );
+ case SYNC_CANCELED:
+ free( vars );
+ return;
+ }
+ free( vars );
+ svars->trash_done[t]++;
+ stats( svars );
+ sync_close( svars, t );
+}
+
+static void box_closed( int sts, void *aux );
+static void box_closed_p2( sync_vars_t *svars, int t );
+
+static void
+sync_close( sync_vars_t *svars, int t )
+{
+ if ((~svars->state[t] & (ST_SENT_FIND_NEW|ST_SENT_TRASH)) ||
+ svars->find_new_done[t] < svars->find_new_total[t] ||
+ svars->trash_done[t] < svars->trash_total[t])
+ return;
+
+ if ((svars->chan->ops[t] & OP_EXPUNGE) /*&& !(svars->state[t] & ST_TRASH_BAD)*/) {
+ debug( "expunging %s\n", str_ms[t] );
+ svars->drv[t]->close( svars->ctx[t], box_closed, AUX );
+ } else
+ box_closed_p2( svars, t );
+}
+
+static void
+box_closed( int sts, void *aux )
+{
+ SVARS(aux)
+
+ if (check_ret( sts, svars, t ))
+ return;
+ svars->state[t] |= ST_DID_EXPUNGE;
+ box_closed_p2( svars, t );
+}
+
+static void
+box_closed_p2( sync_vars_t *svars, int t )
+{
+ sync_rec_t *srec;
+ int minwuid;
+ char fbuf[16]; /* enlarge when support for keywords is added */
+
+ svars->state[t] |= ST_CLOSED;
+ if (!(svars->state[1-t] & ST_CLOSED))
+ return;
+
if ((svars->state[M] | svars->state[S]) & ST_DID_EXPUNGE) {
/* This cleanup is not strictly necessary, as the next full sync
would throw out the dead entries anyway. But ... */
@@ -1172,24 +1653,42 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
unlink( svars->jname );
}
- bail:
+ sync_bail( svars );
+}
+
+static void
+sync_bail( sync_vars_t *svars )
+{
+ sync_rec_t *srec, *nsrec;
+
for (srec = svars->srecs; srec; srec = nsrec) {
nsrec = srec->next;
free( srec );
}
unlink( svars->lname );
- bail1:
+ sync_bail1( svars );
+}
+
+static void
+sync_bail1( sync_vars_t *svars )
+{
close( svars->lfd );
- bail2:
+ sync_bail2( svars );
+}
+
+static void
+sync_bail2( sync_vars_t *svars )
+{
+ void (*cb)( int sts, void *aux ) = svars->cb;
+ void *aux = svars->aux;
+ int ret = svars->ret;
+
free( svars->lname );
free( svars->nname );
free( svars->jname );
free( svars->dname );
- return svars->ret;
-
- finish:
- Fclose( svars->nfp );
- Fclose( svars->jfp );
- goto bail;
+ free( svars );
+ error( "" );
+ cb( ret, aux );
}
diff --git a/src/util.c b/src/util.c
@@ -43,6 +43,7 @@ debug( const char *msg, ... )
vprintf( msg, va );
va_end( va );
fflush( stdout );
+ need_nl = 0;
}
}
@@ -70,6 +71,7 @@ info( const char *msg, ... )
vprintf( msg, va );
va_end( va );
fflush( stdout );
+ need_nl = 0;
}
}
@@ -88,16 +90,6 @@ infon( const char *msg, ... )
}
void
-infoc( char c )
-{
- if (!(DFlags & QUIET)) {
- putchar( c );
- fflush( stdout );
- need_nl = Ontty;
- }
-}
-
-void
warn( const char *msg, ... )
{
va_list va;