2004-03-27 16:07:20 +00:00
|
|
|
/*
|
|
|
|
* mbsync - mailbox synchronizer
|
2002-12-28 15:31:20 +00:00
|
|
|
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
2004-03-27 16:07:20 +00:00
|
|
|
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
|
2000-12-20 21:41:21 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
2002-10-30 02:31:20 +00:00
|
|
|
*
|
2004-03-27 16:07:20 +00:00
|
|
|
* As a special exception, mbsync may be linked with the OpenSSL library,
|
2002-10-30 02:31:20 +00:00
|
|
|
* despite that library's more restrictive license.
|
2000-12-20 21:41:21 +00:00
|
|
|
*/
|
|
|
|
|
2003-05-07 00:06:37 +00:00
|
|
|
#include "isync.h"
|
|
|
|
|
2000-12-20 21:41:21 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
2000-12-21 18:16:44 +00:00
|
|
|
#include <errno.h>
|
2001-01-11 10:13:47 +00:00
|
|
|
#include <sys/stat.h>
|
2000-12-20 21:41:21 +00:00
|
|
|
|
2004-11-13 09:19:36 +00:00
|
|
|
void
|
|
|
|
Fprintf( FILE *f, const char *msg, ... )
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
va_list va;
|
|
|
|
|
|
|
|
va_start( va, msg );
|
|
|
|
r = vfprintf( f, msg, va );
|
|
|
|
va_end( va );
|
|
|
|
if (r < 0) {
|
|
|
|
perror( "cannot write file" );
|
|
|
|
exit( 1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
static const char Flags[] = { 'D', 'F', 'R', 'S', 'T' };
|
2000-12-20 21:41:21 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
static int
|
|
|
|
parse_flags( const char *buf )
|
2000-12-20 21:41:21 +00:00
|
|
|
{
|
2004-03-27 16:07:20 +00:00
|
|
|
unsigned flags, i, d;
|
|
|
|
|
|
|
|
for (flags = i = d = 0; i < as(Flags); i++)
|
|
|
|
if (buf[d] == Flags[i]) {
|
|
|
|
flags |= (1 << i);
|
|
|
|
d++;
|
|
|
|
}
|
|
|
|
return flags;
|
2000-12-20 21:41:21 +00:00
|
|
|
}
|
|
|
|
|
2002-01-16 21:43:58 +00:00
|
|
|
static int
|
2004-03-27 16:07:20 +00:00
|
|
|
make_flags( int flags, char *buf )
|
2002-01-16 19:47:28 +00:00
|
|
|
{
|
2004-03-27 16:07:20 +00:00
|
|
|
unsigned i, d;
|
|
|
|
|
|
|
|
for (i = d = 0; i < as(Flags); i++)
|
|
|
|
if (flags & (1 << i))
|
|
|
|
buf[d++] = Flags[i];
|
|
|
|
buf[d] = 0;
|
|
|
|
return d;
|
2002-01-16 19:47:28 +00:00
|
|
|
}
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
static void
|
|
|
|
makeopts( int dops, store_conf_t *dconf, int *dopts,
|
|
|
|
store_conf_t *sconf, int *sopts )
|
2000-12-20 21:41:21 +00:00
|
|
|
{
|
2004-03-27 16:07:20 +00:00
|
|
|
if (dops & (OP_DELETE|OP_FLAGS)) {
|
|
|
|
*dopts |= OPEN_SETFLAGS;
|
|
|
|
*sopts |= OPEN_OLD;
|
|
|
|
if (dops & OP_FLAGS)
|
|
|
|
*sopts |= OPEN_FLAGS;
|
|
|
|
}
|
|
|
|
if (dops & (OP_NEW|OP_RENEW)) {
|
|
|
|
*dopts |= OPEN_APPEND;
|
|
|
|
if (dops & OP_RENEW)
|
|
|
|
*sopts |= OPEN_OLD;
|
|
|
|
if (dops & OP_NEW)
|
|
|
|
*sopts |= OPEN_NEW;
|
|
|
|
if (dops & OP_EXPUNGE)
|
|
|
|
*sopts |= OPEN_FLAGS;
|
|
|
|
if (dconf->max_size)
|
|
|
|
*sopts |= OPEN_SIZE;
|
|
|
|
}
|
|
|
|
if (dops & OP_EXPUNGE) {
|
|
|
|
*dopts |= OPEN_EXPUNGE;
|
|
|
|
if (dconf->trash) {
|
|
|
|
if (!dconf->trash_only_new)
|
|
|
|
*dopts |= OPEN_OLD;
|
|
|
|
*dopts |= OPEN_NEW|OPEN_FLAGS;
|
|
|
|
} else if (sconf->trash && sconf->trash_remote_new)
|
|
|
|
*dopts |= OPEN_NEW|OPEN_FLAGS;
|
2000-12-21 23:10:18 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
if (dops & OP_CREATE)
|
|
|
|
*dopts |= OPEN_CREATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dump_box( store_t *ctx )
|
|
|
|
{
|
|
|
|
message_t *msg;
|
|
|
|
char fbuf[16]; /* enlarge when support for keywords is added */
|
|
|
|
|
|
|
|
if (Debug)
|
|
|
|
for (msg = ctx->msgs; msg; msg = msg->next) {
|
|
|
|
make_flags( msg->flags, fbuf );
|
|
|
|
printf( " message %d, %s, %d\n", msg->uid, fbuf, msg->size );
|
2002-06-19 02:31:07 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2002-06-19 02:31:07 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
static message_t *
|
|
|
|
findmsg( store_t *ctx, int uid, message_t **nmsg, const char *who )
|
|
|
|
{
|
|
|
|
message_t *msg;
|
|
|
|
|
|
|
|
if (uid > 0) {
|
|
|
|
if (*nmsg && (*nmsg)->uid == uid) {
|
|
|
|
debug( " %s came in sequence\n", who );
|
|
|
|
msg = *nmsg;
|
|
|
|
found:
|
|
|
|
*nmsg = msg->next;
|
|
|
|
if (!(msg->status & M_DEAD)) {
|
|
|
|
msg->status |= M_PROCESSED;
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
debug( " ... but it vanished under our feet!\n" );
|
|
|
|
} else {
|
|
|
|
for (msg = ctx->msgs; msg; msg = msg->next)
|
|
|
|
if (msg->uid == uid) {
|
|
|
|
debug( " %s came out of sequence\n", who );
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
debug( " %s not present\n", who );
|
2001-01-11 10:13:47 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
} else
|
|
|
|
debug( " no %s expected\n", who );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define S_DEAD (1<<0)
|
|
|
|
#define S_EXPIRED (1<<1)
|
|
|
|
#define S_DEL_MASTER (1<<2)
|
|
|
|
#define S_DEL_SLAVE (1<<3)
|
|
|
|
#define S_EXP_SLAVE (1<<4)
|
|
|
|
|
|
|
|
typedef struct sync_rec {
|
|
|
|
struct sync_rec *next;
|
|
|
|
/* string_list_t *keywords; */
|
|
|
|
int muid, suid;
|
|
|
|
unsigned char flags, status;
|
|
|
|
} sync_rec_t;
|
|
|
|
|
|
|
|
|
|
|
|
#define EX_OK 0
|
|
|
|
#define EX_FAIL 1
|
|
|
|
#define EX_STORE_BAD 2
|
|
|
|
#define EX_RSTORE_BAD 3
|
|
|
|
|
|
|
|
static int
|
|
|
|
expunge( store_t *ctx, store_t *rctx )
|
|
|
|
{
|
|
|
|
driver_t *driver = ctx->conf->driver, *rdriver = rctx->conf->driver;
|
|
|
|
message_t *msg;
|
|
|
|
msg_data_t msgdata;
|
|
|
|
|
|
|
|
for (msg = ctx->msgs; msg; msg = msg->next)
|
|
|
|
if (msg->flags & F_DELETED) {
|
|
|
|
if (ctx->conf->trash) {
|
|
|
|
if (!ctx->conf->trash_only_new || (msg->status & M_NOT_SYNCED)) {
|
|
|
|
debug( " trashing message %d\n", msg->uid );
|
|
|
|
switch (driver->trash_msg( ctx, msg )) {
|
|
|
|
case DRV_STORE_BAD: return EX_STORE_BAD;
|
|
|
|
default: return EX_FAIL;
|
|
|
|
case DRV_OK: break;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
debug( " not trashing message %d - not new\n", msg->uid );
|
|
|
|
} else if (rctx->conf->trash && rctx->conf->trash_remote_new) {
|
|
|
|
if (msg->status & M_NOT_SYNCED) {
|
|
|
|
if (!rctx->conf->max_size || msg->size <= rctx->conf->max_size) {
|
|
|
|
debug( " remote trashing message %d\n", msg->uid );
|
|
|
|
msgdata.flags = msg->flags;
|
|
|
|
switch (driver->fetch_msg( ctx, msg, &msgdata )) {
|
|
|
|
case DRV_STORE_BAD: return EX_STORE_BAD;
|
|
|
|
default: return EX_FAIL;
|
|
|
|
case DRV_OK: break;
|
|
|
|
}
|
|
|
|
switch (rdriver->store_msg( rctx, &msgdata, 0 )) {
|
|
|
|
case DRV_STORE_BAD: return EX_RSTORE_BAD;
|
|
|
|
default: return EX_FAIL;
|
|
|
|
case DRV_OK: break;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
debug( " not remote trashing message %d - too big\n", msg->uid );
|
|
|
|
} else
|
|
|
|
debug( " not remote trashing message %d - not new\n", msg->uid );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (driver->close( ctx )) {
|
|
|
|
case DRV_STORE_BAD: return EX_STORE_BAD;
|
|
|
|
default: return EX_FAIL;
|
|
|
|
case DRV_OK: return EX_OK;;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* cases:
|
|
|
|
a) both non-null
|
|
|
|
b) only master null
|
|
|
|
b.1) muid 0
|
|
|
|
b.2) muid -1
|
|
|
|
b.3) master not scanned
|
|
|
|
b.4) master gone
|
|
|
|
c) only slave null
|
|
|
|
c.1) suid 0
|
|
|
|
c.2) suid -1
|
|
|
|
c.3) slave not scanned
|
|
|
|
c.4) slave gone
|
|
|
|
d) both null
|
|
|
|
d.1) both gone
|
|
|
|
d.2) muid 0, slave not scanned
|
|
|
|
d.3) muid -1, slave not scanned
|
|
|
|
d.4) master gone, slave not scanned
|
|
|
|
d.5) muid 0, slave gone
|
|
|
|
d.6) muid -1, slave gone
|
|
|
|
d.7) suid 0, master not scanned
|
|
|
|
d.8) suid -1, master not scanned
|
|
|
|
d.9) slave gone, master not scanned
|
|
|
|
d.10) suid 0, master gone
|
|
|
|
d.11) suid -1, master gone
|
|
|
|
impossible cases: both muid & suid 0 or -1, both not scanned
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
sync_old( int tops, store_t *sctx, store_t *tctx, store_conf_t *tconf, FILE *jfp, int pull,
|
|
|
|
unsigned char *nflags, sync_rec_t *srec, message_t *smsg, message_t *tmsg, int dels, int delt )
|
|
|
|
{
|
|
|
|
driver_t *tdriver = tctx->conf->driver, *sdriver = sctx->conf->driver;
|
|
|
|
int uid, tuid, unex;
|
|
|
|
unsigned char sflags, aflags, dflags, rflags;
|
|
|
|
msg_data_t msgdata;
|
|
|
|
|
|
|
|
/* excludes (push) c.3) d.2) d.3) d.4) / (pull) b.3) d.7) d.8) d.9) */
|
|
|
|
tuid = pull ? srec->suid : srec->muid;
|
|
|
|
if (!tuid) {
|
|
|
|
/* b.1) / c.1) */
|
|
|
|
debug( pull ? " no more slave\n" : " no more master\n" );
|
|
|
|
} else if (dels) {
|
|
|
|
/* c.4) d.9) / b.4) d.4) */
|
|
|
|
debug( pull ? " master vanished\n" : " slave vanished\n" );
|
|
|
|
if (tmsg && tmsg->flags != *nflags)
|
|
|
|
info( "Info: conflicting changes in (%d,%d)\n", srec->muid, srec->suid );
|
|
|
|
if (tops & OP_DELETE) {
|
|
|
|
debug( pull ? " -> pulling delete\n" : " -> pushing delete\n" );
|
|
|
|
switch (tdriver->set_flags( tctx, tmsg, tuid, F_DELETED, 0 )) {
|
|
|
|
case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD;
|
|
|
|
case DRV_BOX_BAD: return SYNC_FAIL;
|
|
|
|
default: /* ok */ break;
|
|
|
|
case DRV_OK:
|
|
|
|
if (pull) {
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "< %d %d 0\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
srec->muid = 0;
|
|
|
|
} else {
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "> %d %d 0\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
srec->suid = 0;
|
|
|
|
}
|
|
|
|
}
|
2001-01-11 10:13:47 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
} else if (!smsg)
|
|
|
|
/* c.1) c.2) d.7) d.8) / b.1) b.2) d.2) d.3) */
|
|
|
|
;
|
|
|
|
else if (tuid < 0) {
|
|
|
|
/* b.2) / c.2) */
|
|
|
|
debug( pull ? " no slave yet\n" : " no master yet\n" );
|
|
|
|
if (tops & OP_RENEW) {
|
|
|
|
if ((tops & OP_EXPUNGE) && (smsg->flags & F_DELETED)) {
|
|
|
|
debug( pull ? " -> not pulling - would be expunged anyway\n" : " -> not pushing - would be expunged anyway\n" );
|
|
|
|
smsg->status |= M_NOT_SYNCED;
|
|
|
|
} else {
|
|
|
|
if ((smsg->flags & F_FLAGGED) || !tconf->max_size || smsg->size <= tconf->max_size) {
|
|
|
|
debug( pull ? " -> pulling it\n" : " -> pushing it\n" );
|
|
|
|
msgdata.flags = smsg->flags;
|
|
|
|
switch (sdriver->fetch_msg( sctx, smsg, &msgdata )) {
|
|
|
|
case DRV_STORE_BAD: return pull ? SYNC_MASTER_BAD : SYNC_SLAVE_BAD;
|
|
|
|
case DRV_BOX_BAD: return SYNC_FAIL;
|
|
|
|
default: /* ok */ smsg->status |= M_NOT_SYNCED; break;
|
|
|
|
case DRV_OK:
|
|
|
|
smsg->flags = msgdata.flags;
|
|
|
|
switch (tdriver->store_msg( tctx, &msgdata, &uid )) {
|
|
|
|
case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD;
|
|
|
|
default: return SYNC_FAIL;
|
|
|
|
case DRV_OK:
|
|
|
|
if (pull) {
|
|
|
|
srec->suid = uid;
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "> %d -1 %d\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
} else {
|
|
|
|
srec->muid = uid;
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "< -1 %d %d\n", srec->suid, srec->muid );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
*nflags = smsg->flags;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
debug( pull ? " -> not pulling - still too big\n" : " -> not pushing - still too big\n" );
|
|
|
|
smsg->status |= M_NOT_SYNCED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
smsg->status |= M_NOT_SYNCED;
|
|
|
|
} else if (!delt) {
|
|
|
|
/* a) & b.3) / c.3) */
|
|
|
|
debug( pull ? " may pull\n" : " may push\n" );
|
|
|
|
if (tops & OP_FLAGS) {
|
|
|
|
debug( pull ? " -> pulling flags\n" : " -> pushing flags\n" );
|
|
|
|
sflags = smsg->flags;
|
|
|
|
aflags = sflags & ~*nflags;
|
|
|
|
dflags = ~sflags & *nflags;
|
|
|
|
unex = 0;
|
|
|
|
if (srec->status & S_EXPIRED) {
|
|
|
|
if (!pull) {
|
|
|
|
if (sflags & F_DELETED) {
|
|
|
|
if (!(sflags & F_FLAGGED))
|
|
|
|
aflags &= ~F_DELETED;
|
|
|
|
} else
|
|
|
|
unex = 1;
|
|
|
|
} else {
|
|
|
|
if ((sflags & F_FLAGGED) && !(sflags & F_DELETED)) {
|
|
|
|
unex = 1;
|
|
|
|
dflags |= F_DELETED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rflags = (*nflags | aflags) & ~dflags;
|
|
|
|
if ((tops & OP_EXPUNGE) && (rflags & F_DELETED) &&
|
|
|
|
(!tctx->conf->trash || tctx->conf->trash_only_new))
|
|
|
|
{
|
|
|
|
aflags &= F_DELETED;
|
|
|
|
dflags = 0;
|
|
|
|
}
|
2005-09-29 21:05:05 +00:00
|
|
|
switch ((aflags | dflags) ? tdriver->set_flags( tctx, tmsg, tuid, aflags, dflags ) : DRV_OK) {
|
2004-03-27 16:07:20 +00:00
|
|
|
case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD;
|
|
|
|
case DRV_BOX_BAD: return SYNC_FAIL;
|
|
|
|
default: /* ok */ break;
|
|
|
|
case DRV_OK:
|
|
|
|
*nflags = rflags;
|
|
|
|
if (unex) {
|
|
|
|
debug( "unexpiring pair(%d,%d)\n", srec->muid, srec->suid );
|
|
|
|
/* log last, so deletion can't be misinterpreted! */
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "~ %d %d 0\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
srec->status &= ~S_EXPIRED;
|
|
|
|
}
|
|
|
|
}
|
2001-01-11 10:13:47 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
} /* else b.4) / c.4) */
|
|
|
|
return SYNC_OK;
|
|
|
|
}
|
2001-01-11 10:13:47 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
static int
|
|
|
|
sync_new( int tops, store_t *sctx, store_t *tctx, store_conf_t *tconf, FILE *jfp, sync_rec_t ***srecadd, int pull, int *smaxuid )
|
|
|
|
{
|
|
|
|
driver_t *tdriver = tctx->conf->driver, *sdriver = sctx->conf->driver;
|
|
|
|
sync_rec_t *srec;
|
|
|
|
message_t *msg;
|
|
|
|
int nmsgs, uid;
|
|
|
|
msg_data_t msgdata;
|
|
|
|
|
|
|
|
for (nmsgs = 0, msg = sctx->msgs; msg; msg = msg->next)
|
|
|
|
if (!(msg->status & M_PROCESSED)) {
|
|
|
|
if (tops & OP_NEW) {
|
|
|
|
debug( pull ? "new message %d on master\n" : "new message %d on slave\n", msg->uid );
|
|
|
|
if ((tops & OP_EXPUNGE) && (msg->flags & F_DELETED)) {
|
|
|
|
debug( pull ? " not pulling - would be expunged anyway\n" : " not pushing - would be expunged anyway\n" );
|
|
|
|
msg->status |= M_NOT_SYNCED;
|
|
|
|
} else {
|
|
|
|
if ((msg->flags & F_FLAGGED) || !tconf->max_size || msg->size <= tconf->max_size) {
|
|
|
|
debug( pull ? " pulling it\n" : " pushing it\n" );
|
|
|
|
if (!nmsgs)
|
|
|
|
info( pull ? "Pulling new messages..." : "Pushing new messages..." );
|
|
|
|
else
|
|
|
|
infoc( '.' );
|
|
|
|
nmsgs++;
|
|
|
|
msgdata.flags = msg->flags;
|
|
|
|
switch (sdriver->fetch_msg( sctx, msg, &msgdata )) {
|
|
|
|
case DRV_STORE_BAD: return pull ? SYNC_MASTER_BAD : SYNC_SLAVE_BAD;
|
|
|
|
case DRV_BOX_BAD: return SYNC_FAIL;
|
|
|
|
case DRV_MSG_BAD: /* ok */ msg->status |= M_NOT_SYNCED; continue;
|
|
|
|
}
|
|
|
|
msg->flags = msgdata.flags;
|
|
|
|
switch (tdriver->store_msg( tctx, &msgdata, &uid )) {
|
|
|
|
case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD;
|
|
|
|
default: return SYNC_FAIL;
|
|
|
|
case DRV_OK: break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
debug( pull ? " not pulling - too big\n" : " not pushing - too big\n" );
|
|
|
|
msg->status |= M_NOT_SYNCED;
|
|
|
|
uid = -1;
|
|
|
|
}
|
|
|
|
srec = nfmalloc( sizeof(*srec) );
|
|
|
|
if (pull) {
|
|
|
|
srec->muid = msg->uid;
|
|
|
|
srec->suid = uid;
|
|
|
|
} else {
|
|
|
|
srec->muid = uid;
|
|
|
|
srec->suid = msg->uid;
|
|
|
|
}
|
|
|
|
srec->flags = msg->flags;
|
|
|
|
srec->status = 0;
|
|
|
|
srec->next = 0;
|
|
|
|
**srecadd = srec;
|
|
|
|
*srecadd = &srec->next;
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "+ %d %d %u\n", srec->muid, srec->suid, srec->flags );
|
2004-03-27 16:07:20 +00:00
|
|
|
if (*smaxuid < msg->uid) {
|
|
|
|
*smaxuid = msg->uid;
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, pull ? "( %d\n" : ") %d\n", msg->uid );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
msg->status |= M_NOT_SYNCED;
|
Bunch 'o patches from Oswald Buddenhagen:
i implemented some cool stuff (tm).
first, the long missing "create server-side missing mailboxes". -C now
creates both local and remote boxes; -L and -R create only local/remote.
second, i implemented a 1:1 remote:local folder mapping (-1) with an
optional INBOX exception (inbox/-I). the remote folder is specified with
the folder keyword (or -F switch) and takes precedence over the
namespace setting. the local directory with the mailboxes can now be
specified on the command line, too (-M).
another patch:
- made the -1 switch settable permanently (OneToOne). after all, you
usually define your mailbox layout once forever. removed -A, as it is
semantically -a modified by -1.
- cleaned up message output a bit. still, the quiet variable should be
used throughout the program. at best, create some generic output
function, which obeys a global verbosity level variable.
- optimized + cleaned up configuration parser slightly
- minor cleanups
add an (almost) unique id to every uploaded message and search for it
right after. i thought about using the message-id, but a) it is not
guaranteed to be unique in a mailbox (imagine you edit a mail and store
the dupe in the same box) and b) some mails (e.g., postponed) don't even
have one. a downside of the current implementation is, that this
id-header remains in the mailbox, but given that it wastes only 27 bytes
per mail and removing it would mean several roundtrips more, this seems
acceptable.
i changed the line-counting loop to use a mmapped file instead of
reading it in chunks, as it makes things simpler and is probably even
faster for big mails.
the amount of goto statements in my code may be scary, but c is simply
lacking a multi-level break statement. :)
this is the "shut up" patch. :) it makes the -q option consequent, so to
say.
additionally it adds an -l option which gathers all defined/found
mailboxes and just outputs the list. don't ask what i need it for. ;)
2002-10-30 02:23:05 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
if (nmsgs)
|
|
|
|
info( " %d messages\n", nmsgs );
|
|
|
|
return SYNC_OK;
|
|
|
|
}
|
2001-01-11 10:13:47 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
static char *
|
|
|
|
clean_strdup( const char *s )
|
|
|
|
{
|
|
|
|
char *cs;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
cs = nfstrdup( s );
|
|
|
|
for (i = 0; cs[i]; i++)
|
|
|
|
if (cs[i] == '/')
|
|
|
|
cs[i] = '!';
|
|
|
|
return cs;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sync_boxes( store_t *mctx, const char *mname,
|
|
|
|
store_t *sctx, const char *sname,
|
|
|
|
channel_conf_t *chan )
|
|
|
|
{
|
|
|
|
driver_t *mdriver = mctx->conf->driver, *sdriver = sctx->conf->driver;
|
|
|
|
message_t *mmsg, *smsg, *nmmsg, *nsmsg;
|
|
|
|
sync_rec_t *recs, *srec, **srecadd, *nsrec;
|
|
|
|
char *dname, *jname, *nname, *lname, *s, *cmname, *csname;
|
|
|
|
FILE *dfp, *jfp, *nfp;
|
|
|
|
int mopts, sopts;
|
|
|
|
int nom, nos, delm, dels, mex, sex;
|
|
|
|
int muidval, suidval, smaxxuid, mmaxuid, smaxuid, minwuid, maxwuid;
|
|
|
|
int t1, t2, t3;
|
|
|
|
int lfd, ret, line, todel, delt, i, *mexcs, nmexcs, rmexcs;
|
|
|
|
unsigned char nflags;
|
|
|
|
struct stat st;
|
|
|
|
struct flock lck;
|
|
|
|
char fbuf[16]; /* enlarge when support for keywords is added */
|
|
|
|
char buf[64];
|
|
|
|
|
|
|
|
ret = SYNC_OK;
|
|
|
|
recs = 0, srecadd = &recs;
|
|
|
|
|
|
|
|
nmmsg = nsmsg = 0;
|
|
|
|
|
|
|
|
mctx->uidvalidity = sctx->uidvalidity = 0;
|
|
|
|
mopts = sopts = 0;
|
|
|
|
makeopts( chan->sops, chan->slave, &sopts, chan->master, &mopts );
|
|
|
|
makeopts( chan->mops, chan->master, &mopts, chan->slave, &sopts );
|
|
|
|
if ((chan->sops & (OP_NEW|OP_RENEW)) && chan->max_messages)
|
|
|
|
sopts |= OPEN_OLD|OPEN_NEW|OPEN_FLAGS;
|
|
|
|
if (!mname || (mctx->conf->map_inbox && !strcmp( mctx->conf->map_inbox, mname )))
|
|
|
|
mname = "INBOX";
|
|
|
|
mctx->name = mname;
|
|
|
|
mdriver->prepare( mctx, mopts );
|
|
|
|
if (!sname || (sctx->conf->map_inbox && !strcmp( sctx->conf->map_inbox, sname )))
|
|
|
|
sname = "INBOX";
|
|
|
|
sctx->name = sname;
|
|
|
|
sdriver->prepare( sctx, sopts );
|
|
|
|
|
|
|
|
if (!strcmp( chan->sync_state ? chan->sync_state : global_sync_state, "*" )) {
|
|
|
|
if (!sctx->path) {
|
|
|
|
fprintf( stderr, "Error: store '%s' does not support in-box sync state\n", chan->slave->name );
|
|
|
|
return SYNC_SLAVE_BAD;
|
|
|
|
}
|
|
|
|
nfasprintf( &dname, "%s/." EXE "state", sctx->path );
|
|
|
|
} else {
|
|
|
|
csname = clean_strdup( sname );
|
|
|
|
if (chan->sync_state)
|
|
|
|
nfasprintf( &dname, "%s%s", chan->sync_state, csname );
|
|
|
|
else {
|
|
|
|
cmname = clean_strdup( mname );
|
|
|
|
nfasprintf( &dname, "%s:%s:%s_:%s:%s", global_sync_state,
|
|
|
|
chan->master->name, cmname, chan->slave->name, csname );
|
|
|
|
free( cmname );
|
|
|
|
}
|
|
|
|
free( csname );
|
|
|
|
}
|
|
|
|
nfasprintf( &jname, "%s.journal", dname );
|
|
|
|
nfasprintf( &nname, "%s.new", dname );
|
|
|
|
nfasprintf( &lname, "%s.lock", dname );
|
|
|
|
muidval = suidval = smaxxuid = mmaxuid = smaxuid = 0;
|
|
|
|
memset( &lck, 0, sizeof(lck) );
|
|
|
|
#if SEEK_SET != 0
|
|
|
|
lck.l_whence = SEEK_SET;
|
|
|
|
#endif
|
|
|
|
#if F_WRLCK != 0
|
|
|
|
lck.l_type = F_WRLCK;
|
|
|
|
#endif
|
|
|
|
if ((lfd = open( lname, O_WRONLY|O_CREAT, 0666 )) < 0) {
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
lferr:
|
|
|
|
fprintf( stderr, "Error: cannot create lock file %s: %s\n", lname, strerror(errno) );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail2;
|
|
|
|
}
|
|
|
|
goto skiprd;
|
|
|
|
}
|
|
|
|
if (fcntl( lfd, F_SETLK, &lck )) {
|
|
|
|
lckerr:
|
|
|
|
fprintf( stderr, "Error: channel :%s:%s-:%s:%s is locked\n",
|
|
|
|
chan->master->name, mname, chan->slave->name, sname );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail1;
|
|
|
|
}
|
|
|
|
if ((dfp = fopen( dname, "r" ))) {
|
|
|
|
debug( "reading sync state %s ...\n", dname );
|
2004-11-13 09:19:36 +00:00
|
|
|
if (!fgets( buf, sizeof(buf), dfp ) || !(i = strlen( buf )) || buf[i - 1] != '\n') {
|
|
|
|
fprintf( stderr, "Error: incomplete sync state header in %s\n", dname );
|
|
|
|
fclose( dfp );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
if (sscanf( buf, "%d:%d %d:%d:%d", &muidval, &mmaxuid, &suidval, &smaxxuid, &smaxuid) != 5) {
|
2004-03-27 16:07:20 +00:00
|
|
|
fprintf( stderr, "Error: invalid sync state header in %s\n", dname );
|
|
|
|
fclose( dfp );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
line = 1;
|
|
|
|
while (fgets( buf, sizeof(buf), dfp )) {
|
|
|
|
line++;
|
2004-11-13 09:19:36 +00:00
|
|
|
if (!(i = strlen( buf )) || buf[i - 1] != '\n') {
|
|
|
|
fprintf( stderr, "Error: incomplete sync state entry at %s:%d\n", dname, line );
|
|
|
|
fclose( dfp );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
fbuf[0] = 0;
|
2004-11-13 09:19:36 +00:00
|
|
|
if (sscanf( buf, "%d %d %15s", &t1, &t2, fbuf ) < 2) {
|
2004-03-27 16:07:20 +00:00
|
|
|
fprintf( stderr, "Error: invalid sync state entry at %s:%d\n", dname, line );
|
|
|
|
fclose( dfp );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
srec = nfmalloc( sizeof(*srec) );
|
|
|
|
srec->muid = t1;
|
|
|
|
srec->suid = t2;
|
|
|
|
s = fbuf;
|
|
|
|
if (*s == 'X') {
|
|
|
|
s++;
|
|
|
|
srec->status = S_EXPIRED;
|
|
|
|
} else
|
|
|
|
srec->status = 0;
|
|
|
|
srec->flags = parse_flags( s );
|
|
|
|
debug( " entry (%d,%d,%u,%s)\n", srec->muid, srec->suid, srec->flags, srec->status & S_EXPIRED ? "X" : "" );
|
|
|
|
srec->next = 0;
|
|
|
|
*srecadd = srec;
|
|
|
|
srecadd = &srec->next;
|
|
|
|
}
|
|
|
|
fclose( dfp );
|
|
|
|
} else {
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
fprintf( stderr, "Error: cannot read sync state %s\n", dname );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((jfp = fopen( jname, "r" ))) {
|
|
|
|
if (!stat( nname, &st )) {
|
|
|
|
debug( "recovering journal ...\n" );
|
|
|
|
line = 0;
|
|
|
|
srec = recs;
|
|
|
|
while (fgets( buf, sizeof(buf), jfp )) {
|
|
|
|
line++;
|
2004-11-13 09:19:36 +00:00
|
|
|
if (!(i = strlen( buf )) || buf[i - 1] != '\n') {
|
|
|
|
fprintf( stderr, "Error: incomplete journal entry at %s:%d\n", jname, line );
|
|
|
|
fclose( jfp );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
if (buf[0] == '^')
|
|
|
|
srec = recs;
|
|
|
|
else {
|
|
|
|
if (buf[0] == '(' || buf[0] == ')' ?
|
2004-11-13 09:19:36 +00:00
|
|
|
(sscanf( buf + 2, "%d", &t1 ) != 1) :
|
2004-03-27 16:07:20 +00:00
|
|
|
buf[0] == '-' || buf[0] == '|' ?
|
2004-11-13 09:19:36 +00:00
|
|
|
(sscanf( buf + 2, "%d %d", &t1, &t2 ) != 2) :
|
|
|
|
(sscanf( buf + 2, "%d %d %d", &t1, &t2, &t3 ) != 3))
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
fprintf( stderr, "Error: malformed journal entry at %s:%d\n", jname, line );
|
|
|
|
fclose( jfp );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
if (buf[0] == '(')
|
|
|
|
mmaxuid = t1;
|
|
|
|
else if (buf[0] == ')')
|
|
|
|
smaxuid = t1;
|
|
|
|
else if (buf[0] == '|') {
|
|
|
|
muidval = t1;
|
|
|
|
suidval = t2;
|
|
|
|
} else if (buf[0] == '+') {
|
|
|
|
srec = nfmalloc( sizeof(*srec) );
|
|
|
|
srec->muid = t1;
|
|
|
|
srec->suid = t2;
|
|
|
|
srec->flags = t3;
|
|
|
|
debug( " new entry(%d,%d,%u)\n", t1, t2, t3 );
|
|
|
|
srec->status = 0;
|
|
|
|
srec->next = 0;
|
|
|
|
*srecadd = srec;
|
|
|
|
srecadd = &srec->next;
|
|
|
|
} else {
|
|
|
|
for (; srec; srec = srec->next)
|
|
|
|
if (srec->muid == t1 && srec->suid == t2)
|
|
|
|
goto syncfnd;
|
|
|
|
fprintf( stderr, "Error: journal entry at %s:%d refers to non-existing sync state entry\n", jname, line );
|
|
|
|
fclose( jfp );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
syncfnd:
|
|
|
|
debug( " entry(%d,%d,%u) ", srec->muid, srec->suid, srec->flags );
|
|
|
|
switch (buf[0]) {
|
|
|
|
case '-':
|
|
|
|
debug( "killed\n" );
|
|
|
|
srec->status = S_DEAD;
|
|
|
|
break;
|
|
|
|
case '<':
|
|
|
|
debug( "master now %d\n", t3 );
|
|
|
|
srec->muid = t3;
|
|
|
|
break;
|
|
|
|
case '>':
|
|
|
|
debug( "slave now %d\n", t3 );
|
|
|
|
srec->suid = t3;
|
|
|
|
break;
|
|
|
|
case '*':
|
|
|
|
debug( "flags now %d\n", t3 );
|
|
|
|
srec->flags = t3;
|
|
|
|
break;
|
|
|
|
case '~':
|
|
|
|
debug( "expired now %d\n", t3 );
|
|
|
|
if (t3) {
|
|
|
|
if (smaxxuid < t2)
|
|
|
|
smaxxuid = t2;
|
|
|
|
srec->status |= S_EXPIRED;
|
|
|
|
} else
|
|
|
|
srec->status &= ~S_EXPIRED;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf( stderr, "Error: unrecognized journal entry at %s:%d\n", jname, line );
|
|
|
|
fclose( jfp );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose( jfp );
|
|
|
|
} else {
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
fprintf( stderr, "Error: cannot read journal %s\n", jname );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
skiprd:
|
|
|
|
|
|
|
|
if (sctx->opts & OPEN_NEW)
|
|
|
|
maxwuid = INT_MAX;
|
|
|
|
else if (sctx->opts & OPEN_OLD) {
|
|
|
|
maxwuid = 0;
|
|
|
|
for (srec = recs; srec; srec = srec->next)
|
|
|
|
if (!(srec->status & S_DEAD) && srec->suid > maxwuid)
|
|
|
|
maxwuid = srec->suid;
|
|
|
|
} else
|
|
|
|
maxwuid = 0;
|
2004-09-07 11:59:51 +00:00
|
|
|
info( "Selecting slave %s... ", sname );
|
2004-03-27 16:07:20 +00:00
|
|
|
debug( "selecting slave [1,%d]\n", maxwuid );
|
|
|
|
switch (sdriver->select( sctx, (sctx->opts & OPEN_OLD) ? 1 : smaxuid + 1, maxwuid, 0, 0 )) {
|
|
|
|
case DRV_STORE_BAD: ret = SYNC_SLAVE_BAD; goto bail;
|
|
|
|
case DRV_BOX_BAD: ret = SYNC_FAIL; goto bail;
|
|
|
|
}
|
|
|
|
info( "%d messages, %d recent\n", sctx->count, sctx->recent );
|
|
|
|
dump_box( sctx );
|
|
|
|
|
|
|
|
if (suidval && suidval != sctx->uidvalidity) {
|
|
|
|
fprintf( stderr, "Error: UIDVALIDITY of slave changed\n" );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
s = strrchr( dname, '/' );
|
|
|
|
*s = 0;
|
|
|
|
mkdir( dname, 0700 );
|
|
|
|
*s = '/';
|
|
|
|
if (lfd < 0) {
|
|
|
|
if ((lfd = open( lname, O_WRONLY|O_CREAT, 0666 )) < 0)
|
|
|
|
goto lferr;
|
|
|
|
if (fcntl( lfd, F_SETLK, &lck ))
|
|
|
|
goto lckerr;
|
2000-12-20 21:41:21 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
if (!(nfp = fopen( nname, "w" ))) {
|
|
|
|
fprintf( stderr, "Error: cannot write new sync state %s\n", nname );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
2001-01-11 10:13:47 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
if (!(jfp = fopen( jname, "a" ))) {
|
|
|
|
fprintf( stderr, "Error: cannot write journal %s\n", jname );
|
|
|
|
fclose( nfp );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
setlinebuf( jfp );
|
|
|
|
|
|
|
|
mexcs = 0;
|
|
|
|
nmexcs = rmexcs = 0;
|
|
|
|
minwuid = INT_MAX;
|
|
|
|
if (smaxxuid) {
|
|
|
|
debug( "preparing master selection - max expired slave uid is %d\n", smaxxuid );
|
|
|
|
for (srec = recs; srec; srec = srec->next) {
|
|
|
|
if (srec->status & S_DEAD)
|
|
|
|
continue;
|
|
|
|
if (srec->status & S_EXPIRED) {
|
|
|
|
if (!srec->suid || ((sctx->opts & OPEN_OLD) && !findmsg( sctx, srec->suid, &nsmsg, "slave" )))
|
|
|
|
srec->status |= S_EXP_SLAVE;
|
|
|
|
else if (minwuid > srec->muid)
|
|
|
|
minwuid = srec->muid;
|
|
|
|
} else if (smaxxuid < srec->suid && minwuid > srec->muid)
|
|
|
|
minwuid = srec->muid;
|
|
|
|
}
|
|
|
|
debug( " min non-orphaned master uid is %d\n", minwuid );
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "^\n" ); /* if any S_EXP_SLAVE */
|
2004-03-27 16:07:20 +00:00
|
|
|
for (srec = recs; srec; srec = srec->next) {
|
|
|
|
if (srec->status & S_DEAD)
|
|
|
|
continue;
|
|
|
|
if (srec->status & S_EXP_SLAVE) {
|
|
|
|
if (minwuid > srec->muid && mmaxuid >= srec->muid) {
|
|
|
|
debug( " -> killing (%d,%d)\n", srec->muid, srec->suid );
|
|
|
|
srec->status = S_DEAD;
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "- %d %d\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
} else if (srec->suid) {
|
|
|
|
debug( " -> orphaning (%d,[%d])\n", srec->muid, srec->suid );
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "> %d %d 0\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
srec->suid = 0;
|
|
|
|
}
|
|
|
|
} else if (minwuid > srec->muid) {
|
|
|
|
if (srec->suid < 0) {
|
|
|
|
if (mmaxuid >= srec->muid) {
|
|
|
|
debug( " -> killing (%d,%d)\n", srec->muid, srec->suid );
|
|
|
|
srec->status = S_DEAD;
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "- %d %d\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
} else if (srec->muid > 0 && srec->suid && (mctx->opts & OPEN_OLD) &&
|
|
|
|
(!(mctx->opts & OPEN_NEW) || mmaxuid >= srec->muid)) {
|
|
|
|
if (nmexcs == rmexcs) {
|
|
|
|
rmexcs = rmexcs * 2 + 100;
|
|
|
|
mexcs = nfrealloc( mexcs, rmexcs * sizeof(int) );
|
|
|
|
}
|
|
|
|
mexcs[nmexcs++] = srec->muid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
debug( " exception list is:" );
|
|
|
|
for (i = 0; i < nmexcs; i++)
|
|
|
|
debug( " %d", mexcs[i] );
|
|
|
|
debug( "\n" );
|
|
|
|
} else if (mctx->opts & OPEN_OLD)
|
|
|
|
minwuid = 1;
|
|
|
|
if (mctx->opts & OPEN_NEW) {
|
|
|
|
if (minwuid > mmaxuid + 1)
|
|
|
|
minwuid = mmaxuid + 1;
|
|
|
|
maxwuid = INT_MAX;
|
|
|
|
} else if (mctx->opts & OPEN_OLD) {
|
|
|
|
maxwuid = 0;
|
|
|
|
for (srec = recs; srec; srec = srec->next)
|
|
|
|
if (!(srec->status & S_DEAD) && srec->muid > maxwuid)
|
|
|
|
maxwuid = srec->muid;
|
|
|
|
} else
|
|
|
|
maxwuid = 0;
|
2004-09-07 11:59:51 +00:00
|
|
|
info( "Selecting master %s... ", mname );
|
2004-03-27 16:07:20 +00:00
|
|
|
debug( "selecting master [%d,%d]\n", minwuid, maxwuid );
|
|
|
|
switch (mdriver->select( mctx, minwuid, maxwuid, mexcs, nmexcs )) {
|
|
|
|
case DRV_STORE_BAD: ret = SYNC_MASTER_BAD; goto finish;
|
|
|
|
case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish;
|
|
|
|
}
|
|
|
|
info( "%d messages, %d recent\n", mctx->count, mctx->recent );
|
|
|
|
dump_box( mctx );
|
2001-01-11 10:13:47 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
if (muidval && muidval != mctx->uidvalidity) {
|
|
|
|
fprintf( stderr, "Error: UIDVALIDITY of master changed\n" );
|
|
|
|
ret = SYNC_FAIL;
|
|
|
|
goto finish;
|
2000-12-20 21:41:21 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
if (!muidval || !suidval) {
|
|
|
|
muidval = mctx->uidvalidity;
|
|
|
|
suidval = sctx->uidvalidity;
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "| %d %d\n", muidval, suidval );
|
2001-11-15 23:59:27 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
info( "Synchronizing\n" );
|
|
|
|
debug( "synchronizing old entries\n" );
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "^\n" );
|
2004-03-27 16:07:20 +00:00
|
|
|
for (srec = recs; srec; srec = srec->next) {
|
|
|
|
if (srec->status & S_DEAD)
|
|
|
|
continue;
|
|
|
|
debug( "pair (%d,%d)\n", srec->muid, srec->suid );
|
|
|
|
mmsg = findmsg( mctx, srec->muid, &nmmsg, "master" );
|
|
|
|
smsg = (srec->status & S_EXP_SLAVE) ? 0 : findmsg( sctx, srec->suid, &nsmsg, "slave" );
|
|
|
|
nom = !mmsg && (mctx->opts & OPEN_OLD);
|
|
|
|
nos = !smsg && (sctx->opts & OPEN_OLD);
|
|
|
|
if (nom && nos) {
|
|
|
|
debug( " vanished\n" );
|
|
|
|
/* d.1) d.5) d.6) d.10) d.11) */
|
|
|
|
srec->status = S_DEAD;
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "- %d %d\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
} else {
|
|
|
|
delm = nom && (srec->muid > 0);
|
|
|
|
dels = nos && (srec->suid > 0);
|
|
|
|
nflags = srec->flags;
|
|
|
|
|
|
|
|
if ((ret = sync_old( chan->mops, sctx, mctx, chan->master, jfp, 0, &nflags, srec, smsg, mmsg, dels, delm )) != SYNC_OK ||
|
2005-09-29 21:05:05 +00:00
|
|
|
(ret = sync_old( chan->sops, mctx, sctx, chan->slave, jfp, 1, &nflags, srec, mmsg, smsg, delm, dels )) != SYNC_OK)
|
2004-03-27 16:07:20 +00:00
|
|
|
goto finish;
|
|
|
|
|
|
|
|
if (srec->flags != nflags) {
|
|
|
|
debug( " updating flags (%u -> %u)\n", srec->flags, nflags );
|
|
|
|
srec->flags = nflags;
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "* %d %d %u\n", srec->muid, srec->suid, nflags );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
if (mmsg && (mmsg->flags & F_DELETED))
|
|
|
|
srec->status |= S_DEL_MASTER;
|
|
|
|
if (smsg && (smsg->flags & F_DELETED))
|
|
|
|
srec->status |= S_DEL_SLAVE;
|
|
|
|
}
|
2001-11-15 23:59:27 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
debug( "synchronizing new entries\n" );
|
|
|
|
if ((ret = sync_new( chan->mops, sctx, mctx, chan->master, jfp, &srecadd, 0, &smaxuid )) != SYNC_OK ||
|
|
|
|
(ret = sync_new( chan->sops, mctx, sctx, chan->slave, jfp, &srecadd, 1, &mmaxuid )) != SYNC_OK)
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
if ((chan->sops & (OP_NEW|OP_RENEW)) && chan->max_messages) {
|
|
|
|
debug( "expiring excessive entries\n" );
|
|
|
|
todel = sctx->count - chan->max_messages;
|
|
|
|
for (smsg = sctx->msgs; smsg && todel > 0; smsg = smsg->next)
|
|
|
|
if (!(smsg->status & M_DEAD) && (smsg->flags & F_DELETED))
|
|
|
|
todel--;
|
|
|
|
delt = 0;
|
|
|
|
for (smsg = sctx->msgs; smsg && todel > 0; smsg = smsg->next) {
|
|
|
|
if ((smsg->status & M_DEAD) || (smsg->flags & F_DELETED))
|
|
|
|
continue;
|
|
|
|
if ((smsg->flags & F_FLAGGED) || (smsg->status & M_NOT_SYNCED)) /* add M_DESYNCED? */
|
|
|
|
todel--;
|
|
|
|
else if (!(smsg->status & M_RECENT)) {
|
|
|
|
smsg->status |= M_EXPIRED;
|
|
|
|
delt++;
|
|
|
|
todel--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (delt) {
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "^\n" );
|
2004-03-27 16:07:20 +00:00
|
|
|
for (srec = recs; srec; srec = srec->next) {
|
|
|
|
if (srec->status & (S_DEAD|S_EXPIRED))
|
|
|
|
continue;
|
|
|
|
smsg = findmsg( sctx, srec->suid, &nsmsg, "slave" );
|
|
|
|
if (smsg && (smsg->status & M_EXPIRED)) {
|
|
|
|
debug( " expiring pair(%d,%d)\n", srec->muid, srec->suid );
|
|
|
|
/* log first, so deletion can't be misinterpreted! */
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "~ %d %d 1\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
if (smaxxuid < srec->suid)
|
|
|
|
smaxxuid = srec->suid;
|
|
|
|
srec->status |= S_EXPIRED;
|
|
|
|
switch (sdriver->set_flags( sctx, smsg, 0, F_DELETED, 0 )) {
|
|
|
|
case DRV_STORE_BAD: ret = SYNC_SLAVE_BAD; goto finish;
|
|
|
|
case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish;
|
|
|
|
default: /* ok */ break;
|
|
|
|
case DRV_OK: srec->status |= S_DEL_SLAVE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-12-21 18:16:44 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2000-12-21 18:16:44 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
/* Doing CLOSE here instead of EXPUNGE above saves network traffic.
|
|
|
|
But it costs more server power for single-file formats. And it
|
|
|
|
makes disk-full/quota-exceeded more probable. */
|
|
|
|
mex = sex = 0;
|
|
|
|
if (chan->mops & OP_EXPUNGE) {
|
|
|
|
info( "Expunging master\n" );
|
|
|
|
debug( "expunging master\n" );
|
|
|
|
switch (expunge( mctx, sctx )) {
|
|
|
|
case EX_STORE_BAD: ret = SYNC_MASTER_BAD; goto finish;
|
|
|
|
case EX_RSTORE_BAD: ret = SYNC_SLAVE_BAD; goto finish;
|
|
|
|
default: ret = SYNC_FAIL; break;
|
|
|
|
case EX_OK: mex = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (chan->sops & OP_EXPUNGE) {
|
|
|
|
info( "Expunging slave\n" );
|
|
|
|
debug( "expunging slave\n" );
|
|
|
|
switch (expunge( sctx, mctx )) {
|
|
|
|
case EX_STORE_BAD: ret = SYNC_SLAVE_BAD; goto finish;
|
|
|
|
case EX_RSTORE_BAD: ret = SYNC_MASTER_BAD; goto finish;
|
|
|
|
default: ret = SYNC_FAIL; break;
|
|
|
|
case EX_OK: mex = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mex || sex) {
|
|
|
|
/* This cleanup is not strictly necessary, as the next full sync
|
|
|
|
would throw out the dead entries anyway. But ... */
|
|
|
|
|
|
|
|
minwuid = INT_MAX;
|
|
|
|
if (smaxxuid) {
|
|
|
|
debug( "preparing entry purge - max expired slave uid is %d\n", smaxxuid );
|
|
|
|
for (srec = recs; srec; srec = srec->next) {
|
|
|
|
if (srec->status & S_DEAD)
|
|
|
|
continue;
|
|
|
|
if (!((srec->suid <= 0 || ((srec->status & S_DEL_SLAVE) && sex)) &&
|
|
|
|
(srec->muid <= 0 || ((srec->status & S_DEL_MASTER) && mex) || (srec->status & S_EXPIRED))) &&
|
|
|
|
smaxxuid < srec->suid && minwuid > srec->muid)
|
|
|
|
minwuid = srec->muid;
|
|
|
|
}
|
|
|
|
debug( " min non-orphaned master uid is %d\n", minwuid );
|
2002-01-16 19:47:28 +00:00
|
|
|
}
|
2000-12-21 18:16:44 +00:00
|
|
|
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "^\n" );
|
2004-03-27 16:07:20 +00:00
|
|
|
for (srec = recs; srec; srec = srec->next) {
|
|
|
|
if (srec->status & S_DEAD)
|
|
|
|
continue;
|
|
|
|
if (srec->suid <= 0 || ((srec->status & S_DEL_SLAVE) && sex)) {
|
|
|
|
if (srec->muid <= 0 || ((srec->status & S_DEL_MASTER) && mex)) {
|
|
|
|
debug( " -> killing (%d,%d)\n", srec->muid, srec->suid );
|
|
|
|
srec->status = S_DEAD;
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "- %d %d\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
} else if (srec->status & S_EXPIRED) {
|
|
|
|
if (mmaxuid >= srec->muid && minwuid > srec->muid) {
|
|
|
|
debug( " -> killing (%d,%d)\n", srec->muid, srec->suid );
|
|
|
|
srec->status = S_DEAD;
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "- %d %d\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
} else if (srec->suid) {
|
|
|
|
debug( " -> orphaning (%d,[%d])\n", srec->muid, srec->suid );
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( jfp, "> %d %d 0\n", srec->muid, srec->suid );
|
2004-03-27 16:07:20 +00:00
|
|
|
srec->suid = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-12-20 21:41:21 +00:00
|
|
|
}
|
2001-10-31 19:50:01 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
finish:
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( nfp, "%d:%d %d:%d:%d\n", muidval, mmaxuid, suidval, smaxxuid, smaxuid );
|
2004-03-27 16:07:20 +00:00
|
|
|
for (srec = recs; srec; srec = srec->next) {
|
|
|
|
if (srec->status & S_DEAD)
|
|
|
|
continue;
|
|
|
|
make_flags( srec->flags, fbuf );
|
2004-11-13 09:19:36 +00:00
|
|
|
Fprintf( nfp, "%d %d %s%s\n", srec->muid, srec->suid,
|
2004-03-27 16:07:20 +00:00
|
|
|
srec->status & S_EXPIRED ? "X" : "", fbuf );
|
|
|
|
}
|
2000-12-20 21:41:21 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
fclose( nfp );
|
|
|
|
fclose( jfp );
|
|
|
|
/* order is important! */
|
|
|
|
rename( nname, dname );
|
|
|
|
unlink( jname );
|
2002-12-28 04:12:07 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
bail:
|
|
|
|
for (srec = recs; srec; srec = nsrec) {
|
|
|
|
nsrec = srec->next;
|
|
|
|
free( srec );
|
|
|
|
}
|
|
|
|
unlink( lname );
|
|
|
|
bail1:
|
|
|
|
close( lfd );
|
|
|
|
bail2:
|
|
|
|
free( lname );
|
|
|
|
free( nname );
|
|
|
|
free( jname );
|
|
|
|
free( dname );
|
|
|
|
return ret;
|
2000-12-20 21:41:21 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|