fbc563e4cb
ensure that the messages are actually skipped, not subsequently expunged.
502 lines
12 KiB
C
502 lines
12 KiB
C
// SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins <me@mutt.org>
|
|
// SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen <ossi@users.sf.net>
|
|
// SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception
|
|
/*
|
|
* mbsync - mailbox synchronizer
|
|
*/
|
|
|
|
#include "main_p.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
#ifdef __linux__
|
|
# include <sys/prctl.h>
|
|
#endif
|
|
|
|
static void ATTR_NORETURN
|
|
version( void )
|
|
{
|
|
puts( PACKAGE " " VERSION );
|
|
exit( 0 );
|
|
}
|
|
|
|
static void ATTR_NORETURN
|
|
usage( int code )
|
|
{
|
|
fputs(
|
|
PACKAGE " " VERSION " - mailbox synchronizer\n"
|
|
"Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>\n"
|
|
"Copyright (C) 2002-2022 Oswald Buddenhagen <ossi@users.sf.net>\n"
|
|
"Copyright (C) 2004 Theodore Ts'o <tytso@mit.edu>\n"
|
|
"usage:\n"
|
|
" " EXE " [flags] {{channel[:box,...]|group} ...|-a}\n"
|
|
" -a, --all operate on all defined channels\n"
|
|
" -l, --list list mailboxes instead of syncing them\n"
|
|
" -ls, --list-stores raw listing of stores' mailboxes\n"
|
|
" -n, --new propagate new messages\n"
|
|
" -d, --delete propagate message deletions\n"
|
|
" -f, --flags propagate message flag changes\n"
|
|
" -N, --renew propagate previously not propagated new messages\n"
|
|
" -L, --pull propagate from far to near side\n"
|
|
" -H, --push propagate from near to far side\n"
|
|
" -C, --create propagate creations of mailboxes\n"
|
|
" -R, --remove propagate deletions of mailboxes\n"
|
|
" -X, --expunge expunge deleted messages\n"
|
|
" -c, --config CONFIG read an alternate config file (default: ~/." EXE "rc)\n"
|
|
" -D, --debug debugging modes (see manual)\n"
|
|
" -V, --verbose display what is happening\n"
|
|
" -q, --quiet don't display progress counters\n"
|
|
" -v, --version display version\n"
|
|
" -h, --help display this help message\n"
|
|
"\nIf neither --pull nor --push are specified, both are active.\n"
|
|
"If neither --new, --delete, --flags nor --renew are specified, all are active.\n"
|
|
"Direction and operation can be concatenated like --pull-new, etc.\n"
|
|
"--create, --remove, and --expunge can be suffixed with -far/-near.\n"
|
|
"See the man page for details.\n"
|
|
"\nSupported mailbox formats are: IMAP4rev1, Maildir\n"
|
|
"\nCompile time options:\n"
|
|
#ifdef HAVE_LIBSSL
|
|
" +HAVE_LIBSSL"
|
|
#else
|
|
" -HAVE_LIBSSL"
|
|
#endif
|
|
#ifdef HAVE_LIBSASL
|
|
" +HAVE_LIBSASL"
|
|
#else
|
|
" -HAVE_LIBSASL"
|
|
#endif
|
|
#ifdef HAVE_LIBZ
|
|
" +HAVE_LIBZ"
|
|
#else
|
|
" -HAVE_LIBZ"
|
|
#endif
|
|
#ifdef USE_DB
|
|
" +USE_DB"
|
|
#else
|
|
" -USE_DB"
|
|
#endif
|
|
#ifdef HAVE_IPV6
|
|
" +HAVE_IPV6\n"
|
|
#else
|
|
" -HAVE_IPV6\n"
|
|
#endif
|
|
, code ? stderr : stdout );
|
|
exit( code );
|
|
}
|
|
|
|
#ifdef __linux__
|
|
static void ATTR_NORETURN
|
|
crashHandler( int n )
|
|
{
|
|
int dpid;
|
|
char pbuf[10], pabuf[20];
|
|
|
|
close( 0 );
|
|
open( "/dev/tty", O_RDWR );
|
|
dup2( 0, 1 );
|
|
dup2( 0, 2 );
|
|
error( "*** " EXE " caught signal %d. Starting debugger ...\n", n );
|
|
#ifdef PR_SET_PTRACER
|
|
int pip[2];
|
|
if (pipe( pip ) < 0) {
|
|
perror( "pipe()" );
|
|
exit( 3 );
|
|
}
|
|
#endif
|
|
switch ((dpid = fork())) {
|
|
case -1:
|
|
perror( "fork()" );
|
|
break;
|
|
case 0:
|
|
#ifdef PR_SET_PTRACER
|
|
close( pip[1] );
|
|
read( pip[0], pbuf, 1 );
|
|
close( pip[0] );
|
|
#endif
|
|
sprintf( pbuf, "%d", Pid );
|
|
sprintf( pabuf, "/proc/%d/exe", Pid );
|
|
execlp( "gdb", "gdb", pabuf, pbuf, (char *)0 );
|
|
perror( "execlp()" );
|
|
_exit( 1 );
|
|
default:
|
|
#ifdef PR_SET_PTRACER
|
|
prctl( PR_SET_PTRACER, (ulong)dpid );
|
|
close( pip[1] );
|
|
close( pip[0] );
|
|
#endif
|
|
waitpid( dpid, NULL, 0 );
|
|
break;
|
|
}
|
|
exit( 3 );
|
|
}
|
|
#endif
|
|
|
|
void
|
|
countStep( void )
|
|
{
|
|
if (!--JLimit)
|
|
exit( 100 );
|
|
}
|
|
|
|
int
|
|
main( int argc, char **argv )
|
|
{
|
|
core_vars_t mvars[1];
|
|
char *config = NULL, *opt, *ochar;
|
|
int oind, cops = 0, op, ms_warn = 0;
|
|
|
|
tzset();
|
|
gethostname( Hostname, sizeof(Hostname) );
|
|
if ((ochar = strchr( Hostname, '.' )))
|
|
*ochar = 0;
|
|
Pid = getpid();
|
|
if (!(Home = getenv("HOME"))) {
|
|
fputs( "Fatal: $HOME not set\n", stderr );
|
|
return 1;
|
|
}
|
|
arc4_init();
|
|
|
|
memset( mvars, 0, sizeof(*mvars) );
|
|
|
|
for (oind = 1, ochar = NULL; ; ) {
|
|
if (!ochar || !*ochar) {
|
|
if (oind >= argc)
|
|
break;
|
|
if (argv[oind][0] != '-')
|
|
break;
|
|
if (argv[oind][1] == '-') {
|
|
opt = argv[oind++] + 2;
|
|
if (!*opt)
|
|
break;
|
|
if (!strcmp( opt, "config" )) {
|
|
if (oind >= argc) {
|
|
error( "--config requires an argument.\n" );
|
|
return 1;
|
|
}
|
|
config = argv[oind++];
|
|
} else if (starts_with( opt, -1, "config=", 7 )) {
|
|
config = opt + 7;
|
|
} else if (!strcmp( opt, "all" )) {
|
|
mvars->all = 1;
|
|
} else if (!strcmp( opt, "list" )) {
|
|
mvars->list = 1;
|
|
} else if (!strcmp( opt, "list-stores" )) {
|
|
mvars->list_stores = 1;
|
|
} else if (!strcmp( opt, "help" )) {
|
|
usage( 0 );
|
|
} else if (!strcmp( opt, "version" )) {
|
|
version();
|
|
} else if (!strcmp( opt, "quiet" )) {
|
|
if (Verbosity > VERYQUIET)
|
|
Verbosity--;
|
|
} else if (!strcmp( opt, "verbose" )) {
|
|
Verbosity = VERBOSE;
|
|
} else if (starts_with( opt, -1, "debug", 5 )) {
|
|
opt += 5;
|
|
if (!*opt)
|
|
op = DEBUG_ALL;
|
|
else if (!strcmp( opt, "-crash" ))
|
|
op = DEBUG_CRASH;
|
|
else if (!strcmp( opt, "-driver" ))
|
|
op = DEBUG_DRV;
|
|
else if (!strcmp( opt, "-driver-all" ))
|
|
op = DEBUG_DRV | DEBUG_DRV_ALL;
|
|
else if (!strcmp( opt, "-maildir" ))
|
|
op = DEBUG_MAILDIR;
|
|
else if (!strcmp( opt, "-main" ))
|
|
op = DEBUG_MAIN;
|
|
else if (!strcmp( opt, "-net" ))
|
|
op = DEBUG_NET;
|
|
else if (!strcmp( opt, "-net-all" ))
|
|
op = DEBUG_NET | DEBUG_NET_ALL;
|
|
else if (!strcmp( opt, "-sync" ))
|
|
op = DEBUG_SYNC;
|
|
else
|
|
goto badopt;
|
|
DFlags |= op;
|
|
} else if (!strcmp( opt, "pull" )) {
|
|
cops |= XOP_PULL, mvars->ops[F] |= XOP_HAVE_TYPE;
|
|
} else if (!strcmp( opt, "push" )) {
|
|
cops |= XOP_PUSH, mvars->ops[F] |= XOP_HAVE_TYPE;
|
|
} else if (starts_with( opt, -1, "create", 6 )) {
|
|
opt += 6;
|
|
op = OP_CREATE|XOP_HAVE_CREATE;
|
|
lcop:
|
|
if (!*opt)
|
|
cops |= op;
|
|
else if (!strcmp( opt, "-far" ))
|
|
mvars->ops[F] |= op;
|
|
else if (!strcmp( opt, "-master" )) // Pre-1.4 legacy
|
|
mvars->ops[F] |= op, ms_warn = 1;
|
|
else if (!strcmp( opt, "-near" ))
|
|
mvars->ops[N] |= op;
|
|
else if (!strcmp( opt, "-slave" )) // Pre-1.4 legacy
|
|
mvars->ops[N] |= op, ms_warn = 1;
|
|
else
|
|
goto badopt;
|
|
mvars->ops[F] |= op & (XOP_HAVE_CREATE | XOP_HAVE_REMOVE | XOP_HAVE_EXPUNGE);
|
|
} else if (starts_with( opt, -1, "remove", 6 )) {
|
|
opt += 6;
|
|
op = OP_REMOVE|XOP_HAVE_REMOVE;
|
|
goto lcop;
|
|
} else if (starts_with( opt, -1, "expunge", 7 )) {
|
|
opt += 7;
|
|
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
|
|
goto lcop;
|
|
} else if (!strcmp( opt, "no-expunge" )) {
|
|
mvars->ops[F] |= XOP_EXPUNGE_NOOP | XOP_HAVE_EXPUNGE;
|
|
} else if (!strcmp( opt, "no-create" )) {
|
|
mvars->ops[F] |= XOP_CREATE_NOOP | XOP_HAVE_CREATE;
|
|
} else if (!strcmp( opt, "no-remove" )) {
|
|
mvars->ops[F] |= XOP_REMOVE_NOOP | XOP_HAVE_REMOVE;
|
|
} else if (!strcmp( opt, "full" )) {
|
|
mvars->ops[F] |= XOP_HAVE_TYPE | XOP_PULL | XOP_PUSH;
|
|
} else if (!strcmp( opt, "noop" )) {
|
|
mvars->ops[F] |= XOP_TYPE_NOOP | XOP_HAVE_TYPE;
|
|
} else if (starts_with( opt, -1, "pull", 4 )) {
|
|
op = XOP_PULL;
|
|
lcac:
|
|
opt += 4;
|
|
if (!*opt) {
|
|
cops |= op;
|
|
} else if (*opt == '-') {
|
|
opt++;
|
|
goto rlcac;
|
|
} else {
|
|
goto badopt;
|
|
}
|
|
} else if (starts_with( opt, -1, "push", 4 )) {
|
|
op = XOP_PUSH;
|
|
goto lcac;
|
|
} else {
|
|
op = 0;
|
|
rlcac:
|
|
if (!strcmp( opt, "new" )) {
|
|
op |= OP_NEW;
|
|
} else if (!strcmp( opt, "renew" )) {
|
|
op |= OP_RENEW;
|
|
} else if (!strcmp( opt, "delete" )) {
|
|
op |= OP_DELETE;
|
|
} else if (!strcmp( opt, "flags" )) {
|
|
op |= OP_FLAGS;
|
|
} else {
|
|
badopt:
|
|
error( "Unknown option '%s'\n", argv[oind - 1] );
|
|
return 1;
|
|
}
|
|
switch (op & XOP_MASK_DIR) {
|
|
case XOP_PULL: mvars->ops[N] |= op & OP_MASK_TYPE; break;
|
|
case XOP_PUSH: mvars->ops[F] |= op & OP_MASK_TYPE; break;
|
|
default: cops |= op; break;
|
|
}
|
|
mvars->ops[F] |= XOP_HAVE_TYPE;
|
|
}
|
|
continue;
|
|
}
|
|
ochar = argv[oind++] + 1;
|
|
if (!*ochar) {
|
|
error( "Invalid option '-'\n" );
|
|
return 1;
|
|
}
|
|
}
|
|
switch (*ochar++) {
|
|
case 'a':
|
|
mvars->all = 1;
|
|
break;
|
|
case 'l':
|
|
if (*ochar == 's')
|
|
mvars->list_stores = 1, ochar++;
|
|
else
|
|
mvars->list = 1;
|
|
break;
|
|
case 'c':
|
|
if (oind >= argc) {
|
|
error( "-c requires an argument.\n" );
|
|
return 1;
|
|
}
|
|
config = argv[oind++];
|
|
break;
|
|
case 'C':
|
|
op = OP_CREATE|XOP_HAVE_CREATE;
|
|
cop:
|
|
if (*ochar == 'f')
|
|
mvars->ops[F] |= op, ochar++;
|
|
else if (*ochar == 'm') // Pre-1.4 legacy
|
|
mvars->ops[F] |= op, ms_warn = 1, ochar++;
|
|
else if (*ochar == 'n')
|
|
mvars->ops[N] |= op, ochar++;
|
|
else if (*ochar == 's') // Pre-1.4 legacy
|
|
mvars->ops[N] |= op, ms_warn = 1, ochar++;
|
|
else if (*ochar == '-')
|
|
ochar++;
|
|
else
|
|
cops |= op;
|
|
mvars->ops[F] |= op & (XOP_HAVE_CREATE | XOP_HAVE_REMOVE | XOP_HAVE_EXPUNGE);
|
|
break;
|
|
case 'R':
|
|
op = OP_REMOVE|XOP_HAVE_REMOVE;
|
|
goto cop;
|
|
case 'X':
|
|
op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
|
|
goto cop;
|
|
case 'F':
|
|
cops |= XOP_PULL|XOP_PUSH;
|
|
mvars->ops[F] |= XOP_HAVE_TYPE;
|
|
break;
|
|
case '0':
|
|
mvars->ops[F] |= XOP_TYPE_NOOP | XOP_HAVE_TYPE;
|
|
break;
|
|
case 'n':
|
|
case 'd':
|
|
case 'f':
|
|
case 'N':
|
|
--ochar;
|
|
op = 0;
|
|
cac:
|
|
for (;; ochar++) {
|
|
if (*ochar == 'n')
|
|
op |= OP_NEW;
|
|
else if (*ochar == 'd')
|
|
op |= OP_DELETE;
|
|
else if (*ochar == 'f')
|
|
op |= OP_FLAGS;
|
|
else if (*ochar == 'N')
|
|
op |= OP_RENEW;
|
|
else
|
|
break;
|
|
}
|
|
if (op & OP_MASK_TYPE) {
|
|
switch (op & XOP_MASK_DIR) {
|
|
case XOP_PULL: mvars->ops[N] |= op & OP_MASK_TYPE; break;
|
|
case XOP_PUSH: mvars->ops[F] |= op & OP_MASK_TYPE; break;
|
|
default: cops |= op; break;
|
|
}
|
|
} else {
|
|
cops |= op;
|
|
}
|
|
mvars->ops[F] |= XOP_HAVE_TYPE;
|
|
break;
|
|
case 'L':
|
|
op = XOP_PULL;
|
|
goto cac;
|
|
case 'H':
|
|
op = XOP_PUSH;
|
|
goto cac;
|
|
case 'q':
|
|
if (Verbosity > VERYQUIET)
|
|
Verbosity--;
|
|
break;
|
|
case 'V':
|
|
Verbosity = VERBOSE;
|
|
break;
|
|
case 'D':
|
|
for (op = 0; *ochar; ochar++) {
|
|
switch (*ochar) {
|
|
case 'C':
|
|
op |= DEBUG_CRASH;
|
|
break;
|
|
case 'd':
|
|
op |= DEBUG_DRV;
|
|
break;
|
|
case 'D':
|
|
op |= DEBUG_DRV | DEBUG_DRV_ALL;
|
|
break;
|
|
case 'm':
|
|
op |= DEBUG_MAILDIR;
|
|
break;
|
|
case 'M':
|
|
op |= DEBUG_MAIN;
|
|
break;
|
|
case 'n':
|
|
op |= DEBUG_NET;
|
|
break;
|
|
case 'N':
|
|
op |= DEBUG_NET | DEBUG_NET_ALL;
|
|
break;
|
|
case 's':
|
|
op |= DEBUG_SYNC;
|
|
break;
|
|
default:
|
|
error( "Unknown -D flag '%c'\n", *ochar );
|
|
return 1;
|
|
}
|
|
}
|
|
if (!op)
|
|
op = DEBUG_ALL;
|
|
DFlags |= op;
|
|
break;
|
|
case 'T':
|
|
for (; *ochar; ) {
|
|
switch (*ochar++) {
|
|
case 'a':
|
|
DFlags |= FORCEASYNC(F);
|
|
break;
|
|
case 'A':
|
|
DFlags |= FORCEASYNC(F) | FORCEASYNC(N);
|
|
break;
|
|
case 'j':
|
|
DFlags |= KEEPJOURNAL;
|
|
break;
|
|
case 'J':
|
|
DFlags |= FORCEJOURNAL;
|
|
break;
|
|
case 's':
|
|
JLimit = strtol( ochar, &ochar, 10 );
|
|
break;
|
|
case 'x':
|
|
DFlags |= FAKEEXPUNGE;
|
|
break;
|
|
case 'z':
|
|
DFlags |= ZERODELAY;
|
|
break;
|
|
default:
|
|
error( "Unknown -T flag '%c'\n", *(ochar - 1) );
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
case 'v':
|
|
version();
|
|
case 'h':
|
|
usage( 0 );
|
|
default:
|
|
error( "Unknown option '-%c'\n", *(ochar - 1) );
|
|
return 1;
|
|
}
|
|
}
|
|
if (ms_warn)
|
|
warn( "Notice: -master/-slave/m/s suffixes are deprecated; use -far/-near/f/n instead.\n" );
|
|
|
|
if (DFlags & DEBUG_ANY) {
|
|
Verbosity = VERBOSE;
|
|
|
|
fputs( PACKAGE " " VERSION " called with:", stdout );
|
|
for (op = 1; op < argc; op++)
|
|
printf( " '%s'", argv[op] );
|
|
puts( "" );
|
|
} else if (Verbosity >= TERSE && isatty( 1 )) {
|
|
DFlags |= PROGRESS;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
if (DFlags & DEBUG_CRASH) {
|
|
signal( SIGSEGV, crashHandler );
|
|
signal( SIGBUS, crashHandler );
|
|
signal( SIGILL, crashHandler );
|
|
}
|
|
#endif
|
|
|
|
if (merge_ops( cops, mvars->ops, NULL ))
|
|
return 1;
|
|
|
|
if (load_config( config ))
|
|
return 1;
|
|
|
|
if (mvars->list_stores)
|
|
list_stores( mvars, argv + oind );
|
|
else
|
|
sync_chans( mvars, argv + oind );
|
|
return mvars->ret;
|
|
}
|