isync/src/main.c
Oswald Buddenhagen fbc563e4cb autotest skipping doomed messages more thoroughly
ensure that the messages are actually skipped, not subsequently
expunged.
2022-06-19 16:09:55 +02:00

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;
}