Compare commits

..

No commits in common. "65cd4429bb04fafadaed702cff342b7a96678fd0" and "c9e57161cc6f605282b084d63cfd19531576de20" have entirely different histories.

9 changed files with 122 additions and 362 deletions

108
AUTHORS
View File

@ -1,101 +1,17 @@
Contact
=======
Oswald Buddenhagen <ossi@users.sf.net>
* Contributor, current maintainer
Theodore Ts'o <tytso@mit.edu>
* Contributor, Debian package co-maintainer
Nicolas Boullis <nboullis@debian.org>
* Debian package maintainer and minor upstream contributions
Michael Elkins <me@mutt.org>
* Original author
Send questions and bug reports to the isync-devel@lists.sourceforge.net
mailing list.
Do _NOT_ report bugs to Michael, not even in a CC: - he is not actively
_DON'T_ report bugs to Michael, not even in a CC: - he is not actively
involved in isync development any more.
Lead Developers
===============
Oswald Buddenhagen <ossi@users.sf.net>
- Current maintainer
Michael Elkins <me@mutt.org>
- Original author
Contributors
============
(Some of these people also contributed bugfixes and optimizations.)
(In chronological order.)
Jeremy Katz <katzj@linuxpower.org>
- UseNamespace & UseSSL* options
Daniel Resare <noa@metamatrix.se>
- Numerous SSL handling improvements
Eivind Eklund <eivind@FreeBSD.org>
- MaxMessages option
Theodore Ts'o <tytso@mit.edu>
- get-cert script
- Maildir UID mapping improvements
- Initial version of partial async IMAP support
Marc Hoersken <info@marc-hoersken.de>
- CopyArrivalDate option
Jack Stone <jwjstone@fastmail.fm>
Jan Synacek <jsynacek@redhat.com>
- SASL support
Jesse Weaver <pianohacker@gmail.com>
- IMAP stream compression support
Anton Khirnov <anton@khirnov.net>
- ClientKey & ClientCertificate options
Michael J Gruber <github@grubix.eu>
- Support for the $Forwarded/Passed flag
Patrick Steinhardt <ps@pks.im>
- UserCmd option
Oliver Runge <oliver.runge@gmail.com>
- UseKeychain option
Georgy Kibardin <georgy@kibardin.name>
- Support for UTF-7 IMAP mailbox names
Honorary Contributors
=====================
(These people contributed patches that were too small or obvious
to claim copyright, or were rewritten from scratch.)
(In alphabetical order.)
Alessandro Ghedini <ghedo@debian.org>
Andreas Grapentin <andreas@grapentin.org>
Aurélien Francillon <aurelien.francillon@eurecom.fr>
Ben Kibbey <bjk@luxsci.net>
Caspar Schutijser <caspar@schutijser.com>
Cedric Ware <cedric.ware__bml@normalesup.org>
Dmitrij D. Czarkoff <czarkoff@gmail.com>
Dmitry Torokhov <dtor@chromium.org>
Felipe Contreras <felipe.contreras@gmail.com>
Felix Janda <felix.janda@posteo.de>
Gergely Risko <gergely@risko.hu>
Sung Pae "guns" <self@sungpae.com>
Helmut Grohne <helmut@subdivi.de>
Hugo Haas <hugo@larve.net>
Jaroslav Suchanek <jaroslav.suchanek@gmail.com>
Jeremie Courreges-Anglas <jca@openbsd.org>
Klemens Nanni <kn@openbsd.org>
Lorenzo Martignoni <lorenzo.martignoni@technologist.com>
Magnus Jonsson <bigfoot@acc.umu.se>
Marcin Niestroj <macius1990w@gmail.com>
Martin Stenberg <martin@gnutiken.se>
Mike Delaney <mdelan@lusars.net>
Nicolas Boullis <nboullis@debian.org>
Nihal Jere <nihal@nihaljere.xyz>
Reimar Döffinger <Reimar.Doeffinger@gmx.de>
Remko Tronçon <remko@el-tramo.be>
sbfnk@users.sf.net
Thomas Roessler <roessler@does-not-exist.org>
Todd T. Fries <todd@fries.net>
Vincent Bernat <vincent@bernat.ch>
Yuri D'Elia <wavexx@thregr.org>

View File

@ -89,26 +89,6 @@ PACKAGE " " VERSION " - mailbox synchronizer\n"
exit( code );
}
static int child_pipe[2];
static notifier_t child_notifier;
static void
childHandler( int n ATTR_UNUSED )
{
// We can't just reap everything here, as we might steal children
// from popen(). Let the main loop handle it synchronously instead.
char dummy = 0;
write( child_pipe[1], &dummy, 1 );
}
static void
childReaper( int events ATTR_UNUSED, void *aux ATTR_UNUSED )
{
char dummy;
while (read( child_pipe[0], &dummy, 1 ) == 1) {}
while (waitpid( -1, NULL, WNOHANG ) > 0) {}
}
#ifdef __linux__
static void ATTR_NORETURN
crashHandler( int n )
@ -561,31 +541,9 @@ main( int argc, char **argv )
if (load_config( config ))
return 1;
signal( SIGPIPE, SIG_IGN );
if (pipe( child_pipe )) {
perror( "pipe" );
return 1;
}
fcntl( child_pipe[0], F_SETFL, O_NONBLOCK );
fcntl( child_pipe[1], F_SETFL, O_NONBLOCK );
init_notifier( &child_notifier, child_pipe[0], childReaper, NULL );
conf_notifier( &child_notifier, 0, POLLIN );
struct sigaction sa = { 0 };
sa.sa_handler = childHandler;
sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
sigaction( SIGCHLD, &sa, NULL );
if (mvars->list_stores)
list_stores( mvars, argv + oind );
else
sync_chans( mvars, argv + oind );
return mvars->ret;
}
void
cleanup_mainloop( void )
{
cleanup_drivers();
wipe_notifier( &child_notifier );
}

View File

@ -126,7 +126,7 @@ do_list_stores( list_vars_t *lvars )
next:
advance_store( lvars );
}
cleanup_mainloop();
cleanup_drivers();
}
static void

View File

@ -21,6 +21,5 @@ typedef struct {
void sync_chans( core_vars_t *cvars, char **argv );
void list_stores( core_vars_t *cvars, char **argv );
void cleanup_mainloop( void );
#endif

View File

@ -43,11 +43,9 @@ print_stats( void )
static void
stats_timeout( void *aux ATTR_UNUSED )
{
if (stats_steps != -1) {
stats_steps = -1;
print_stats();
}
conf_wakeup( &stats_wakeup, 200 );
print_stats();
}
void
@ -57,10 +55,8 @@ stats( void )
return;
// If the main loop appears to be running, skip the sync path.
if (stats_steps < 0) {
stats_steps = -2;
if (stats_steps < 0)
return;
}
// Rate-limit the (somewhat) expensive timer queries.
if (++stats_steps < 10)
@ -496,7 +492,7 @@ do_sync_chans( main_vars_t *mvars )
next:
advance_chan( mvars );
}
cleanup_mainloop();
cleanup_drivers();
if (!mvars->cvars->list && (DFlags & PROGRESS))
wipe_wakeup( &stats_wakeup );
}
@ -672,7 +668,7 @@ static void
do_sync_boxes( main_vars_t *mvars )
{
mvars->box_cben = 0;
while (mvars->state[F] == ST_OPEN && mvars->state[N] == ST_OPEN) {
for (;;) {
if (mvars->chanptr->boxlist) {
box_ent_t *mbox = mvars->boxptr;
if (!mbox)
@ -734,11 +730,13 @@ done_sync( int sts, void *aux )
stats();
if (sts) {
mvars->cvars->ret = 1;
if (sts & (SYNC_BAD(F) | SYNC_BAD(N))) {
if (sts & SYNC_BAD(F))
mvars->state[F] = ST_CLOSED;
if (sts & SYNC_BAD(N))
mvars->state[N] = ST_CLOSED;
}
}
mvars->box_done = 1;
if (mvars->box_cben)
do_sync_boxes( mvars );

View File

@ -36,7 +36,6 @@ Multiple replicas of each mailbox can be maintained.
Read configuration from \fIfile\fR.
By default, the configuration is read from $XDG_CONFIG_HOME/isyncrc, and
if that does not exist, ~/.mbsyncrc is tried in turn.
$XDG_CONFIG_HOME defaults to ~/.config if not set.
.TP
\fB-a\fR, \fB--all\fR
Select all configured Channels. Any Channel/Group specifications on the
@ -81,10 +80,8 @@ operations are determined, but no modifications are actually made
to either the mailboxes or the state files.
.TP
\fB-e\fR, \fB--ext-exit\fR
Return an extended exit code: Add 32 or 64 to the code if any
modifications were made on the far or near side, respectively; these
are not mutually exclusive, so the code may be 96 if changes were both
pushed and pulled.
Return an extended exit code: Add 32 resp. 64 to the code if any
modifications were made on the far resp. near side.
An error may be reported at the same time, so the code may be for example
65 if some changes were successfully pulled, while others failed.
.TP
@ -733,8 +730,7 @@ the appended string is made up according to the pattern
(see also \fBFieldDelimiter\fR below).
.br
(Global default: \fI$XDG_STATE_HOME/isync/\fR, with a fallback to
\fI~/.mbsync/\fR if only that exists.
$XDG_STATE_HOME defaults to ~/.local/state if not set.)
\fI~/.mbsync/\fR if only that exists)
.
.SS Groups
.TP
@ -860,11 +856,11 @@ There is no risk as long as the IMAP mailbox is accessed by only one client
.
.SH FILES
.TP
\fB$XDG_CONFIG_HOME/isyncrc\fR (usually \fB~/.config/isyncrc\fR)
.B $XDG_CONFIG_HOME/isyncrc
Default configuration file.
See also the example file in the documentation directory.
.TP
\fB$XDG_STATE_HOME/isync/\fR (usually \fB~/.local/state/isync/\fR)
.B $XDG_STATE_HOME/isync/
Directory containing synchronization state files.
.TP
.B ~/.mbsyncrc

View File

@ -28,7 +28,6 @@
#endif
enum {
SCK_RESOLVING,
SCK_CONNECTING,
#ifdef HAVE_LIBSSL
SCK_STARTTLS,
@ -416,7 +415,6 @@ static void socket_fd_cb( int, void * );
static void socket_fake_cb( void * );
static void socket_timeout_cb( void * );
static void socket_resolve( conn_t * );
static void socket_connect_one( conn_t * );
static void socket_connect_next( conn_t * );
static void socket_connect_failed( conn_t * );
@ -424,21 +422,15 @@ static void socket_connected( conn_t * );
static void socket_connect_bail( conn_t * );
static void
socket_register_internal( conn_t *sock, int fd )
socket_open_internal( conn_t *sock, int fd )
{
sock->fd = fd;
fcntl( fd, F_SETFL, O_NONBLOCK );
init_notifier( &sock->notify, fd, socket_fd_cb, sock );
init_wakeup( &sock->fd_fake, socket_fake_cb, sock );
init_wakeup( &sock->fd_timeout, socket_timeout_cb, sock );
}
static void
socket_open_internal( conn_t *sock, int fd )
{
fcntl( fd, F_SETFL, O_NONBLOCK );
socket_register_internal( sock, fd );
}
static void
socket_close_internal( conn_t *sock )
{
@ -449,6 +441,32 @@ socket_close_internal( conn_t *sock )
sock->fd = -1;
}
#ifndef HAVE_IPV6
struct addr_info {
struct addr_info *ai_next;
struct sockaddr_in ai_addr[1];
};
#define freeaddrinfo(ai) free( ai )
static struct addr_info *
init_addrinfo( struct hostent *he )
{
uint naddr = 0;
for (char **addr = he->h_addr_list; *addr; addr++)
naddr++;
struct addr_info *caddr = nfzalloc( naddr * sizeof(struct addrinfo) );
struct addr_info *ret, **caddrp = &ret;
for (char **addr = he->h_addr_list; *addr; addr++, caddr++) {
caddr->ai_addr->sin_family = AF_INET;
memcpy( &caddr->ai_addr->sin_addr.s_addr, *addr, sizeof(struct in_addr) );
*caddrp = caddr;
caddrp = &caddr->ai_next;
}
return ret;
}
#endif
void
socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
{
@ -483,202 +501,77 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
info( "\vok\n" );
socket_connected( sock );
} else {
socket_resolve( sock );
}
}
static void
pipe_write( int fd, void *buf, int len )
{
do {
int wrote = write( fd, buf, len );
if (wrote < 0) {
perror( "write" );
_exit( 1 );
}
buf = ((char *)buf) + wrote;
len -= wrote;
} while (len);
}
static void
socket_resolve( conn_t *sock )
{
info( "Resolving %s...\n", sock->conf->host );
int pfd[2];
if (pipe( pfd )) {
perror( "pipe" );
exit( 1 );
}
switch (fork()) {
case -1:
perror( "fork" );
exit( 1 );
case 0:
break;
default:
close( pfd[1] );
socket_register_internal( sock, pfd[0] );
sock->state = SCK_RESOLVING;
conf_notifier( &sock->notify, 0, POLLIN );
socket_expect_activity( sock, 1 );
return;
}
#ifdef HAVE_IPV6
struct addrinfo *res, hints = { 0 };
int gaierr;
struct addrinfo hints;
memset( &hints, 0, sizeof(hints) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG;
int gaierr = getaddrinfo( sock->conf->host, NULL, &hints, &res );
pipe_write( pfd[1], &gaierr, sizeof(gaierr) );
if (gaierr)
_exit( 1 );
static_assert( sizeof(((struct addrinfo){ 0 }).ai_family) == sizeof(int), "unexpected size of ai_family" );
static_assert( sizeof(struct in_addr) % sizeof(int) == 0, "unexpected size of struct in_addr" );
static_assert( sizeof(struct in6_addr) % sizeof(int) == 0, "unexpected size of struct in6_addr" );
int nbytes = 0;
for (struct addrinfo *cres = res; cres; cres = cres->ai_next) {
if (cres->ai_family == AF_INET) {
nbytes += sizeof(int) + sizeof(struct in_addr);
} else {
assert( cres->ai_family == AF_INET6 );
nbytes += sizeof(int) + sizeof(struct in6_addr);
}
}
pipe_write( pfd[1], &nbytes, sizeof(nbytes) );
for (struct addrinfo *cres = res; cres; cres = cres->ai_next) {
pipe_write( pfd[1], &cres->ai_family, sizeof(int) );
if (cres->ai_family == AF_INET)
pipe_write( pfd[1], &((struct sockaddr_in *)cres->ai_addr)->sin_addr, sizeof(struct in_addr) );
else
pipe_write( pfd[1], &((struct sockaddr_in6 *)cres->ai_addr)->sin6_addr, sizeof(struct in6_addr) );
}
#else
struct hostent *he = gethostbyname( sock->conf->host );
int herrno = he ? 0 : h_errno;
pipe_write( pfd[1], &herrno, sizeof(herrno) );
if (!he)
_exit( 1 );
static_assert( sizeof(struct in_addr) % sizeof(int) == 0, "unexpected size of struct in_addr" );
int nbytes = 0;
for (char **addr = he->h_addr_list; *addr; addr++)
nbytes += sizeof(struct in_addr);
pipe_write( pfd[1], &nbytes, sizeof(nbytes) );
for (char **addr = he->h_addr_list; *addr; addr++)
pipe_write( pfd[1], *addr, sizeof(struct in_addr) );
#endif
_exit( 0 );
}
static void
pipe_read( int fd, void *buf, int len )
{
do {
int didrd = read( fd, buf, len );
if (didrd < 0) {
sys_error( "read" );
exit( 1 );
}
if (!didrd) {
error( "read: unexpected EOF\n" );
exit( 1 );
}
buf = ((char *)buf) + didrd;
len -= didrd;
} while (len);
}
static void
socket_resolve_finalize( conn_t *sock )
{
int errcode;
pipe_read( sock->fd, &errcode, sizeof(errcode) );
if (errcode) {
#ifdef HAVE_IPV6
const char *err = gai_strerror( errcode );
#else
const char *err = hstrerror( errcode );
#endif
error( "Error: Cannot resolve server '%s': %s\n", sock->conf->host, err );
socket_close_internal( sock );
infon( "Resolving %s... ", conf->host );
if ((gaierr = getaddrinfo( conf->host, NULL, &hints, &sock->addrs ))) {
error( "Error: Cannot resolve server '%s': %s\n", conf->host, gai_strerror( gaierr ) );
socket_connect_bail( sock );
return;
}
info( "\vok\n" );
#else
struct hostent *he;
int nbytes;
pipe_read( sock->fd, &nbytes, sizeof(nbytes) );
char *addrs = nfmalloc( nbytes );
pipe_read( sock->fd, addrs, nbytes );
sock->curr_addr = sock->addrs = addrs;
sock->addrs_end = addrs + nbytes;
socket_close_internal( sock ); // Get rid of the pipe
socket_connect_one( sock );
}
static void
socket_resolve_timeout( conn_t *sock )
{
error( "Error: Cannot resolve server '%s': timeout.\n", sock->conf->host );
socket_close_internal( sock );
infon( "Resolving %s... ", conf->host );
he = gethostbyname( conf->host );
if (!he) {
error( "Error: Cannot resolve server '%s': %s\n", conf->host, hstrerror( h_errno ) );
socket_connect_bail( sock );
return;
}
info( "\vok\n" );
sock->addrs = init_addrinfo( he );
#endif
sock->curr_addr = sock->addrs;
socket_connect_one( sock );
}
}
static void
socket_connect_one( conn_t *sock )
{
char *ai = sock->curr_addr;
if (ai == sock->addrs_end) {
int s;
#ifdef HAVE_IPV6
struct addrinfo *ai;
#else
struct addr_info *ai;
#endif
if (!(ai = sock->curr_addr)) {
error( "No working address found for %s\n", sock->conf->host );
socket_connect_bail( sock );
return;
}
union {
struct sockaddr any;
struct sockaddr_in ip4;
#ifdef HAVE_IPV6
struct sockaddr_in6 ip6;
#endif
} addr;
#ifdef HAVE_IPV6
int fam = *(int *)ai;
ai += sizeof(int);
int addr_len;
if (fam == AF_INET6) {
addr_len = sizeof(addr.ip6);
addr.ip6.sin6_addr = *(struct in6_addr *)ai;
addr.ip6.sin6_flowinfo = 0;
addr.ip6.sin6_scope_id = 0;
ai += sizeof(struct in6_addr);
} else {
addr_len = sizeof(addr.ip4);
#else
const int fam = AF_INET;
const int addr_len = sizeof(addr.ip4);
{
#endif
addr.ip4.sin_addr = *(struct in_addr *)ai;
ai += sizeof(struct in_addr);
}
sock->curr_addr = ai;
#ifdef HAVE_IPV6
if (fam == AF_INET6) {
if (ai->ai_family == AF_INET6) {
struct sockaddr_in6 *in6 = ((struct sockaddr_in6 *)ai->ai_addr);
char sockname[64];
inet_ntop( fam, &addr.ip6.sin6_addr, sockname, sizeof(sockname) );
in6->sin6_port = htons( sock->conf->port );
nfasprintf( &sock->name, "%s ([%s]:%hu)",
sock->conf->host, sockname, sock->conf->port );
sock->conf->host, inet_ntop( AF_INET6, &in6->sin6_addr, sockname, sizeof(sockname) ), sock->conf->port );
} else
#endif
{
struct sockaddr_in *in = ((struct sockaddr_in *)ai->ai_addr);
in->sin_port = htons( sock->conf->port );
nfasprintf( &sock->name, "%s (%s:%hu)",
sock->conf->host, inet_ntoa( addr.ip4.sin_addr ), sock->conf->port );
sock->conf->host, inet_ntoa( in->sin_addr ), sock->conf->port );
}
int s = socket( fam, SOCK_STREAM, 0 );
#ifdef HAVE_IPV6
s = socket( ai->ai_family, SOCK_STREAM, 0 );
#else
s = socket( PF_INET, SOCK_STREAM, 0 );
#endif
if (s < 0) {
socket_connect_next( sock );
return;
@ -686,9 +579,11 @@ socket_connect_one( conn_t *sock )
socket_open_internal( sock, s );
infon( "Connecting to %s... ", sock->name );
addr.any.sa_family = fam;
addr.ip4.sin_port = htons( sock->conf->port ); // Aliased for ip6
if (connect( s, &addr.any, addr_len )) {
#ifdef HAVE_IPV6
if (connect( s, ai->ai_addr, ai->ai_addrlen )) {
#else
if (connect( s, ai->ai_addr, sizeof(*ai->ai_addr) )) {
#endif
if (errno != EINPROGRESS) {
socket_connect_failed( sock );
return;
@ -709,6 +604,7 @@ socket_connect_next( conn_t *conn )
sys_error( "Cannot connect to %s", conn->name );
free( conn->name );
conn->name = NULL;
conn->curr_addr = conn->curr_addr->ai_next;
socket_connect_one( conn );
}
@ -722,8 +618,10 @@ socket_connect_failed( conn_t *conn )
static void
socket_connected( conn_t *conn )
{
free( conn->addrs );
if (conn->addrs) {
freeaddrinfo( conn->addrs );
conn->addrs = NULL;
}
conf_notifier( &conn->notify, 0, POLLIN );
socket_expect_activity( conn, 0 );
conn->state = SCK_READY;
@ -733,8 +631,10 @@ socket_connected( conn_t *conn )
static void
socket_cleanup_names( conn_t *conn )
{
free( conn->addrs );
if (conn->addrs) {
freeaddrinfo( conn->addrs );
conn->addrs = NULL;
}
free( conn->name );
conn->name = NULL;
}
@ -1206,11 +1106,6 @@ socket_fd_cb( int events, void *aux )
{
conn_t *conn = (conn_t *)aux;
if (conn->state == SCK_RESOLVING) {
socket_resolve_finalize( conn );
return;
}
if ((events & POLLERR) || conn->state == SCK_CONNECTING) {
int soerr;
socklen_t selen = sizeof(soerr);
@ -1273,9 +1168,7 @@ socket_timeout_cb( void *aux )
{
conn_t *conn = (conn_t *)aux;
if (conn->state == SCK_RESOLVING) {
socket_resolve_timeout( conn );
} else if (conn->state == SCK_CONNECTING) {
if (conn->state == SCK_CONNECTING) {
errno = ETIMEDOUT;
socket_connect_failed( conn );
} else {

View File

@ -57,7 +57,11 @@ typedef struct {
int fd;
int state;
const server_conf_t *conf; /* needed during connect */
char *addrs, *addrs_end, *curr_addr; // needed during connect; assumed to be int-aligned
#ifdef HAVE_IPV6
struct addrinfo *addrs, *curr_addr; /* needed during connect */
#else
struct addr_info *addrs, *curr_addr; /* needed during connect */
#endif
char *name;
#ifdef HAVE_LIBSSL
SSL *ssl;

View File

@ -1110,8 +1110,6 @@ event_wait( void )
case 0:
return;
case -1:
if (errno == EINTR)
return;
perror( "poll() failed in event loop" );
abort();
default:
@ -1164,8 +1162,6 @@ event_wait( void )
case 0:
return;
case -1:
if (errno == EINTR)
return;
perror( "select() failed in event loop" );
abort();
default: