compile and run!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

499 lines
12 KiB

// 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
24 years ago
*/
#include "main_p.h"
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#ifdef __linux__
# include <sys/prctl.h>
#endif
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. ;)
23 years ago
static void ATTR_NORETURN
version( void )
24 years ago
{
puts( PACKAGE " " VERSION );
exit( 0 );
24 years ago
}
static void ATTR_NORETURN
usage( int code )
24 years ago
{
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"
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. ;)
23 years ago
"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"
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. ;)
23 years ago
" -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 );
24 years ago
}
#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;
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. ;)
23 years ago
}
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 '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;
}
24 years ago
}
if (ms_warn)
warn( "Notice: -master/-slave/m/s suffixes are deprecated; use -far/-near/f/n instead.\n" );
24 years ago
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;
24 years ago
if (load_config( config ))
return 1;
24 years ago
if (mvars->list_stores)
list_stores( mvars, argv + oind );
else
sync_chans( mvars, argv + oind );
return mvars->ret;
}