Browse Source

unify error reporting

- introduce sys_error() and use it instead of perror() and
  error(strerror()) in all expected error conditions
- perror() is used only for "something's really wrong with the system"
  kind of errors
- file names, etc. are quoted if they are not validated yet, so e.g. an
  empty string becomes immediately obvious
- improve and unify language
- add missing newlines
wip/maildir-uid-dupes-test
Oswald Buddenhagen 14 years ago
parent
commit
d2bed4990d
  1. 2
      TODO
  2. 2
      src/compat/config.c
  3. 20
      src/compat/convert.c
  4. 1
      src/compat/isync.h
  5. 9
      src/compat/main.c
  6. 13
      src/compat/util.c
  7. 3
      src/config.c
  8. 7
      src/drv_imap.c
  9. 99
      src/drv_maildir.c
  10. 3
      src/isync.h
  11. 46
      src/mdconvert.c
  12. 49
      src/socket.c
  13. 10
      src/sync.c
  14. 17
      src/util.c

2
TODO

@ -15,8 +15,6 @@ quotas are weird, they make close() fail.
clarify error cases of transactions. clarify error cases of transactions.
cleanup/improve error messages in the drivers.
sync.c does not consider UID 0 valid, which is wrong. fixing this requires sync.c does not consider UID 0 valid, which is wrong. fixing this requires
an incompatible change or a remapping hack in sync state files. an incompatible change or a remapping hack in sync state files.

2
src/compat/config.c

@ -97,7 +97,7 @@ load_config( const char *path, config_t ***stor )
if (!(fp = fopen( path, "r" ))) { if (!(fp = fopen( path, "r" ))) {
if (errno != ENOENT) if (errno != ENOENT)
perror( "fopen" ); sys_error( "Cannot read config file '%s'", path );
return; return;
} }
if (!Quiet && !Debug && !Verbose) if (!Quiet && !Debug && !Verbose)

20
src/compat/convert.c

@ -28,7 +28,6 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h>
#include <time.h> #include <time.h>
#include <db.h> #include <db.h>
@ -97,10 +96,9 @@ convert( config_t *box )
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
nfsnprintf( buf, sizeof(buf), "%s/%s", mboxdir, subdirs[i] ); nfsnprintf( buf, sizeof(buf), "%s/%s", mboxdir, subdirs[i] );
if (stat( buf, &sb )) { if (stat( buf, &sb )) {
fprintf( stderr, "ERROR: stat %s: %s (errno %d)\n", buf, sys_error( "ERROR: cannot access %s", buf );
strerror(errno), errno );
fprintf( stderr, fprintf( stderr,
"ERROR: %s does not appear to be a valid maildir style mailbox\n", "ERROR: '%s' does not appear to be a valid maildir style mailbox\n",
mboxdir ); mboxdir );
goto err1; goto err1;
} }
@ -111,7 +109,7 @@ convert( config_t *box )
nfsnprintf( sname, sizeof(sname), "%s/.mbsyncstate", mboxdir ); nfsnprintf( sname, sizeof(sname), "%s/.mbsyncstate", mboxdir );
if ((fd = open( ilname, O_WRONLY|O_CREAT, 0600 )) < 0) { if ((fd = open( ilname, O_WRONLY|O_CREAT, 0600 )) < 0) {
perror( ilname ); sys_error( "Cannot create %s", ilname );
goto err1; goto err1;
} }
#if SEEK_SET != 0 #if SEEK_SET != 0
@ -121,20 +119,20 @@ convert( config_t *box )
lck.l_type = F_WRLCK; lck.l_type = F_WRLCK;
#endif #endif
if (fcntl( fd, F_SETLKW, &lck )) { if (fcntl( fd, F_SETLKW, &lck )) {
perror( ilname ); sys_error( "Cannot lock %s", ilname );
err2: err2:
close( fd ); close( fd );
goto err1; goto err1;
} }
if (!(fp = fopen( iuvname, "r" ))) { if (!(fp = fopen( iuvname, "r" ))) {
perror( iuvname ); sys_error( "Cannot open %s", iuvname );
goto err2; goto err2;
} }
fscanf( fp, "%d", &uidval ); fscanf( fp, "%d", &uidval );
fclose( fp ); fclose( fp );
if (!(fp = fopen( imuname, "r" ))) { if (!(fp = fopen( imuname, "r" ))) {
perror( imuname ); sys_error( "Cannot open %s", imuname );
goto err2; goto err2;
} }
fscanf( fp, "%d", &maxuid ); fscanf( fp, "%d", &maxuid );
@ -162,7 +160,7 @@ convert( config_t *box )
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
nfsnprintf( buf, sizeof(buf), "%s/%s/", mboxdir, subdirs[i] ); nfsnprintf( buf, sizeof(buf), "%s/%s/", mboxdir, subdirs[i] );
if (!(d = opendir( buf ))) { if (!(d = opendir( buf ))) {
perror( "opendir" ); sys_error( "Cannot list %s", buf );
err4: err4:
free( msgs ); free( msgs );
if (db) if (db)
@ -199,7 +197,7 @@ convert( config_t *box )
qsort( msgs, nmsgs, sizeof(msg_t), compare_uids ); qsort( msgs, nmsgs, sizeof(msg_t), compare_uids );
if (!(fp = fopen( sname, "w" ))) { if (!(fp = fopen( sname, "w" ))) {
perror( sname ); sys_error( "Cannot create %s", sname );
goto err4; goto err4;
} }
if (box->max_messages) { if (box->max_messages) {
@ -238,7 +236,7 @@ convert( config_t *box )
rename( iumname, diumname ); rename( iumname, diumname );
} else { } else {
if (!(fp = fopen( uvname, "w" ))) { if (!(fp = fopen( uvname, "w" ))) {
perror( uvname ); sys_error( "Cannot create %s", uvname );
goto err4; goto err4;
} }
fprintf( fp, "%d\n%d\n", uidval, maxuid ); fprintf( fp, "%d\n%d\n", uidval, maxuid );

1
src/compat/isync.h

@ -98,4 +98,5 @@ char *nfstrdup( const char *str );
int nfvasprintf( char **str, const char *fmt, va_list va ); int nfvasprintf( char **str, const char *fmt, va_list va );
int nfasprintf( char **str, const char *fmt, ... ); int nfasprintf( char **str, const char *fmt, ... );
int nfsnprintf( char *buf, int blen, const char *fmt, ... ); int nfsnprintf( char *buf, int blen, const char *fmt, ... );
void sys_error( const char *, ... );
void ATTR_NORETURN oob( void ); void ATTR_NORETURN oob( void );

9
src/compat/main.c

@ -29,7 +29,6 @@
#include <limits.h> #include <limits.h>
#include <pwd.h> #include <pwd.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <dirent.h> #include <dirent.h>
@ -336,7 +335,7 @@ main( int argc, char **argv )
struct dirent *de; struct dirent *de;
if (!(dir = opendir( xmaildir ))) { if (!(dir = opendir( xmaildir ))) {
fprintf( stderr, "%s: %s\n", xmaildir, strerror(errno) ); sys_error( "Cannot list '%s'", xmaildir );
return 1; return 1;
} }
while ((de = readdir( dir ))) { while ((de = readdir( dir ))) {
@ -379,13 +378,13 @@ main( int argc, char **argv )
outconfig = path2; outconfig = path2;
} }
if ((fd = creat( outconfig, 0666 )) < 0) { if ((fd = creat( outconfig, 0666 )) < 0) {
fprintf( stderr, "Error: cannot write new config %s: %s\n", outconfig, strerror(errno) ); sys_error( "Error: cannot create config file '%s'", outconfig );
return 1; return 1;
} }
} else { } else {
strcpy( path2, "/tmp/mbsyncrcXXXXXX" ); strcpy( path2, "/tmp/mbsyncrcXXXXXX" );
if ((fd = mkstemp( path2 )) < 0) { if ((fd = mkstemp( path2 )) < 0) {
fprintf( stderr, "Can't create temp file\n" ); sys_error( "Error: cannot create temporary config file" );
return 1; return 1;
} }
} }
@ -432,6 +431,6 @@ main( int argc, char **argv )
add_arg( &args, find_box( argv[optind] )->channel_name ); add_arg( &args, find_box( argv[optind] )->channel_name );
} }
execvp( args[0], args ); execvp( args[0], args );
perror( args[0] ); sys_error( "Cannot execute %s", args[0] );
return 1; return 1;
} }

13
src/compat/util.c

@ -27,6 +27,19 @@
#include <pwd.h> #include <pwd.h>
#include <ctype.h> #include <ctype.h>
void
sys_error( const char *msg, ... )
{
va_list va;
char buf[1024];
va_start( va, msg );
if ((unsigned)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
oob();
va_end( va );
perror( buf );
}
char * char *
next_arg( char **s ) next_arg( char **s )
{ {

3
src/config.c

@ -25,7 +25,6 @@
#include <unistd.h> #include <unistd.h>
#include <limits.h> #include <limits.h>
#include <errno.h>
#include <pwd.h> #include <pwd.h>
#include <sys/types.h> #include <sys/types.h>
#include <string.h> #include <string.h>
@ -272,7 +271,7 @@ load_config( const char *where, int pseudo )
info( "Reading configuration file %s\n", cfile.file ); info( "Reading configuration file %s\n", cfile.file );
if (!(cfile.fp = fopen( cfile.file, "r" ))) { if (!(cfile.fp = fopen( cfile.file, "r" ))) {
perror( "Cannot open config file" ); sys_error( "Cannot open config file '%s'", cfile.file );
return 1; return 1;
} }
buf[sizeof(buf) - 1] = 0; buf[sizeof(buf) - 1] = 0;

7
src/drv_imap.c

@ -30,7 +30,6 @@
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
#include <limits.h> #include <limits.h>
#include <errno.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
@ -841,7 +840,7 @@ imap_socket_read( void *aux )
if (*arg == '*') { if (*arg == '*') {
arg = next_arg( &cmd ); arg = next_arg( &cmd );
if (!arg) { if (!arg) {
error( "IMAP error: unable to parse untagged response\n" ); error( "IMAP error: malformed untagged response\n" );
break; break;
} }
@ -1838,8 +1837,8 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep, int *err )
else if (!strcasecmp( "CertificateFile", cfg->cmd )) { else if (!strcasecmp( "CertificateFile", cfg->cmd )) {
server->sconf.cert_file = expand_strdup( cfg->val ); server->sconf.cert_file = expand_strdup( cfg->val );
if (access( server->sconf.cert_file, R_OK )) { if (access( server->sconf.cert_file, R_OK )) {
error( "%s:%d: CertificateFile '%s': %s\n", sys_error( "%s:%d: CertificateFile '%s'",
cfg->file, cfg->line, server->sconf.cert_file, strerror( errno ) ); cfg->file, cfg->line, server->sconf.cert_file );
*err = 1; *err = 1;
} }
} else if (!strcasecmp( "RequireSSL", cfg->cmd )) } else if (!strcasecmp( "RequireSSL", cfg->cmd ))

99
src/drv_maildir.c

@ -101,7 +101,7 @@ maildir_open_store( store_conf_t *conf,
struct stat st; struct stat st;
if (stat( conf->path, &st ) || !S_ISDIR(st.st_mode)) { if (stat( conf->path, &st ) || !S_ISDIR(st.st_mode)) {
error( "Maildir error: cannot open store %s\n", conf->path ); error( "Maildir error: cannot open store '%s'\n", conf->path );
cb( 0, aux ); cb( 0, aux );
return; return;
} }
@ -173,7 +173,7 @@ maildir_list( store_t *gctx,
struct dirent *de; struct dirent *de;
if (!(dir = opendir( gctx->conf->path ))) { if (!(dir = opendir( gctx->conf->path ))) {
error( "%s: %s\n", gctx->conf->path, strerror(errno) ); sys_error( "Maildir error: cannot list %s", gctx->conf->path );
maildir_invoke_bad_callback( gctx ); maildir_invoke_bad_callback( gctx );
cb( DRV_CANCELED, aux ); cb( DRV_CANCELED, aux );
return; return;
@ -240,16 +240,14 @@ maildir_validate( const char *prefix, const char *box, int create, maildir_store
if (errno == ENOENT) { if (errno == ENOENT) {
if (create) { if (create) {
if (mkdir( buf, 0700 )) { if (mkdir( buf, 0700 )) {
error( "Maildir error: mkdir %s: %s (errno %d)\n", sys_error( "Maildir error: cannot create mailbox '%s'", buf );
buf, strerror(errno), errno );
maildir_invoke_bad_callback( &ctx->gen ); maildir_invoke_bad_callback( &ctx->gen );
return DRV_CANCELED; return DRV_CANCELED;
} }
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
memcpy( buf + bl, subdirs[i], 4 ); memcpy( buf + bl, subdirs[i], 4 );
if (mkdir( buf, 0700 )) { if (mkdir( buf, 0700 )) {
error( "Maildir error: mkdir %s: %s (errno %d)\n", sys_error( "Maildir error: cannot create directory %s", buf );
buf, strerror(errno), errno );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
} }
@ -258,8 +256,7 @@ maildir_validate( const char *prefix, const char *box, int create, maildir_store
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
} else { } else {
error( "Maildir error: stat %s: %s (errno %d)\n", sys_error( "Maildir error: cannot access mailbox '%s'", buf );
buf, strerror(errno), errno );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
} else { } else {
@ -273,8 +270,7 @@ maildir_validate( const char *prefix, const char *box, int create, maildir_store
memcpy( buf + bl, "tmp/", 5 ); memcpy( buf + bl, "tmp/", 5 );
bl += 4; bl += 4;
if (!(dirp = opendir( buf ))) { if (!(dirp = opendir( buf ))) {
error( "Maildir error: opendir: %s: %s (errno %d)\n", sys_error( "Maildir error: cannot list %s", buf );
buf, strerror(errno), errno );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
time( &now ); time( &now );
@ -282,16 +278,14 @@ maildir_validate( const char *prefix, const char *box, int create, maildir_store
nfsnprintf( buf + bl, sizeof(buf) - bl, "%s", entry->d_name ); nfsnprintf( buf + bl, sizeof(buf) - bl, "%s", entry->d_name );
if (stat( buf, &st )) { if (stat( buf, &st )) {
if (errno != ENOENT) if (errno != ENOENT)
error( "Maildir error: stat: %s: %s (errno %d)\n", sys_error( "Maildir error: cannot access %s", buf );
buf, strerror(errno), errno );
} else if (S_ISREG(st.st_mode) && now - st.st_ctime >= _24_HOURS) { } else if (S_ISREG(st.st_mode) && now - st.st_ctime >= _24_HOURS) {
/* this should happen infrequently enough that it won't be /* this should happen infrequently enough that it won't be
* bothersome to the user to display when it occurs. * bothersome to the user to display when it occurs.
*/ */
info( "Maildir notice: removing stale file %s\n", buf ); info( "Maildir notice: removing stale file %s\n", buf );
if (unlink( buf ) && errno != ENOENT) if (unlink( buf ) && errno != ENOENT)
error( "Maildir error: unlink: %s: %s (errno %d)\n", sys_error( "Maildir error: cannot remove %s", buf );
buf, strerror(errno), errno );
} }
} }
closedir( dirp ); closedir( dirp );
@ -360,9 +354,8 @@ maildir_store_uid( maildir_store_t *ctx )
} }
static int static int
maildir_init_uid( maildir_store_t *ctx, const char *msg ) maildir_init_uid( maildir_store_t *ctx )
{ {
info( "Maildir notice: %s.\n", msg ? msg : "cannot read UIDVALIDITY, creating new" );
ctx->gen.uidvalidity = time( 0 ); ctx->gen.uidvalidity = time( 0 );
ctx->nuid = 0; ctx->nuid = 0;
ctx->uvok = 0; ctx->uvok = 0;
@ -376,6 +369,13 @@ maildir_init_uid( maildir_store_t *ctx, const char *msg )
return maildir_store_uid( ctx ); return maildir_store_uid( ctx );
} }
static int
maildir_init_uid_new( maildir_store_t *ctx )
{
info( "Maildir notice: no UIDVALIDITY, creating new.\n" );
return maildir_init_uid( ctx );
}
static int static int
maildir_uidval_lock( maildir_store_t *ctx ) maildir_uidval_lock( maildir_store_t *ctx )
{ {
@ -402,7 +402,7 @@ maildir_uidval_lock( maildir_store_t *ctx )
lseek( ctx->uvfd, 0, SEEK_SET ); lseek( ctx->uvfd, 0, SEEK_SET );
if ((n = read( ctx->uvfd, buf, sizeof(buf) )) <= 0 || if ((n = read( ctx->uvfd, buf, sizeof(buf) )) <= 0 ||
(buf[n] = 0, sscanf( buf, "%d\n%d", &ctx->gen.uidvalidity, &ctx->nuid ) != 2)) { (buf[n] = 0, sscanf( buf, "%d\n%d", &ctx->gen.uidvalidity, &ctx->nuid ) != 2)) {
return maildir_init_uid( ctx, 0 ); return maildir_init_uid_new( ctx );
} else } else
ctx->uvok = 1; ctx->uvok = 1;
return DRV_OK; return DRV_OK;
@ -532,7 +532,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
memcpy( buf + bl, subdirs[i], 4 ); memcpy( buf + bl, subdirs[i], 4 );
if (!(d = opendir( buf ))) { if (!(d = opendir( buf ))) {
perror( buf ); sys_error( "Maildir error: cannot list %s", buf );
#ifdef USE_DB #ifdef USE_DB
if (!ctx->db) if (!ctx->db)
#endif /* USE_DB */ #endif /* USE_DB */
@ -633,7 +633,8 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
entry = &msglist->ents[i]; entry = &msglist->ents[i];
if (entry->uid != INT_MAX) { if (entry->uid != INT_MAX) {
if (uid == entry->uid) { if (uid == entry->uid) {
if ((ret = maildir_init_uid( ctx, "duplicate UID; changing UIDVALIDITY" )) != DRV_OK) { info( "Maildir notice: duplicate UID; changing UIDVALIDITY.\n");
if ((ret = maildir_init_uid( ctx )) != DRV_OK) {
maildir_free_scan( msglist ); maildir_free_scan( msglist );
return ret; return ret;
} }
@ -670,13 +671,14 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
memcpy( nbuf, buf, bl + 4 ); memcpy( nbuf, buf, bl + 4 );
nfsnprintf( nbuf + bl + 4, sizeof(nbuf) - bl - 4, "%s", entry->base ); nfsnprintf( nbuf + bl + 4, sizeof(nbuf) - bl - 4, "%s", entry->base );
if (rename( nbuf, buf )) { if (rename( nbuf, buf )) {
notok:
if (errno != ENOENT) { if (errno != ENOENT) {
perror( buf ); sys_error( "Maildir error: cannot rename %s to %s", nbuf, buf );
fail:
maildir_uidval_unlock( ctx ); maildir_uidval_unlock( ctx );
maildir_free_scan( msglist ); maildir_free_scan( msglist );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
retry:
maildir_free_scan( msglist ); maildir_free_scan( msglist );
goto again; goto again;
} }
@ -685,13 +687,23 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
memcpy( entry->base, buf + bl + 4, fnl ); memcpy( entry->base, buf + bl + 4, fnl );
} }
if (ctx->gen.opts & OPEN_SIZE) { if (ctx->gen.opts & OPEN_SIZE) {
if (stat( buf, &st )) if (stat( buf, &st )) {
goto notok; if (errno != ENOENT) {
sys_error( "Maildir error: cannot stat %s", buf );
goto fail;
}
goto retry;
}
entry->size = st.st_size; entry->size = st.st_size;
} }
if (ctx->gen.opts & OPEN_FIND) { if (ctx->gen.opts & OPEN_FIND) {
if (!(f = fopen( buf, "r" ))) if (!(f = fopen( buf, "r" ))) {
goto notok; if (errno != ENOENT) {
sys_error( "Maildir error: cannot open %s", buf );
goto fail;
}
goto retry;
}
while (fgets( nbuf, sizeof(nbuf), f )) { while (fgets( nbuf, sizeof(nbuf), f )) {
if (!nbuf[0] || nbuf[0] == '\n') if (!nbuf[0] || nbuf[0] == '\n')
break; break;
@ -767,7 +779,7 @@ maildir_select( store_t *gctx, int create,
nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", gctx->path ); nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", gctx->path );
#ifndef USE_DB #ifndef USE_DB
if ((ctx->uvfd = open( uvpath, O_RDWR|O_CREAT, 0600 )) < 0) { if ((ctx->uvfd = open( uvpath, O_RDWR|O_CREAT, 0600 )) < 0) {
perror( uvpath ); sys_error( "Maildir error: cannot write %s", uvpath );
cb( DRV_BOX_BAD, aux ); cb( DRV_BOX_BAD, aux );
return; return;
} }
@ -783,7 +795,7 @@ maildir_select( store_t *gctx, int create,
if ((ctx->uvfd = open( uvpath, O_RDWR|O_CREAT, 0600 )) >= 0) if ((ctx->uvfd = open( uvpath, O_RDWR|O_CREAT, 0600 )) >= 0)
goto fnok; goto fnok;
} }
perror( uvpath ); sys_error( "Maildir error: cannot write %s", uvpath );
cb( DRV_BOX_BAD, aux ); cb( DRV_BOX_BAD, aux );
return; return;
} }
@ -793,7 +805,7 @@ maildir_select( store_t *gctx, int create,
#endif #endif
lck.l_type = F_WRLCK; lck.l_type = F_WRLCK;
if (fcntl( ctx->uvfd, F_SETLKW, &lck )) { if (fcntl( ctx->uvfd, F_SETLKW, &lck )) {
perror( uvpath ); sys_error( "Maildir error: cannot lock %s", uvpath );
bork: bork:
close( ctx->uvfd ); close( ctx->uvfd );
ctx->uvfd = -1; ctx->uvfd = -1;
@ -817,7 +829,7 @@ maildir_select( store_t *gctx, int create,
ctx->db->err( ctx->db, ret, "Maildir error: db->get()" ); ctx->db->err( ctx->db, ret, "Maildir error: db->get()" );
goto dbork; goto dbork;
} }
if (maildir_init_uid( ctx, 0 ) != DRV_OK) if (maildir_init_uid_new( ctx ) != DRV_OK)
goto dbork; goto dbork;
} else { } else {
ctx->gen.uidvalidity = ((int *)value.data)[0]; ctx->gen.uidvalidity = ((int *)value.data)[0];
@ -926,12 +938,13 @@ maildir_rescan( maildir_store_t *ctx )
} }
static int static int
maildir_again( maildir_store_t *ctx, maildir_message_t *msg, const char *fn ) maildir_again( maildir_store_t *ctx, maildir_message_t *msg,
const char *err, const char *fn, const char *fn2 )
{ {
int ret; int ret;
if (errno != ENOENT) { if (errno != ENOENT) {
perror( fn ); sys_error( err, fn, fn2 );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
if ((ret = maildir_rescan( ctx )) != DRV_OK) if ((ret = maildir_rescan( ctx )) != DRV_OK)
@ -953,7 +966,7 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data,
nfsnprintf( buf, sizeof(buf), "%s/%s/%s", gctx->path, subdirs[gmsg->status & M_RECENT], msg->base ); nfsnprintf( buf, sizeof(buf), "%s/%s/%s", gctx->path, subdirs[gmsg->status & M_RECENT], msg->base );
if ((fd = open( buf, O_RDONLY )) >= 0) if ((fd = open( buf, O_RDONLY )) >= 0)
break; break;
if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) { if ((ret = maildir_again( ctx, msg, "Cannot open %s", buf, 0 )) != DRV_OK) {
cb( ret, aux ); cb( ret, aux );
return; return;
} }
@ -962,7 +975,7 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data,
data->len = st.st_size; data->len = st.st_size;
data->data = nfmalloc( data->len ); data->data = nfmalloc( data->len );
if (read( fd, data->data, data->len ) != data->len) { if (read( fd, data->data, data->len ) != data->len) {
perror( buf ); sys_error( "Maildir error: cannot read %s", buf );
close( fd ); close( fd );
cb( DRV_MSG_BAD, aux ); cb( DRV_MSG_BAD, aux );
return; return;
@ -1028,7 +1041,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
nfsnprintf( buf, sizeof(buf), "%s%s/tmp/%s%s", prefix, box, base, fbuf ); nfsnprintf( buf, sizeof(buf), "%s%s/tmp/%s%s", prefix, box, base, fbuf );
if ((fd = open( buf, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) { if ((fd = open( buf, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) {
if (errno != ENOENT) { if (errno != ENOENT) {
perror( buf ); sys_error( "Maildir error: cannot create %s", buf );
free( data->data ); free( data->data );
cb( DRV_BOX_BAD, 0, aux ); cb( DRV_BOX_BAD, 0, aux );
return; return;
@ -1039,7 +1052,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
return; return;
} }
if ((fd = open( buf, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) { if ((fd = open( buf, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) {
perror( buf ); sys_error( "Maildir error: cannot create %s", buf );
free( data->data ); free( data->data );
cb( DRV_BOX_BAD, 0, aux ); cb( DRV_BOX_BAD, 0, aux );
return; return;
@ -1049,23 +1062,23 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
free( data->data ); free( data->data );
if (ret != data->len) { if (ret != data->len) {
if (ret < 0) if (ret < 0)
perror( buf ); sys_error( "Maildir error: cannot write %s", buf );
else else
error( "Maildir error: %s: partial write\n", buf ); error( "Maildir error: cannot write %s. Disk full?\n", buf );
close( fd ); close( fd );
cb( DRV_BOX_BAD, 0, aux ); cb( DRV_BOX_BAD, 0, aux );
return; return;
} }
if (close( fd ) < 0) { if (close( fd ) < 0) {
/* Quota exceeded may cause this. */ /* Quota exceeded may cause this. */
perror( buf ); sys_error( "Maildir error: cannot write %s", buf );
cb( DRV_BOX_BAD, 0, aux ); cb( DRV_BOX_BAD, 0, aux );
return; return;
} }
/* Moving seen messages to cur/ is strictly speaking incorrect, but makes mutt happy. */ /* Moving seen messages to cur/ is strictly speaking incorrect, but makes mutt happy. */
nfsnprintf( nbuf, sizeof(nbuf), "%s%s/%s/%s%s", prefix, box, subdirs[!(data->flags & F_SEEN)], base, fbuf ); nfsnprintf( nbuf, sizeof(nbuf), "%s%s/%s/%s%s", prefix, box, subdirs[!(data->flags & F_SEEN)], base, fbuf );
if (rename( buf, nbuf )) { if (rename( buf, nbuf )) {
perror( nbuf ); sys_error( "Maildir error: cannot rename %s to %s", buf, nbuf );
cb( DRV_BOX_BAD, 0, aux ); cb( DRV_BOX_BAD, 0, aux );
return; return;
} }
@ -1131,7 +1144,7 @@ maildir_set_flags( store_t *gctx, message_t *gmsg, int uid, int add, int del,
} }
if (!rename( buf, nbuf )) if (!rename( buf, nbuf ))
break; break;
if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) { if ((ret = maildir_again( ctx, msg, "Maildir error: cannot rename %s to %s", buf, nbuf )) != DRV_OK) {
cb( ret, aux ); cb( ret, aux );
return; return;
} }
@ -1189,12 +1202,12 @@ maildir_trash_msg( store_t *gctx, message_t *gmsg,
if (!rename( buf, nbuf )) if (!rename( buf, nbuf ))
break; break;
if (errno != ENOENT) { if (errno != ENOENT) {
perror( nbuf ); sys_error( "Maildir error: cannot move %s to %s", buf, nbuf );
cb( DRV_BOX_BAD, aux ); cb( DRV_BOX_BAD, aux );
return; return;
} }
} }
if ((ret = maildir_again( ctx, msg, buf )) != DRV_OK) { if ((ret = maildir_again( ctx, msg, "Maildir error: cannot move %s to %s", buf, nbuf )) != DRV_OK) {
cb( ret, aux ); cb( ret, aux );
return; return;
} }
@ -1232,7 +1245,7 @@ maildir_close( store_t *gctx,
if (errno == ENOENT) if (errno == ENOENT)
retry = 1; retry = 1;
else else
perror( buf ); sys_error( "Maildir error: cannot remove %s", buf );
} else { } else {
msg->status |= M_DEAD; msg->status |= M_DEAD;
gctx->count--; gctx->count--;

3
src/isync.h

@ -83,6 +83,7 @@ typedef struct {
int fd; int fd;
int state; int state;
const server_conf_t *conf; /* needed during connect */ const server_conf_t *conf; /* needed during connect */
char *name;
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
SSL *ssl; SSL *ssl;
#endif #endif
@ -366,6 +367,7 @@ static INLINE void socket_init( conn_t *conn,
conn->write_callback = write_callback; conn->write_callback = write_callback;
conn->callback_aux = aux; conn->callback_aux = aux;
conn->fd = -1; conn->fd = -1;
conn->name = 0;
conn->write_buf_append = &conn->write_buf; conn->write_buf_append = &conn->write_buf;
} }
void socket_connect( conn_t *conn, void (*cb)( int ok, void *aux ) ); void socket_connect( conn_t *conn, void (*cb)( int ok, void *aux ) );
@ -397,6 +399,7 @@ void info( const char *, ... );
void infon( const char *, ... ); void infon( const char *, ... );
void warn( const char *, ... ); void warn( const char *, ... );
void error( const char *, ... ); void error( const char *, ... );
void sys_error( const char *, ... );
char *next_arg( char ** ); char *next_arg( char ** );

46
src/mdconvert.c

@ -35,6 +35,32 @@
#define EXE "mdconvert" #define EXE "mdconvert"
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define ATTR_NORETURN __attribute__((noreturn))
#else
# define ATTR_NORETURN
#endif
static void ATTR_NORETURN
oob( void )
{
fputs( "Fatal: buffer too small. Please report a bug.\n", stderr );
abort();
}
static void
sys_error( const char *msg, ... )
{
va_list va;
char buf[1024];
va_start( va, msg );
if ((unsigned)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
oob();
va_end( va );
perror( buf );
}
static int static int
nfsnprintf( char *buf, int blen, const char *fmt, ... ) nfsnprintf( char *buf, int blen, const char *fmt, ... )
{ {
@ -42,10 +68,8 @@ nfsnprintf( char *buf, int blen, const char *fmt, ... )
va_list va; va_list va;
va_start( va, fmt ); va_start( va, fmt );
if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen) { if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
fputs( "Fatal: buffer too small. Please report a bug.\n", stderr ); oob();
abort();
}
va_end( va ); va_end( va );
return ret; return ret;
} }
@ -81,15 +105,15 @@ convert( const char *box, int altmap )
nfsnprintf( tdpath, sizeof(tdpath), "%s.tmp", dpath ); nfsnprintf( tdpath, sizeof(tdpath), "%s.tmp", dpath );
if ((sfd = open( spath, O_RDWR )) < 0) { if ((sfd = open( spath, O_RDWR )) < 0) {
if (errno != ENOENT) if (errno != ENOENT)
perror( spath ); sys_error( "Cannot open %s", spath );
return 1; return 1;
} }
if (fcntl( sfd, F_SETLKW, &lck )) { if (fcntl( sfd, F_SETLKW, &lck )) {
perror( spath ); sys_error( "Cannot lock %s", spath );
goto sbork; goto sbork;
} }
if ((dfd = open( tdpath, O_RDWR|O_CREAT, 0600 )) < 0) { if ((dfd = open( tdpath, O_RDWR|O_CREAT, 0600 )) < 0) {
perror( tdpath ); sys_error( "Cannot create %s", tdpath );
goto sbork; goto sbork;
} }
if (db_create( &db, 0, 0 )) { if (db_create( &db, 0, 0 )) {
@ -138,7 +162,7 @@ convert( const char *box, int altmap )
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
bl = nfsnprintf( buf, sizeof(buf), "%s/%s/", box, subdirs[i] ); bl = nfsnprintf( buf, sizeof(buf), "%s/%s/", box, subdirs[i] );
if (!(d = opendir( buf ))) { if (!(d = opendir( buf ))) {
perror( "opendir" ); sys_error( "Cannot list %s", buf );
goto dbork; goto dbork;
} }
while ((e = readdir( d ))) { while ((e = readdir( d ))) {
@ -185,7 +209,7 @@ convert( const char *box, int altmap )
if (rename( buf, buf2 )) { if (rename( buf, buf2 )) {
if (errno == ENOENT) if (errno == ENOENT)
goto again; goto again;
perror( buf ); sys_error( "Cannot rename %s to %s", buf, buf2 );
ebork: ebork:
closedir( d ); closedir( d );
goto dbork; goto dbork;
@ -198,11 +222,11 @@ convert( const char *box, int altmap )
db->close( db, 0 ); db->close( db, 0 );
close( dfd ); close( dfd );
if (rename( tdpath, dpath )) { if (rename( tdpath, dpath )) {
perror( dpath ); sys_error( "Cannot rename %s to %s", tdpath, dpath );
return 1; return 1;
} }
if (unlink( spath )) if (unlink( spath ))
perror( spath ); sys_error( "Cannot remove %s", spath );
close( sfd ); close( sfd );
return 0; return 0;
} }

49
src/socket.c

@ -79,15 +79,15 @@ ssl_return( const char *func, conn_t *conn, int ret )
case SSL_ERROR_SSL: case SSL_ERROR_SSL:
if (!(err = ERR_get_error())) { if (!(err = ERR_get_error())) {
if (ret == 0) if (ret == 0)
error( "SSL_%s: unexpected EOF\n", func ); error( "Socket error: secure %s %s: unexpected EOF\n", func, conn->name );
else else
error( "SSL_%s: %s\n", func, strerror( errno ) ); sys_error( "Socket error: secure %s %s", func, conn->name );
} else { } else {
error( "SSL_%s: %s\n", func, ERR_error_string( err, 0 ) ); error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, 0 ) );
} }
break; break;
default: default:
error( "SSL_%s: unhandled SSL error %d\n", func, err ); error( "Socket error: secure %s %s: unhandled SSL error %d\n", func, conn->name, err );
break; break;
} }
if (conn->state == SCK_STARTTLS) if (conn->state == SCK_STARTTLS)
@ -148,11 +148,11 @@ verify_cert( const server_conf_t *conf, conn_t *sock )
while (conf->cert_file) { /* while() instead of if() so break works */ while (conf->cert_file) { /* while() instead of if() so break works */
if (X509_cmp_current_time( X509_get_notBefore( cert )) >= 0) { if (X509_cmp_current_time( X509_get_notBefore( cert )) >= 0) {
error( "Server certificate is not yet valid" ); error( "Server certificate is not yet valid\n" );
break; break;
} }
if (X509_cmp_current_time( X509_get_notAfter( cert )) <= 0) { if (X509_cmp_current_time( X509_get_notAfter( cert )) <= 0) {
error( "Server certificate has expired" ); error( "Server certificate has expired\n" );
break; break;
} }
if (!X509_digest( cert, EVP_sha1(), md, &n )) { if (!X509_digest( cert, EVP_sha1(), md, &n )) {
@ -160,8 +160,7 @@ verify_cert( const server_conf_t *conf, conn_t *sock )
break; break;
} }
if (!(fp = fopen( conf->cert_file, "rt" ))) { if (!(fp = fopen( conf->cert_file, "rt" ))) {
error( "Unable to load CertificateFile '%s': %s\n", sys_error( "Unable to load CertificateFile '%s'", conf->cert_file );
conf->cert_file, strerror( errno ) );
return -1; return -1;
} }
err = -1; err = -1;
@ -197,7 +196,7 @@ verify_cert( const server_conf_t *conf, conn_t *sock )
X509_STORE_CTX_cleanup( &xsc ); X509_STORE_CTX_cleanup( &xsc );
if (!err) if (!err)
return 0; return 0;
error( "Error, can't verify certificate: %s (%d)\n", error( "Error, cannot verify certificate: %s (%d)\n",
X509_verify_cert_error_string( err ), err ); X509_verify_cert_error_string( err ), err );
X509_NAME_oneline( X509_get_subject_name( cert ), buf, sizeof(buf) ); X509_NAME_oneline( X509_get_subject_name( cert ), buf, sizeof(buf) );
@ -286,7 +285,7 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) )
static void static void
start_tls_p2( conn_t *conn ) start_tls_p2( conn_t *conn )
{ {
switch (ssl_return( "connect", conn, SSL_connect( conn->ssl ) )) { switch (ssl_return( "connect to", conn, SSL_connect( conn->ssl ) )) {
case -1: case -1:
start_tls_p3( conn, 0 ); start_tls_p3( conn, 0 );
break; break;
@ -337,7 +336,8 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
/* open connection to IMAP server */ /* open connection to IMAP server */
if (conf->tunnel) { if (conf->tunnel) {
infon( "Starting tunnel '%s'... ", conf->tunnel ); nfasprintf( &sock->name, "tunnel '%s'", conf->tunnel );
infon( "Starting %s... ", sock->name );
if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) { if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) {
perror( "socketpair" ); perror( "socketpair" );
@ -387,11 +387,12 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
fcntl( s, F_SETFL, O_NONBLOCK ); fcntl( s, F_SETFL, O_NONBLOCK );
add_fd( s, socket_fd_cb, sock ); add_fd( s, socket_fd_cb, sock );
infon( "Connecting to %s (%s:%hu) ... ", nfasprintf( &sock->name, "%s (%s:%hu)",
conf->host, inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) ); conf->host, inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );
infon( "Connecting to %s... ", sock->name );
if (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) { if (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) {
if (errno != EINPROGRESS) { if (errno != EINPROGRESS) {
perror( "connect" ); sys_error( "Cannot connect to %s", sock->name );
socket_close_internal( sock ); socket_close_internal( sock );
goto bail; goto bail;
} }
@ -416,19 +417,17 @@ socket_connected( conn_t *conn )
int soerr; int soerr;
socklen_t selen = sizeof(soerr); socklen_t selen = sizeof(soerr);
infon( "Connecting to %s: ", conn->conf->host );
if (getsockopt( conn->fd, SOL_SOCKET, SO_ERROR, &soerr, &selen )) { if (getsockopt( conn->fd, SOL_SOCKET, SO_ERROR, &soerr, &selen )) {
perror( "getsockopt" ); perror( "getsockopt" );
exit( 1 ); exit( 1 );
} }
if (soerr) { if (soerr) {
errno = soerr; errno = soerr;
perror( "connect" ); sys_error( "Cannot connect to %s", conn->name );
socket_close_internal( conn ); socket_close_internal( conn );
socket_connect_bail( conn ); socket_connect_bail( conn );
return; return;
} }
info( "ok\n" );
socket_connected2( conn ); socket_connected2( conn );
} }
@ -443,6 +442,8 @@ socket_connected2( conn_t *conn )
static void static void
socket_connect_bail( conn_t *conn ) socket_connect_bail( conn_t *conn )
{ {
free( conn->name );
conn->name = 0;
conn->callbacks.connect( 0, conn->callback_aux ); conn->callbacks.connect( 0, conn->callback_aux );
} }
@ -453,6 +454,8 @@ socket_close( conn_t *sock )
{ {
if (sock->fd >= 0) if (sock->fd >= 0)
socket_close_internal( sock ); socket_close_internal( sock );
free( sock->name );
sock->name = 0;
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
if (sock->ssl) { if (sock->ssl) {
SSL_free( sock->ssl ); SSL_free( sock->ssl );
@ -478,7 +481,7 @@ socket_fill( conn_t *sock )
buf = sock->buf + n; buf = sock->buf + n;
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
if (sock->ssl) { if (sock->ssl) {
if ((n = ssl_return( "read", sock, SSL_read( sock->ssl, buf, len ) )) <= 0) if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, len ) )) <= 0)
return; return;
if (n == len && SSL_pending( sock->ssl )) if (n == len && SSL_pending( sock->ssl ))
fake_fd( sock->fd, POLLIN ); fake_fd( sock->fd, POLLIN );
@ -486,11 +489,11 @@ socket_fill( conn_t *sock )
#endif #endif
{ {
if ((n = read( sock->fd, buf, len )) < 0) { if ((n = read( sock->fd, buf, len )) < 0) {
perror( "read" ); sys_error( "Socket error: read from %s", sock->name );
socket_fail( sock ); socket_fail( sock );
return; return;
} else if (!n) { } else if (!n) {
error( "read: unexpected EOF\n" ); error( "Socket error: read from %s: unexpected EOF\n", sock->name );
socket_fail( sock ); socket_fail( sock );
return; return;
} }
@ -549,12 +552,12 @@ do_write( conn_t *sock, char *buf, int len )
assert( sock->fd >= 0 ); assert( sock->fd >= 0 );
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
if (sock->ssl) if (sock->ssl)
return ssl_return( "write", sock, SSL_write( sock->ssl, buf, len ) ); return ssl_return( "write to", sock, SSL_write( sock->ssl, buf, len ) );
#endif #endif
n = write( sock->fd, buf, len ); n = write( sock->fd, buf, len );
if (n < 0) { if (n < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) { if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror( "write" ); sys_error( "Socket error: write to %s", sock->name );
socket_fail( sock ); socket_fail( sock );
} else { } else {
n = 0; n = 0;
@ -646,7 +649,7 @@ socket_fd_cb( int events, void *aux )
conn_t *conn = (conn_t *)aux; conn_t *conn = (conn_t *)aux;
if (events & POLLERR) { if (events & POLLERR) {
error( "Unidentified socket error.\n" ); error( "Unidentified socket error from %s.\n", conn->name );
socket_fail( conn ); socket_fail( conn );
return; return;
} }

10
src/sync.c

@ -40,7 +40,7 @@ void
Fclose( FILE *f ) Fclose( FILE *f )
{ {
if (fclose( f ) == EOF) { if (fclose( f ) == EOF) {
perror( "cannot close file" ); sys_error( "Error: cannot close file. Disk full?" );
exit( 1 ); exit( 1 );
} }
} }
@ -55,7 +55,7 @@ Fprintf( FILE *f, const char *msg, ... )
r = vfprintf( f, msg, va ); r = vfprintf( f, msg, va );
va_end( va ); va_end( va );
if (r < 0) { if (r < 0) {
perror( "cannot write file" ); sys_error( "Error: cannot write file. Disk full?" );
exit( 1 ); exit( 1 );
} }
} }
@ -624,12 +624,12 @@ box_selected( int sts, void *aux )
} }
free( csname ); free( csname );
if (!(s = strrchr( svars->dname, '/' ))) { if (!(s = strrchr( svars->dname, '/' ))) {
error( "Error: invalid SyncState '%s'\n", svars->dname ); error( "Error: invalid SyncState location '%s'\n", svars->dname );
goto sbail; goto sbail;
} }
*s = 0; *s = 0;
if (mkdir( svars->dname, 0700 ) && errno != EEXIST) { if (mkdir( svars->dname, 0700 ) && errno != EEXIST) {
error( "Error: cannot create SyncState directory '%s': %s\n", svars->dname, strerror(errno) ); sys_error( "Error: cannot create SyncState directory '%s'", svars->dname );
goto sbail; goto sbail;
} }
*s = '/'; *s = '/';
@ -645,7 +645,7 @@ box_selected( int sts, void *aux )
lck.l_type = F_WRLCK; lck.l_type = F_WRLCK;
#endif #endif
if ((svars->lfd = open( svars->lname, O_WRONLY|O_CREAT, 0666 )) < 0) { if ((svars->lfd = open( svars->lname, O_WRONLY|O_CREAT, 0666 )) < 0) {
error( "Error: cannot create lock file %s: %s\n", svars->lname, strerror(errno) ); sys_error( "Error: cannot create lock file %s", svars->lname );
svars->ret = SYNC_FAIL; svars->ret = SYNC_FAIL;
sync_bail2( svars ); sync_bail2( svars );
return; return;

17
src/util.c

@ -120,6 +120,23 @@ error( const char *msg, ... )
va_end( va ); va_end( va );
} }
void
sys_error( const char *msg, ... )
{
va_list va;
char buf[1024];
if (need_nl) {
putchar( '\n' );
need_nl = 0;
}
va_start( va, msg );
if ((unsigned)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
oob();
va_end( va );
perror( buf );
}
char * char *
next_arg( char **s ) next_arg( char **s )
{ {

Loading…
Cancel
Save