Browse Source

*** attempt to structure all message output

the central idea is to categorize all output and connect it
hierarchically, so it can be presented by higher layers with appropriate
context, e.g., to avoid a mess in a massively parallel run while
avoiding noise in a single-threaded one.

this is very far from completion ...
wip/better-stderr
Oswald Buddenhagen 7 years ago
parent
commit
66d891ac9a
  1. 14
      src/common.h
  2. 46
      src/config.c
  3. 5
      src/driver.c
  4. 227
      src/drv_imap.c
  5. 66
      src/drv_maildir.c
  6. 73
      src/socket.c
  7. 18
      src/sync.c
  8. 127
      src/util.c

14
src/common.h

@ -73,10 +73,7 @@ typedef unsigned int uint;
#define DEBUG_DRV 0x40 #define DEBUG_DRV 0x40
#define DEBUG_DRV_ALL 0x80 #define DEBUG_DRV_ALL 0x80
#define DEBUG_ALL (0xFF & ~(DEBUG_NET_ALL | DEBUG_DRV_ALL)) #define DEBUG_ALL (0xFF & ~(DEBUG_NET_ALL | DEBUG_DRV_ALL))
#define QUIET 0x100
#define VERYQUIET 0x200
#define PROGRESS 0x400 #define PROGRESS 0x400
#define VERBOSE 0x800
#define KEEPJOURNAL 0x1000 #define KEEPJOURNAL 0x1000
#define ZERODELAY 0x2000 #define ZERODELAY 0x2000
@ -110,6 +107,17 @@ void ATTR_PRINTFLIKE(1, 2) error( const char *, ... );
void ATTR_PRINTFLIKE(1, 2) sys_error( const char *, ... ); void ATTR_PRINTFLIKE(1, 2) sys_error( const char *, ... );
void flushn( void ); void flushn( void );
extern int PrintLevel;
#define PRN_ERROR 0
#define PRN_WARN 1
#define PRN_NOTICE 2
#define PRN_INFO 3
#define PRN_NONL 0x100
#define PRN_CONT 0x200
char *nfeprintf( char *buf, char *buf_end, const char *fmt, ... );
char *nfevprintf( char *buf, char *buf_end, const char *fmt, va_list ap );
typedef struct string_list { typedef struct string_list {
struct string_list *next; struct string_list *next;
char string[1]; char string[1];

46
src/config.c

@ -51,7 +51,7 @@ get_arg( conffile_t *cfile, int required, int *comment )
if (comment) if (comment)
*comment = (c == '#'); *comment = (c == '#');
if (required) { if (required) {
error( "%s:%d: parameter missing\n", cfile->file, cfile->line ); glbl_print( PRN_ERROR, "%s:%d: parameter missing\n", cfile->file, cfile->line );
cfile->err = 1; cfile->err = 1;
} }
ret = 0; ret = 0;
@ -72,12 +72,12 @@ get_arg( conffile_t *cfile, int required, int *comment )
} }
*t = 0; *t = 0;
if (escaped) { if (escaped) {
error( "%s:%d: unterminated escape sequence\n", cfile->file, cfile->line ); glbl_print( PRN_ERROR, "%s:%d: unterminated escape sequence\n", cfile->file, cfile->line );
cfile->err = 1; cfile->err = 1;
ret = 0; ret = 0;
} }
if (quoted) { if (quoted) {
error( "%s:%d: missing closing quote\n", cfile->file, cfile->line ); glbl_print( PRN_ERROR, "%s:%d: missing closing quote\n", cfile->file, cfile->line );
cfile->err = 1; cfile->err = 1;
ret = 0; ret = 0;
} }
@ -98,7 +98,7 @@ parse_bool( conffile_t *cfile )
strcasecmp( cfile->val, "false" ) && strcasecmp( cfile->val, "false" ) &&
strcasecmp( cfile->val, "off" ) && strcasecmp( cfile->val, "off" ) &&
strcmp( cfile->val, "0" )) { strcmp( cfile->val, "0" )) {
error( "%s:%d: invalid boolean value '%s'\n", glbl_print( PRN_ERROR, "%s:%d: invalid boolean value '%s'\n",
cfile->file, cfile->line, cfile->val ); cfile->file, cfile->line, cfile->val );
cfile->err = 1; cfile->err = 1;
} }
@ -113,7 +113,7 @@ parse_int( conffile_t *cfile )
ret = strtol( cfile->val, &p, 10 ); ret = strtol( cfile->val, &p, 10 );
if (*p) { if (*p) {
error( "%s:%d: invalid integer value '%s'\n", glbl_print( PRN_ERROR, "%s:%d: invalid integer value '%s'\n",
cfile->file, cfile->line, cfile->val ); cfile->file, cfile->line, cfile->val );
cfile->err = 1; cfile->err = 1;
return 0; return 0;
@ -192,7 +192,7 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg )) else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg ))
*cops |= XOP_PULL|XOP_PUSH; *cops |= XOP_PULL|XOP_PUSH;
else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg )) { else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg )) {
error( "%s:%d: invalid Sync arg '%s'\n", glbl_print( PRN_ERROR, "%s:%d: invalid Sync arg '%s'\n",
cfile->file, cfile->line, arg ); cfile->file, cfile->line, arg );
cfile->err = 1; cfile->err = 1;
} }
@ -219,7 +219,7 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
} else if (!strcasecmp( "Slave", arg )) { } else if (!strcasecmp( "Slave", arg )) {
conf->ops[S] |= op; conf->ops[S] |= op;
} else if (strcasecmp( "None", arg )) { } else if (strcasecmp( "None", arg )) {
error( "%s:%d: invalid %s arg '%s'\n", glbl_print( PRN_ERROR, "%s:%d: invalid %s arg '%s'\n",
cfile->file, cfile->line, boxOps[i].name, arg ); cfile->file, cfile->line, boxOps[i].name, arg );
cfile->err = 1; cfile->err = 1;
} }
@ -240,7 +240,7 @@ getcline( conffile_t *cfile )
int comment; int comment;
if (cfile->rest && (arg = get_arg( cfile, ARG_OPTIONAL, 0 ))) { if (cfile->rest && (arg = get_arg( cfile, ARG_OPTIONAL, 0 ))) {
error( "%s:%d: excess token '%s'\n", cfile->file, cfile->line, arg ); glbl_print( PRN_ERROR, "%s:%d: excess token '%s'\n", cfile->file, cfile->line, arg );
cfile->err = 1; cfile->err = 1;
} }
while (fgets( cfile->buf, cfile->bufl, cfile->fp )) { while (fgets( cfile->buf, cfile->bufl, cfile->fp )) {
@ -270,7 +270,7 @@ merge_ops( int cops, int ops[] )
if (aops & OP_MASK_TYPE) { if (aops & OP_MASK_TYPE) {
if (aops & cops & OP_MASK_TYPE) { if (aops & cops & OP_MASK_TYPE) {
cfl: cfl:
error( "Conflicting Sync args specified.\n" ); glbl_print( PRN_ERROR, "Conflicting Sync args specified.\n" );
return 1; return 1;
} }
ops[M] |= cops & OP_MASK_TYPE; ops[M] |= cops & OP_MASK_TYPE;
@ -300,7 +300,7 @@ merge_ops( int cops, int ops[] )
op = boxOps[i].op; op = boxOps[i].op;
if (ops[M] & (op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE))) { if (ops[M] & (op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE))) {
if (aops & cops & op) { if (aops & cops & op) {
error( "Conflicting %s args specified.\n", boxOps[i].name ); glbl_print( PRN_ERROR, "Conflicting %s args specified.\n", boxOps[i].name );
return 1; return 1;
} }
ops[M] |= cops & op; ops[M] |= cops & op;
@ -334,7 +334,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" ))) {
sys_error( "Cannot open config file '%s'", cfile.file ); glbl_print( PRN_ERROR, "Cannot open config file '%s': %m\n", cfile.file );
return 1; return 1;
} }
buf[sizeof(buf) - 1] = 0; buf[sizeof(buf) - 1] = 0;
@ -388,7 +388,7 @@ load_config( const char *where, int pseudo )
ms = S; ms = S;
linkst: linkst:
if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) { if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) {
error( "%s:%d: malformed mailbox spec\n", glbl_print( PRN_ERROR, "%s:%d: malformed mailbox spec\n",
cfile.file, cfile.line ); cfile.file, cfile.line );
cfile.err = 1; cfile.err = 1;
continue; continue;
@ -399,7 +399,7 @@ load_config( const char *where, int pseudo )
channel->stores[ms] = store; channel->stores[ms] = store;
goto stpcom; goto stpcom;
} }
error( "%s:%d: unknown store '%s'\n", glbl_print( PRN_ERROR, "%s:%d: unknown store '%s'\n",
cfile.file, cfile.line, cfile.val + 1 ); cfile.file, cfile.line, cfile.val + 1 );
cfile.err = 1; cfile.err = 1;
continue; continue;
@ -407,15 +407,16 @@ load_config( const char *where, int pseudo )
if (*++p) if (*++p)
channel->boxes[ms] = nfstrdup( p ); channel->boxes[ms] = nfstrdup( p );
} else if (!getopt_helper( &cfile, &cops, channel )) { } else if (!getopt_helper( &cfile, &cops, channel )) {
error( "%s:%d: unknown keyword '%s'\n", cfile.file, cfile.line, cfile.cmd ); glbl_print( PRN_ERROR, "%s:%d: unknown keyword '%s'\n",
cfile.file, cfile.line, cfile.cmd );
cfile.err = 1; cfile.err = 1;
} }
} }
if (!channel->stores[M]) { if (!channel->stores[M]) {
error( "channel '%s' refers to no master store\n", channel->name ); glbl_print( PRN_ERROR, "Channel '%s' refers to no master store\n", channel->name );
cfile.err = 1; cfile.err = 1;
} else if (!channel->stores[S]) { } else if (!channel->stores[S]) {
error( "channel '%s' refers to no slave store\n", channel->name ); glbl_print( PRN_ERROR, "Channel '%s' refers to no slave store\n", channel->name );
cfile.err = 1; cfile.err = 1;
} else if (merge_ops( cops, channel->ops )) } else if (merge_ops( cops, channel->ops ))
cfile.err = 1; cfile.err = 1;
@ -458,7 +459,7 @@ load_config( const char *where, int pseudo )
} }
else else
{ {
error( "%s:%d: unknown keyword '%s'\n", glbl_print( PRN_ERROR, "%s:%d: unknown keyword '%s'\n",
cfile.file, cfile.line, cfile.cmd ); cfile.file, cfile.line, cfile.cmd );
cfile.err = 1; cfile.err = 1;
} }
@ -472,12 +473,14 @@ load_config( const char *where, int pseudo )
else if (!strcasecmp( "FieldDelimiter", cfile.cmd )) else if (!strcasecmp( "FieldDelimiter", cfile.cmd ))
{ {
if (strlen( cfile.val ) != 1) { if (strlen( cfile.val ) != 1) {
error( "%s:%d: Field delimiter must be exactly one character long\n", cfile.file, cfile.line ); glbl_print( PRN_ERROR, "%s:%d: field delimiter must be exactly one character long\n",
cfile.file, cfile.line );
cfile.err = 1; cfile.err = 1;
} else { } else {
FieldDelimiter = cfile.val[0]; FieldDelimiter = cfile.val[0];
if (!ispunct( FieldDelimiter )) { if (!ispunct( FieldDelimiter )) {
error( "%s:%d: Field delimiter must be a punctuation character\n", cfile.file, cfile.line ); glbl_print( PRN_ERROR, "%s:%d: field delimiter must be a punctuation character\n",
cfile.file, cfile.line );
cfile.err = 1; cfile.err = 1;
} }
} }
@ -486,13 +489,14 @@ load_config( const char *where, int pseudo )
{ {
BufferLimit = parse_size( &cfile ); BufferLimit = parse_size( &cfile );
if (BufferLimit <= 0) { if (BufferLimit <= 0) {
error( "%s:%d: BufferLimit must be positive\n", cfile.file, cfile.line ); glbl_print( PRN_ERROR, "%s:%d: BufferLimit must be positive\n",
cfile.file, cfile.line );
cfile.err = 1; cfile.err = 1;
} }
} }
else if (!getopt_helper( &cfile, &gcops, &global_conf )) else if (!getopt_helper( &cfile, &gcops, &global_conf ))
{ {
error( "%s:%d: unknown section keyword '%s'\n", glbl_print( PRN_ERROR, "%s:%d: unknown section keyword '%s'\n",
cfile.file, cfile.line, cfile.cmd ); cfile.file, cfile.line, cfile.cmd );
cfile.err = 1; cfile.err = 1;
while (getcline( &cfile )) while (getcline( &cfile ))

5
src/driver.c

@ -56,14 +56,15 @@ parse_generic_store( store_conf_t *store, conffile_t *cfg )
const char *p; const char *p;
for (p = cfg->val; *p; p++) { for (p = cfg->val; *p; p++) {
if (*p == '/') { if (*p == '/') {
error( "%s:%d: flattened hierarchy delimiter cannot contain the canonical delimiter '/'\n", cfg->file, cfg->line ); glbl_print( PRN_ERROR, "%s:%d: flattened hierarchy delimiter cannot contain the canonical delimiter '/'\n",
cfg->file, cfg->line );
cfg->err = 1; cfg->err = 1;
return; return;
} }
} }
store->flat_delim = nfstrdup( cfg->val ); store->flat_delim = nfstrdup( cfg->val );
} else { } else {
error( "%s:%d: unknown keyword '%s'\n", cfg->file, cfg->line, cfg->cmd ); glbl_print( PRN_ERROR, "%s:%d: unknown keyword '%s'\n", cfg->file, cfg->line, cfg->cmd );
cfg->err = 1; cfg->err = 1;
} }
} }

227
src/drv_imap.c

@ -440,76 +440,9 @@ submit_imap_cmd( imap_store_t *ctx, imap_cmd_t *cmd )
static char * static char *
imap_vprintf( const char *fmt, va_list ap ) imap_vprintf( const char *fmt, va_list ap )
{ {
const char *s;
char *d, *ed;
int maxlen;
char c;
char buf[1024]; /* Minimal supported command buffer size per IMAP spec. */ char buf[1024]; /* Minimal supported command buffer size per IMAP spec. */
d = buf; return nfstrndup( buf, nfevprintf( buf, buf + sizeof(buf), fmt, ap ) - buf );
ed = d + sizeof(buf);
s = fmt;
for (;;) {
c = *fmt;
if (!c || c == '%') {
int l = fmt - s;
if (d + l > ed)
oob();
memcpy( d, s, l );
d += l;
if (!c)
return nfstrndup( buf, d - buf );
maxlen = INT_MAX;
c = *++fmt;
if (c == '\\') {
c = *++fmt;
if (c != 's') {
fputs( "Fatal: unsupported escaped format specifier. Please report a bug.\n", stderr );
abort();
}
s = va_arg( ap, const char * );
while ((c = *s++)) {
if (d + 2 > ed)
oob();
if (c == '\\' || c == '"')
*d++ = '\\';
*d++ = c;
}
} else { /* \\ cannot be combined with anything else. */
if (c == '.') {
c = *++fmt;
if (c != '*') {
fputs( "Fatal: unsupported string length specification. Please report a bug.\n", stderr );
abort();
}
maxlen = va_arg( ap , int );
c = *++fmt;
}
if (c == 'c') {
if (d + 1 > ed)
oob();
*d++ = (char)va_arg( ap , int );
} else if (c == 's') {
s = va_arg( ap, const char * );
l = strnlen( s, maxlen );
if (d + l > ed)
oob();
memcpy( d, s, l );
d += l;
} else if (c == 'd') {
d += nfsnprintf( d, ed - d, "%d", va_arg( ap , int ) );
} else if (c == 'u') {
d += nfsnprintf( d, ed - d, "%u", va_arg( ap , uint ) );
} else {
fputs( "Fatal: unsupported format specifier. Please report a bug.\n", stderr );
abort();
}
}
s = ++fmt;
} else {
fmt++;
}
}
} }
static void static void
@ -788,7 +721,7 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
n = socket_read( &ctx->conn, s, bytes ); n = socket_read( &ctx->conn, s, bytes );
if (n < 0) { if (n < 0) {
badeof: badeof:
error( "IMAP error: unexpected EOF from %s\n", ctx->conn.name ); drv_print( PRN_ERROR, "Unexpected EOF from %s\n", ctx->conn.name );
goto bail; goto bail;
} }
bytes -= n; bytes -= n;
@ -911,7 +844,7 @@ parse_namespace_check( list_t *list )
} }
return 0; return 0;
bad: bad:
error( "IMAP error: malformed NAMESPACE response\n" ); drv_print( PRN_ERROR, "Malformed IMAP NAMESPACE response\n" );
return -1; return -1;
} }
@ -970,7 +903,7 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED )
time_t date = 0; time_t date = 0;
if (!is_list( list )) { if (!is_list( list )) {
error( "IMAP error: bogus FETCH response\n" ); drv_print( PRN_ERROR, "Malformed IMAP FETCH response\n" );
free_list( list ); free_list( list );
return LIST_BAD; return LIST_BAD;
} }
@ -980,7 +913,7 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED )
if (!strcmp( "UID", tmp->val )) { if (!strcmp( "UID", tmp->val )) {
tmp = tmp->next; tmp = tmp->next;
if (!is_atom( tmp ) || (uid = strtoul( tmp->val, &ep, 10 ), *ep)) if (!is_atom( tmp ) || (uid = strtoul( tmp->val, &ep, 10 ), *ep))
error( "IMAP error: unable to parse UID\n" ); drv_print( PRN_ERROR, "Malformed UID in IMAP FETCH response\n" );
} else if (!strcmp( "FLAGS", tmp->val )) { } else if (!strcmp( "FLAGS", tmp->val )) {
tmp = tmp->next; tmp = tmp->next;
if (is_list( tmp )) { if (is_list( tmp )) {
@ -998,26 +931,26 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED )
} }
if (flags->val[1] == 'X' && flags->val[2] == '-') if (flags->val[1] == 'X' && flags->val[2] == '-')
goto flagok; /* ignore system flag extensions */ goto flagok; /* ignore system flag extensions */
error( "IMAP warning: unknown system flag %s\n", flags->val ); drv_print( PRN_WARN, "Unknown system flag %s in IMAP FETCH response\n", flags->val );
} }
flagok: ; flagok: ;
} else } else
error( "IMAP error: unable to parse FLAGS list\n" ); drv_print( PRN_ERROR, "Malformed FLAGS in IMAP FETCH response\n" );
} }
status |= M_FLAGS; status |= M_FLAGS;
} else } else
error( "IMAP error: unable to parse FLAGS\n" ); drv_print( PRN_ERROR, "Malformed FLAGS in IMAP FETCH response\n" );
} else if (!strcmp( "INTERNALDATE", tmp->val )) { } else if (!strcmp( "INTERNALDATE", tmp->val )) {
tmp = tmp->next; tmp = tmp->next;
if (is_atom( tmp )) { if (is_atom( tmp )) {
if ((date = parse_date( tmp->val )) == -1) if ((date = parse_date( tmp->val )) == -1)
error( "IMAP error: unable to parse INTERNALDATE format\n" ); drv_print( PRN_ERROR, "Unable to parse IMAP INTERNALDATE format\n" );
} else } else
error( "IMAP error: unable to parse INTERNALDATE\n" ); drv_print( PRN_ERROR, "Malformed INTERNALDATE in IMAP FETCH response\n" );
} else if (!strcmp( "RFC822.SIZE", tmp->val )) { } else if (!strcmp( "RFC822.SIZE", tmp->val )) {
tmp = tmp->next; tmp = tmp->next;
if (!is_atom( tmp ) || (size = strtoul( tmp->val, &ep, 10 ), *ep)) if (!is_atom( tmp ) || (size = strtoul( tmp->val, &ep, 10 ), *ep))
error( "IMAP error: unable to parse RFC822.SIZE\n" ); drv_print( PRN_ERROR, "Malformed RFC822.SIZE in IMAP FETCH response\n" );
} else if (!strcmp( "BODY[]", tmp->val )) { } else if (!strcmp( "BODY[]", tmp->val )) {
tmp = tmp->next; tmp = tmp->next;
if (is_atom( tmp )) { if (is_atom( tmp )) {
@ -1025,7 +958,7 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED )
tmp->val = 0; /* don't free together with list */ tmp->val = 0; /* don't free together with list */
size = tmp->len; size = tmp->len;
} else } else
error( "IMAP error: unable to parse BODY[]\n" ); drv_print( PRN_ERROR, "Malformed BODY[] in IMAP FETCH response\n" );
} else if (!strcmp( "BODY[HEADER.FIELDS", tmp->val )) { } else if (!strcmp( "BODY[HEADER.FIELDS", tmp->val )) {
tmp = tmp->next; tmp = tmp->next;
if (is_list( tmp )) { if (is_list( tmp )) {
@ -1073,7 +1006,7 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED )
} }
} else { } else {
bfail: bfail:
error( "IMAP error: unable to parse BODY[HEADER.FIELDS ...]\n" ); drv_print( PRN_ERROR, "Malformed BODY[HEADER.FIELDS ...] in IMAP FETCH response\n" );
} }
} }
} }
@ -1091,7 +1024,7 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED )
for (cmdp = ctx->in_progress; cmdp; cmdp = cmdp->next) for (cmdp = ctx->in_progress; cmdp; cmdp = cmdp->next)
if (cmdp->param.uid == uid) if (cmdp->param.uid == uid)
goto gotuid; goto gotuid;
error( "IMAP error: unexpected FETCH response (UID %u)\n", uid ); drv_print( PRN_ERROR, "Unexpected IMAP FETCH response (UID %u)\n", uid );
free_list( list ); free_list( list );
return LIST_BAD; return LIST_BAD;
gotuid: gotuid:
@ -1156,7 +1089,7 @@ parse_response_code( imap_store_t *ctx, imap_cmd_t *cmd, char *s )
s++; s++;
if (!(p = strchr( s, ']' ))) { if (!(p = strchr( s, ']' ))) {
bad_resp: bad_resp:
error( "IMAP error: malformed response code\n" ); drv_print( PRN_ERROR, "Malformed IMAP response code\n" );
return RESP_CANCEL; return RESP_CANCEL;
} }
*p++ = 0; *p++ = 0;
@ -1166,14 +1099,14 @@ parse_response_code( imap_store_t *ctx, imap_cmd_t *cmd, char *s )
if (!(arg = next_arg( &s )) || if (!(arg = next_arg( &s )) ||
(ctx->uidvalidity = strtoul( arg, &earg, 10 ), *earg)) (ctx->uidvalidity = strtoul( arg, &earg, 10 ), *earg))
{ {
error( "IMAP error: malformed UIDVALIDITY status\n" ); drv_print( PRN_ERROR, "Malformed IMAP UIDVALIDITY status\n" );
return RESP_CANCEL; return RESP_CANCEL;
} }
} else if (!strcmp( "UIDNEXT", arg )) { } else if (!strcmp( "UIDNEXT", arg )) {
if (!(arg = next_arg( &s )) || if (!(arg = next_arg( &s )) ||
(ctx->uidnext = strtoul( arg, &earg, 10 ), *earg)) (ctx->uidnext = strtoul( arg, &earg, 10 ), *earg))
{ {
error( "IMAP error: malformed NEXTUID status\n" ); drv_print( PRN_ERROR, "Malformed IMAP NEXTUID status\n" );
return RESP_CANCEL; return RESP_CANCEL;
} }
} else if (!strcmp( "CAPABILITY", arg )) { } else if (!strcmp( "CAPABILITY", arg )) {
@ -1183,14 +1116,14 @@ parse_response_code( imap_store_t *ctx, imap_cmd_t *cmd, char *s )
* to the user * to the user
*/ */
for (; isspace( (uchar)*p ); p++); for (; isspace( (uchar)*p ); p++);
error( "*** IMAP ALERT *** %s\n", p ); drv_print( PRN_ERROR, "*** IMAP ALERT: %s\n", p );
} else if (cmd && !strcmp( "APPENDUID", arg )) { } else if (cmd && !strcmp( "APPENDUID", arg )) {
if (!(arg = next_arg( &s )) || if (!(arg = next_arg( &s )) ||
(ctx->uidvalidity = strtoul( arg, &earg, 10 ), *earg) || (ctx->uidvalidity = strtoul( arg, &earg, 10 ), *earg) ||
!(arg = next_arg( &s )) || !(arg = next_arg( &s )) ||
(((imap_cmd_out_uid_t *)cmd)->out_uid = strtoul( arg, &earg, 10 ), *earg)) (((imap_cmd_out_uid_t *)cmd)->out_uid = strtoul( arg, &earg, 10 ), *earg))
{ {
error( "IMAP error: malformed APPENDUID status\n" ); drv_print( PRN_ERROR, "Malformed IMAP APPENDUID status\n" );
return RESP_CANCEL; return RESP_CANCEL;
} }
} }
@ -1208,7 +1141,7 @@ parse_list_rsp( imap_store_t *ctx, list_t *list, char *cmd )
if (!is_list( list )) { if (!is_list( list )) {
free_list( list ); free_list( list );
bad_list: bad_list:
error( "IMAP error: malformed LIST response\n" ); drv_print( PRN_ERROR, "Malformed IMAP LIST response\n" );
return LIST_BAD; return LIST_BAD;
} }
for (lp = list->child; lp; lp = lp->next) for (lp = list->child; lp; lp = lp->next)
@ -1242,7 +1175,7 @@ parse_list_rsp_p2( imap_store_t *ctx, list_t *list, char *cmd ATTR_UNUSED )
int argl, l; int argl, l;
if (!is_atom( list )) { if (!is_atom( list )) {
error( "IMAP error: malformed LIST response\n" ); drv_print( PRN_ERROR, "Malformed IMAP LIST response\n" );
free_list( list ); free_list( list );
return LIST_BAD; return LIST_BAD;
} }
@ -1254,7 +1187,7 @@ parse_list_rsp_p2( imap_store_t *ctx, list_t *list, char *cmd ATTR_UNUSED )
argl -= l; argl -= l;
if (is_inbox( ctx, arg, argl )) { if (is_inbox( ctx, arg, argl )) {
if (!arg[5]) if (!arg[5])
warn( "IMAP warning: ignoring INBOX in %s\n", ctx->prefix ); drv_print( PRN_WARN, "Ignoring nested INBOX in %s\n", ctx->prefix );
goto skip; goto skip;
} }
} else if (!is_inbox( ctx, arg, argl )) { } else if (!is_inbox( ctx, arg, argl )) {
@ -1264,7 +1197,7 @@ parse_list_rsp_p2( imap_store_t *ctx, list_t *list, char *cmd ATTR_UNUSED )
if (argl >= 5 && !memcmp( arg + argl - 5, ".lock", 5 )) /* workaround broken servers */ if (argl >= 5 && !memcmp( arg + argl - 5, ".lock", 5 )) /* workaround broken servers */
goto skip; goto skip;
if (map_name( arg, (char **)&narg, offsetof(string_list_t, string), ctx->delimiter, "/") < 0) { if (map_name( arg, (char **)&narg, offsetof(string_list_t, string), ctx->delimiter, "/") < 0) {
warn( "IMAP warning: ignoring mailbox %s (reserved character '/' in name)\n", arg ); drv_print( PRN_WARN, "Ignoring mailbox %s (reserved character '/' in name)\n", arg );
goto skip; goto skip;
} }
narg->next = ctx->boxes; narg->next = ctx->boxes;
@ -1281,10 +1214,10 @@ prepare_name( char **buf, const imap_store_t *ctx, const char *prefix, const cha
switch (map_name( name, buf, pl, "/", ctx->delimiter )) { switch (map_name( name, buf, pl, "/", ctx->delimiter )) {
case -1: case -1:
error( "IMAP error: mailbox name %s contains server's hierarchy delimiter\n", name ); drv_print( PRN_ERROR, "Mailbox name %s contains server's hierarchy delimiter\n", name );
return -1; return -1;
case -2: case -2:
error( "IMAP error: server's hierarchy delimiter not known\n" ); drv_print( PRN_ERROR, "Server's hierarchy delimiter is not known\n" );
return -1; return -1;
default: default:
memcpy( *buf, prefix, pl ); memcpy( *buf, prefix, pl );
@ -1338,7 +1271,7 @@ imap_socket_read( void *aux )
return; return;
if (cmd == (void *)~0) { if (cmd == (void *)~0) {
if (!ctx->expectEOF) if (!ctx->expectEOF)
error( "IMAP error: unexpected EOF from %s\n", ctx->conn.name ); drv_print( PRN_ERROR, "Unexpected EOF from %s\n", ctx->conn.name );
/* A clean shutdown sequence ends with bad_callback as well (see imap_cleanup()). */ /* A clean shutdown sequence ends with bad_callback as well (see imap_cleanup()). */
break; break;
} }
@ -1349,13 +1282,13 @@ imap_socket_read( void *aux )
arg = next_arg( &cmd ); arg = next_arg( &cmd );
if (!arg) { if (!arg) {
error( "IMAP error: empty response\n" ); drv_print( PRN_ERROR, "Empty IMAP response\n" );
break; break;
} }
if (*arg == '*') { if (*arg == '*') {
arg = next_arg( &cmd ); arg = next_arg( &cmd );
if (!arg) { if (!arg) {
error( "IMAP error: malformed untagged response\n" ); drv_print( PRN_ERROR, "Malformed untagged IMAP response\n" );
break; break;
} }
@ -1376,19 +1309,19 @@ imap_socket_read( void *aux )
} else if (!strcmp( "BYE", arg )) { } else if (!strcmp( "BYE", arg )) {
if (!ctx->expectBYE) { if (!ctx->expectBYE) {
ctx->greeting = GreetingBad; ctx->greeting = GreetingBad;
error( "IMAP error: unexpected BYE response: %s\n", cmd ); drv_print( PRN_ERROR, "Unexpected IMAP BYE response: %s\n", cmd );
/* We just wait for the server to close the connection now. */ /* We just wait for the server to close the connection now. */
ctx->expectEOF = 1; ctx->expectEOF = 1;
} else { } else {
/* We still need to wait for the LOGOUT's tagged OK. */ /* We still need to wait for the LOGOUT's tagged OK. */
} }
} else if (ctx->greeting == GreetingPending) { } else if (ctx->greeting == GreetingPending) {
error( "IMAP error: bogus greeting response %s\n", arg ); drv_print( PRN_ERROR, "Unexpected IMAP greeting response %s\n", arg );
break; break;
} else if (!strcmp( "NO", arg )) { } else if (!strcmp( "NO", arg )) {
warn( "Warning from IMAP server: %s\n", cmd ); drv_print( PRN_WARN, "Message from server: %s\n", cmd );
} else if (!strcmp( "BAD", arg )) { } else if (!strcmp( "BAD", arg )) {
error( "Error from IMAP server: %s\n", cmd ); drv_print( PRN_ERROR, "Message from server: %s\n", cmd );
} else if (!strcmp( "CAPABILITY", arg )) { } else if (!strcmp( "CAPABILITY", arg )) {
parse_capability( ctx, cmd ); parse_capability( ctx, cmd );
} else if (!strcmp( "LIST", arg )) { } else if (!strcmp( "LIST", arg )) {
@ -1407,12 +1340,12 @@ imap_socket_read( void *aux )
goto listret; goto listret;
} }
} else { } else {
error( "IMAP error: unrecognized untagged response '%s'\n", arg ); drv_print( PRN_ERROR, "Unrecognized untagged IMAP response '%s'\n", arg );
break; /* this may mean anything, so prefer not to spam the log */ break; /* this may mean anything, so prefer not to spam the log */
} }
continue; continue;
} else if (!ctx->in_progress) { } else if (!ctx->in_progress) {
error( "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" ); drv_print( PRN_ERROR, "Unexpected IMAP reply: %s %s\n", arg, cmd ? cmd : "" );
break; /* this may mean anything, so prefer not to spam the log */ break; /* this may mean anything, so prefer not to spam the log */
} else if (*arg == '+') { } else if (*arg == '+') {
socket_expect_read( &ctx->conn, 0 ); socket_expect_read( &ctx->conn, 0 );
@ -1442,7 +1375,7 @@ imap_socket_read( void *aux )
if (cmdp->param.cont( ctx, cmdp, cmd )) if (cmdp->param.cont( ctx, cmdp, cmd ))
return; return;
} else { } else {
error( "IMAP error: unexpected command continuation request\n" ); drv_print( PRN_ERROR, "Unexpected IMAP command continuation request\n" );
break; break;
} }
socket_expect_read( &ctx->conn, 1 ); socket_expect_read( &ctx->conn, 1 );
@ -1451,7 +1384,7 @@ imap_socket_read( void *aux )
for (pcmdp = &ctx->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next) for (pcmdp = &ctx->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
if (cmdp->tag == tag) if (cmdp->tag == tag)
goto gottag; goto gottag;
error( "IMAP error: unexpected tag %s\n", arg ); drv_print( PRN_ERROR, "Unexpected tag '%s' in IMAP response\n", arg );
break; break;
gottag: gottag:
if (!(*pcmdp = cmdp->next)) if (!(*pcmdp = cmdp->next))
@ -1460,7 +1393,7 @@ imap_socket_read( void *aux )
socket_expect_read( &ctx->conn, 0 ); socket_expect_read( &ctx->conn, 0 );
arg = next_arg( &cmd ); arg = next_arg( &cmd );
if (!arg) { if (!arg) {
error( "IMAP error: malformed tagged response\n" ); drv_print( PRN_ERROR, "Malformed tagged IMAP response\n" );
break; break;
} }
if (!strcmp( "OK", arg )) { if (!strcmp( "OK", arg )) {
@ -1484,7 +1417,7 @@ imap_socket_read( void *aux )
goto doresp; goto doresp;
} else /*if (!strcmp( "BAD", arg ))*/ } else /*if (!strcmp( "BAD", arg ))*/
resp = RESP_CANCEL; resp = RESP_CANCEL;
error( "IMAP command '%s' returned an error: %s %s\n", drv_print( PRN_ERROR, "IMAP command '%s' returned an error: %s %s\n",
starts_with( cmdp->cmd, -1, "LOGIN", 5 ) ? starts_with( cmdp->cmd, -1, "LOGIN", 5 ) ?
"LOGIN <user> <pass>" : "LOGIN <user> <pass>" :
starts_with( cmdp->cmd, -1, "AUTHENTICATE PLAIN", 18 ) ? starts_with( cmdp->cmd, -1, "AUTHENTICATE PLAIN", 18 ) ?
@ -1799,7 +1732,7 @@ imap_open_store_authenticate( imap_store_t *ctx )
imap_exec( ctx, 0, imap_open_store_authenticate_p2, "STARTTLS" ); imap_exec( ctx, 0, imap_open_store_authenticate_p2, "STARTTLS" );
return; return;
} else { } else {
error( "IMAP error: SSL support not available\n" ); drv_print( PRN_ERROR, "IMAP server does not support STARTTLS\n" );
imap_open_store_bail( ctx, FAIL_FINAL ); imap_open_store_bail( ctx, FAIL_FINAL );
return; return;
} }
@ -1809,7 +1742,7 @@ imap_open_store_authenticate( imap_store_t *ctx )
} else { } else {
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
if (srvc->ssl_type == SSL_STARTTLS) { if (srvc->ssl_type == SSL_STARTTLS) {
error( "IMAP error: SSL support not available\n" ); drv_print( PRN_ERROR, "STARTTLS cannot be used with preauthenticated IMAP connection\n" );
imap_open_store_bail( ctx, FAIL_FINAL ); imap_open_store_bail( ctx, FAIL_FINAL );
return; return;
} }
@ -1853,7 +1786,7 @@ static const char *
ensure_user( imap_server_conf_t *srvc ) ensure_user( imap_server_conf_t *srvc )
{ {
if (!srvc->user) { if (!srvc->user) {
error( "Skipping account %s, no user\n", srvc->name ); glbl_print( PRN_ERROR, "Skipping IMAP account %s, no user\n", srvc->name );
return 0; return 0;
} }
return srvc->user; return srvc->user;
@ -1875,7 +1808,7 @@ ensure_password( imap_server_conf_t *srvc )
} }
if (!(fp = popen( cmd, "r" ))) { if (!(fp = popen( cmd, "r" ))) {
pipeerr: pipeerr:
sys_error( "Skipping account %s, password command failed", srvc->name ); glbl_print( PRN_ERROR, "Skipping IMAP account %s, password command failed: %m\n", srvc->name );
return 0; return 0;
} }
if (!fgets( buffer, sizeof(buffer), fp )) if (!fgets( buffer, sizeof(buffer), fp ))
@ -1884,13 +1817,13 @@ ensure_password( imap_server_conf_t *srvc )
goto pipeerr; goto pipeerr;
if (ret) { if (ret) {
if (WIFSIGNALED( ret )) if (WIFSIGNALED( ret ))
error( "Skipping account %s, password command crashed\n", srvc->name ); glbl_print( PRN_ERROR, "Skipping IMAP account %s, password command crashed\n", srvc->name );
else else
error( "Skipping account %s, password command exited with status %d\n", srvc->name, WEXITSTATUS( ret ) ); glbl_print( PRN_ERROR, "Skipping IMAP account %s, password command exited with status %d\n", srvc->name, WEXITSTATUS( ret ) );
return 0; return 0;
} }
if (!buffer[0]) { if (!buffer[0]) {
error( "Skipping account %s, password command produced no output\n", srvc->name ); glbl_print( PRN_ERROR, "Skipping IMAP account %s, password command produced no output\n", srvc->name );
return 0; return 0;
} }
buffer[strcspn( buffer, "\n" )] = 0; /* Strip trailing newline */ buffer[strcspn( buffer, "\n" )] = 0; /* Strip trailing newline */
@ -1907,7 +1840,7 @@ ensure_password( imap_server_conf_t *srvc )
exit( 1 ); exit( 1 );
} }
if (!*pass) { if (!*pass) {
error( "Skipping account %s, no password\n", srvc->name ); glbl_print( PRN_ERROR, "Skipping IMAP account %s, no password\n", srvc->name );
return 0; return 0;
} }
/* getpass() returns a pointer to a static buffer. Make a copy for long term storage. */ /* getpass() returns a pointer to a static buffer. Make a copy for long term storage. */
@ -1942,7 +1875,7 @@ process_sasl_interact( sasl_interact_t *interact, imap_server_conf_t *srvc )
val = ensure_password( srvc ); val = ensure_password( srvc );
break; break;
default: default:
error( "Error: Unknown SASL interaction ID\n" ); drv_print( PRN_ERROR, "Unknown SASL interaction ID\n" );
return -1; return -1;
} }
if (!val) if (!val)
@ -1968,7 +1901,7 @@ process_sasl_step( imap_store_t *ctx, int rc, const char *in, uint in_len,
} else if (rc == SASL_OK) { } else if (rc == SASL_OK) {
ctx->sasl_cont = 0; ctx->sasl_cont = 0;
} else { } else {
error( "Error: %s\n", sasl_errdetail( ctx->sasl ) ); drv_print( PRN_ERROR, "%s\n", sasl_errdetail( ctx->sasl ) );
return -1; return -1;
} }
return 0; return 0;
@ -1985,7 +1918,7 @@ decode_sasl_data( const char *prompt, char **in, uint *in_len )
rc = sasl_decode64( prompt, prompt_len, *in, prompt_len, in_len ); rc = sasl_decode64( prompt, prompt_len, *in, prompt_len, in_len );
if (rc != SASL_OK) { if (rc != SASL_OK) {
free( *in ); free( *in );
error( "Error: SASL(%d): %s\n", rc, sasl_errstring( rc, NULL, NULL ) ); drv_print( PRN_ERROR, "SASL: %s (error %d)\n", sasl_errstring( rc, NULL, NULL ), rc );
return -1; return -1;
} }
} else { } else {
@ -2004,7 +1937,7 @@ encode_sasl_data( const char *out, uint out_len, char **enc, uint *enc_len )
rc = sasl_encode64( out, out_len, *enc, enc_len_max, enc_len ); rc = sasl_encode64( out, out_len, *enc, enc_len_max, enc_len );
if (rc != SASL_OK) { if (rc != SASL_OK) {
free( *enc ); free( *enc );
error( "Error: SASL(%d): %s\n", rc, sasl_errstring( rc, NULL, NULL ) ); drv_print( PRN_ERROR, "SASL: %s (error %d)\n", sasl_errstring( rc, NULL, NULL ), rc );
return -1; return -1;
} }
return 0; return 0;
@ -2021,7 +1954,7 @@ do_sasl_auth( imap_store_t *ctx, imap_cmd_t *cmdp ATTR_UNUSED, const char *promp
conn_iovec_t iov[2]; conn_iovec_t iov[2];
if (!ctx->sasl_cont) { if (!ctx->sasl_cont) {
error( "Error: IMAP wants more steps despite successful SASL authentication.\n" ); drv_print( PRN_ERROR, "Server wants more steps despite successful SASL authentication\n" );
goto bail; goto bail;
} }
if (decode_sasl_data( prompt, &in, &in_len ) < 0) if (decode_sasl_data( prompt, &in, &in_len ) < 0)
@ -2072,9 +2005,9 @@ done_sasl_auth( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response )
uint out_len; uint out_len;
int rc = sasl_client_step( ctx->sasl, NULL, 0, &interact, &out, &out_len ); int rc = sasl_client_step( ctx->sasl, NULL, 0, &interact, &out, &out_len );
if (process_sasl_step( ctx, rc, NULL, 0, interact, &out, &out_len ) < 0) if (process_sasl_step( ctx, rc, NULL, 0, interact, &out, &out_len ) < 0)
warn( "Warning: SASL reported failure despite successful IMAP authentication. Ignoring...\n" ); drv_print( PRN_WARN, "SASL reported failure despite successful IMAP authentication. Ignoring...\n" );
else if (out) else if (out)
warn( "Warning: SASL wants more steps despite successful IMAP authentication. Ignoring...\n" ); drv_print( PRN_WARN, "SASL wants more steps despite successful IMAP authentication. Ignoring...\n" );
} }
imap_open_store_authenticate2_p2( ctx, NULL, response ); imap_open_store_authenticate2_p2( ctx, NULL, response );
@ -2095,7 +2028,7 @@ imap_open_store_authenticate2( imap_store_t *ctx )
char saslmechs[1024], *saslend = saslmechs; char saslmechs[1024], *saslend = saslmechs;
#endif #endif
info( "Logging in...\n" ); drv_print( PRN_INFO, "Logging in...\n" );
for (mech = srvc->auth_mechs; mech; mech = mech->next) { for (mech = srvc->auth_mechs; mech; mech = mech->next) {
int any = !strcmp( mech->string, "*" ); int any = !strcmp( mech->string, "*" );
for (cmech = ctx->auth_mechs; cmech; cmech = cmech->next) { for (cmech = ctx->auth_mechs; cmech; cmech = cmech->next) {
@ -2136,7 +2069,7 @@ imap_open_store_authenticate2( imap_store_t *ctx )
rc = sasl_client_init( sasl_callbacks ); rc = sasl_client_init( sasl_callbacks );
if (rc != SASL_OK) { if (rc != SASL_OK) {
saslbail: saslbail:
error( "Error: SASL(%d): %s\n", rc, sasl_errstring( rc, NULL, NULL ) ); drv_print( PRN_ERROR, "SASL: %s (error %d)\n", sasl_errstring( rc, NULL, NULL ), rc );
goto bail; goto bail;
} }
sasl_inited = 1; sasl_inited = 1;
@ -2148,7 +2081,7 @@ imap_open_store_authenticate2( imap_store_t *ctx )
goto notsasl; goto notsasl;
if (!ctx->sasl) if (!ctx->sasl)
goto saslbail; goto saslbail;
error( "Error: %s\n", sasl_errdetail( ctx->sasl ) ); drv_print( PRN_ERROR, "%s\n", sasl_errdetail( ctx->sasl ) );
goto bail; goto bail;
} }
@ -2156,7 +2089,7 @@ imap_open_store_authenticate2( imap_store_t *ctx )
if (rc == SASL_NOMECH) if (rc == SASL_NOMECH)
goto notsasl; goto notsasl;
if (gotmech) if (gotmech)
info( "Authenticating with SASL mechanism %s...\n", gotmech ); drv_print( PRN_INFO, "Authenticating with SASL mechanism %s...\n", gotmech );
/* Technically, we are supposed to loop over sasl_client_start(), /* Technically, we are supposed to loop over sasl_client_start(),
* but it just calls sasl_client_step() anyway. */ * but it just calls sasl_client_step() anyway. */
if (process_sasl_step( ctx, rc, NULL, 0, interact, CAP(SASLIR) ? &out : NULL, &out_len ) < 0) if (process_sasl_step( ctx, rc, NULL, 0, interact, CAP(SASLIR) ? &out : NULL, &out_len ) < 0)
@ -2177,11 +2110,11 @@ imap_open_store_authenticate2( imap_store_t *ctx )
if (!ctx->sasl || sasl_listmech( ctx->sasl, NULL, "", "", "", &saslavail, NULL, NULL ) != SASL_OK) if (!ctx->sasl || sasl_listmech( ctx->sasl, NULL, "", "", "", &saslavail, NULL, NULL ) != SASL_OK)
saslavail = "(none)"; /* EXTERNAL is always there anyway. */ saslavail = "(none)"; /* EXTERNAL is always there anyway. */
if (!auth_login) { if (!auth_login) {
error( "IMAP error: selected SASL mechanism(s) not available;\n" drv_print( PRN_ERROR, "Selected SASL mechanism(s) not available;\n"
" selected:%s\n available: %s\n", saslmechs, saslavail ); " selected:%s\n available: %s\n", saslmechs, saslavail );
goto skipnote; goto skipnote;
} }
info( "NOT using available SASL mechanism(s): %s\n", saslavail ); drv_print( PRN_INFO, "NOT using available SASL mechanism(s): %s\n", saslavail );
sasl_dispose( &ctx->sasl ); sasl_dispose( &ctx->sasl );
} }
#endif #endif
@ -2191,17 +2124,17 @@ imap_open_store_authenticate2( imap_store_t *ctx )
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
if (!ctx->conn.ssl) if (!ctx->conn.ssl)
#endif #endif
warn( "*** IMAP Warning *** Password is being sent in the clear\n" ); drv_print( PRN_WARN, "*** IMAP Password is being sent unencrypted\n" );
imap_exec( ctx, 0, imap_open_store_authenticate2_p2, imap_exec( ctx, 0, imap_open_store_authenticate2_p2,
"LOGIN \"%\\s\" \"%\\s\"", srvc->user, srvc->pass ); "LOGIN \"%\\s\" \"%\\s\"", srvc->user, srvc->pass );
return; return;
} }
error( "IMAP error: server supports no acceptable authentication mechanism\n" ); drv_print( PRN_ERROR, "Server supports no acceptable authentication mechanism\n" );
#ifdef HAVE_LIBSASL #ifdef HAVE_LIBSASL
skipnote: skipnote:
#endif #endif
if (skipped_login) if (skipped_login)
error( "Note: not using LOGIN because connection is not encrypted;\n" drv_print( PRN_ERROR, "Note: not using LOGIN because connection is not encrypted;\n"
" use 'AuthMechs LOGIN' explicitly to force it.\n" ); " use 'AuthMechs LOGIN' explicitly to force it.\n" );
bail: bail:
@ -3138,7 +3071,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
server->sconf.timeout = parse_int( cfg ); server->sconf.timeout = parse_int( cfg );
else if (!strcasecmp( "PipelineDepth", cfg->cmd )) { else if (!strcasecmp( "PipelineDepth", cfg->cmd )) {
if ((server->max_in_progress = parse_int( cfg )) < 1) { if ((server->max_in_progress = parse_int( cfg )) < 1) {
error( "%s:%d: PipelineDepth must be at least 1\n", cfg->file, cfg->line ); glbl_print( PRN_ERROR, "%s:%d: PipelineDepth must be at least 1\n", cfg->file, cfg->line );
cfg->err = 1; cfg->err = 1;
} }
} else if (!strcasecmp( "DisableExtension", cfg->cmd ) || } else if (!strcasecmp( "DisableExtension", cfg->cmd ) ||
@ -3151,7 +3084,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
goto gotcap; goto gotcap;
} }
} }
error( "%s:%d: Unrecognized IMAP extension '%s'\n", cfg->file, cfg->line, arg ); glbl_print( PRN_ERROR, "%s:%d: Unrecognized IMAP extension '%s'\n", cfg->file, cfg->line, arg );
cfg->err = 1; cfg->err = 1;
gotcap: ; gotcap: ;
} while ((arg = get_arg( cfg, ARG_OPTIONAL, 0 ))); } while ((arg = get_arg( cfg, ARG_OPTIONAL, 0 )));
@ -3160,7 +3093,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
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 )) {
sys_error( "%s:%d: CertificateFile '%s'", glbl_print( PRN_ERROR, "%s:%d: CertificateFile '%s': %m\n",
cfg->file, cfg->line, server->sconf.cert_file ); cfg->file, cfg->line, server->sconf.cert_file );
cfg->err = 1; cfg->err = 1;
} }
@ -3169,14 +3102,14 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
} else if (!strcasecmp( "ClientCertificate", cfg->cmd )) { } else if (!strcasecmp( "ClientCertificate", cfg->cmd )) {
server->sconf.client_certfile = expand_strdup( cfg->val ); server->sconf.client_certfile = expand_strdup( cfg->val );
if (access( server->sconf.client_certfile, R_OK )) { if (access( server->sconf.client_certfile, R_OK )) {
sys_error( "%s:%d: ClientCertificate '%s'", glbl_print( PRN_ERROR, "%s:%d: ClientCertificate '%s': %m\n",
cfg->file, cfg->line, server->sconf.client_certfile ); cfg->file, cfg->line, server->sconf.client_certfile );
cfg->err = 1; cfg->err = 1;
} }
} else if (!strcasecmp( "ClientKey", cfg->cmd )) { } else if (!strcasecmp( "ClientKey", cfg->cmd )) {
server->sconf.client_keyfile = expand_strdup( cfg->val ); server->sconf.client_keyfile = expand_strdup( cfg->val );
if (access( server->sconf.client_keyfile, R_OK )) { if (access( server->sconf.client_keyfile, R_OK )) {
sys_error( "%s:%d: ClientKey '%s'", glbl_print( PRN_ERROR, "%s:%d: ClientKey '%s': %m\n",
cfg->file, cfg->line, server->sconf.client_keyfile ); cfg->file, cfg->line, server->sconf.client_keyfile );
cfg->err = 1; cfg->err = 1;
} }
@ -3188,7 +3121,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
} else if (!strcasecmp( "IMAPS", cfg->val )) { } else if (!strcasecmp( "IMAPS", cfg->val )) {
server->ssl_type = SSL_IMAPS; server->ssl_type = SSL_IMAPS;
} else { } else {
error( "%s:%d: Invalid SSL type\n", cfg->file, cfg->line ); glbl_print( PRN_ERROR, "%s:%d: Invalid SSL type\n", cfg->file, cfg->line );
cfg->err = 1; cfg->err = 1;
} }
} else if (!strcasecmp( "SSLVersion", cfg->cmd ) || } else if (!strcasecmp( "SSLVersion", cfg->cmd ) ||
@ -3207,7 +3140,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
} else if (!strcasecmp( "TLSv1.2", arg )) { } else if (!strcasecmp( "TLSv1.2", arg )) {
server->sconf.ssl_versions |= TLSv1_2; server->sconf.ssl_versions |= TLSv1_2;
} else { } else {
error( "%s:%d: Unrecognized SSL version\n", cfg->file, cfg->line ); glbl_print( PRN_ERROR, "%s:%d: Unrecognized SSL version\n", cfg->file, cfg->line );
cfg->err = 1; cfg->err = 1;
} }
} while ((arg = get_arg( cfg, ARG_OPTIONAL, 0 ))); } while ((arg = get_arg( cfg, ARG_OPTIONAL, 0 )));
@ -3241,7 +3174,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
for (srv = servers; srv; srv = srv->next) for (srv = servers; srv; srv = srv->next)
if (srv->name && !strcmp( srv->name, cfg->val )) if (srv->name && !strcmp( srv->name, cfg->val ))
goto gotsrv; goto gotsrv;
error( "%s:%d: unknown IMAP account '%s'\n", cfg->file, cfg->line, cfg->val ); glbl_print( PRN_ERROR, "%s:%d: unknown IMAP account '%s'\n", cfg->file, cfg->line, cfg->val );
cfg->err = 1; cfg->err = 1;
continue; continue;
gotsrv: gotsrv:
@ -3252,7 +3185,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
store->gen.path = nfstrdup( cfg->val ); store->gen.path = nfstrdup( cfg->val );
else if (!strcasecmp( "PathDelimiter", cfg->cmd )) { else if (!strcasecmp( "PathDelimiter", cfg->cmd )) {
if (strlen( cfg->val ) != 1) { if (strlen( cfg->val ) != 1) {
error( "%s:%d: Path delimiter must be exactly one character long\n", cfg->file, cfg->line ); glbl_print( PRN_ERROR, "%s:%d: Path delimiter must be exactly one character long\n", cfg->file, cfg->line );
cfg->err = 1; cfg->err = 1;
continue; continue;
} }
@ -3261,7 +3194,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
parse_generic_store( &store->gen, cfg ); parse_generic_store( &store->gen, cfg );
continue; continue;
} else { } else {
error( "%s:%d: unknown/misplaced keyword '%s'\n", cfg->file, cfg->line, cfg->cmd ); glbl_print( PRN_ERROR, "%s:%d: unknown/misplaced keyword '%s'\n", cfg->file, cfg->line, cfg->cmd );
cfg->err = 1; cfg->err = 1;
continue; continue;
} }
@ -3273,19 +3206,19 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
type = "IMAP account", name = server->name; type = "IMAP account", name = server->name;
if (!store || !store->server) { if (!store || !store->server) {
if (!server->sconf.tunnel && !server->sconf.host) { if (!server->sconf.tunnel && !server->sconf.host) {
error( "%s '%s' has neither Tunnel nor Host\n", type, name ); glbl_print( PRN_ERROR, "%s '%s' has neither Tunnel nor Host\n", type, name );
cfg->err = 1; cfg->err = 1;
return 1; return 1;
} }
if (server->pass && server->pass_cmd) { if (server->pass && server->pass_cmd) {
error( "%s '%s' has both Pass and PassCmd\n", type, name ); glbl_print( PRN_ERROR, "%s '%s' has both Pass and PassCmd\n", type, name );
cfg->err = 1; cfg->err = 1;
return 1; return 1;
} }
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
if ((use_sslv2 & use_sslv3 & use_tlsv1 & use_tlsv11 & use_tlsv12) != -1 || use_imaps >= 0 || require_ssl >= 0) { if ((use_sslv2 & use_sslv3 & use_tlsv1 & use_tlsv11 & use_tlsv12) != -1 || use_imaps >= 0 || require_ssl >= 0) {
if (server->ssl_type >= 0 || server->sconf.ssl_versions >= 0) { if (server->ssl_type >= 0 || server->sconf.ssl_versions >= 0) {
error( "%s '%s': The deprecated UseSSL*, UseTLS*, UseIMAPS, and RequireSSL options are mutually exlusive with SSLType and SSLVersions.\n", type, name ); glbl_print( PRN_ERROR, "%s '%s': The deprecated UseSSL*, UseTLS*, UseIMAPS, and RequireSSL options are mutually exlusive with SSLType and SSLVersions.\n", type, name );
cfg->err = 1; cfg->err = 1;
return 1; return 1;
} }
@ -3303,11 +3236,11 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
} else if (!server->sconf.ssl_versions) { } else if (!server->sconf.ssl_versions) {
server->ssl_type = SSL_None; server->ssl_type = SSL_None;
} else { } else {
warn( "Notice: %s '%s': 'RequireSSL no' is being ignored\n", type, name ); glbl_print( PRN_NOTICE, "%s '%s': 'RequireSSL no' is being ignored\n", type, name );
server->ssl_type = SSL_STARTTLS; server->ssl_type = SSL_STARTTLS;
} }
if (server->ssl_type != SSL_None && !server->sconf.ssl_versions) { if (server->ssl_type != SSL_None && !server->sconf.ssl_versions) {
error( "%s '%s' requires SSL but no SSL versions enabled\n", type, name ); glbl_print( PRN_ERROR, "%s '%s' requires SSL but no SSL versions enabled\n", type, name );
cfg->err = 1; cfg->err = 1;
return 1; return 1;
} }
@ -3320,11 +3253,11 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
#endif #endif
if (require_cram >= 0) { if (require_cram >= 0) {
if (server->auth_mechs) { if (server->auth_mechs) {
error( "%s '%s': The deprecated RequireCRAM option is mutually exlusive with AuthMech.\n", type, name ); glbl_print( PRN_ERROR, "%s '%s': The deprecated RequireCRAM option is mutually exlusive with AuthMech.\n", type, name );
cfg->err = 1; cfg->err = 1;
return 1; return 1;
} }
warn( "Notice: %s '%s': RequireCRAM is deprecated. Use AuthMech instead.\n", type, name ); glbl_print( PRN_NOTICE, "%s '%s': RequireCRAM is deprecated. Use AuthMech instead.\n", type, name );
if (require_cram) if (require_cram)
add_string_list(&server->auth_mechs, "CRAM-MD5"); add_string_list(&server->auth_mechs, "CRAM-MD5");
} }
@ -3343,7 +3276,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
memcpy( store->server, &sserver, sizeof(sserver) ); memcpy( store->server, &sserver, sizeof(sserver) );
store->server->name = store->gen.name; store->server->name = store->gen.name;
} else if (acc_opt) { } else if (acc_opt) {
error( "%s '%s' has both Account and account-specific options\n", type, name ); glbl_print( PRN_ERROR, "%s '%s' has both Account and account-specific options\n", type, name );
cfg->err = 1; cfg->err = 1;
} }
} }

66
src/drv_maildir.c

@ -129,7 +129,7 @@ static int
maildir_ensure_path( maildir_store_conf_t *conf ) maildir_ensure_path( maildir_store_conf_t *conf )
{ {
if (!conf->gen.path) { if (!conf->gen.path) {
error( "Maildir error: store '%s' has no Path\n", conf->gen.name ); drv_print( PRN_ERROR, "Maildir store '%s' has no Path\n", conf->gen.name );
conf->failed = FAIL_FINAL; conf->failed = FAIL_FINAL;
return -1; return -1;
} }
@ -157,13 +157,13 @@ maildir_join_path( maildir_store_conf_t *conf, int in_inbox, const char *box )
for (bl = 0, n = 0; (c = box[bl]); bl++) for (bl = 0, n = 0; (c = box[bl]); bl++)
if (c == '/') { if (c == '/') {
if (conf->sub_style == SUB_UNSET) { if (conf->sub_style == SUB_UNSET) {
error( "Maildir error: accessing subfolder '%s', but store '%s' does not specify SubFolders style\n", drv_print( PRN_ERROR, "Accessing subfolder '%s', but Maildir store '%s' does not specify SubFolders style\n",
box, conf->gen.name ); box, conf->gen.name );
return 0; return 0;
} }
n++; n++;
} else if (c == '.' && conf->sub_style == SUB_MAILDIRPP) { } else if (c == '.' && conf->sub_style == SUB_MAILDIRPP) {
error( "Maildir error: store '%s', folder '%s': SubFolders style Maildir++ does not support dots in mailbox names\n", drv_print( PRN_ERROR, "Maildir store '%s', folder '%s': SubFolders style Maildir++ does not support dots in mailbox names\n",
conf->gen.name, box ); conf->gen.name, box );
return 0; return 0;
} }
@ -397,7 +397,7 @@ maildir_list_recurse( maildir_store_t *ctx, int isBox, int flags,
if (!(dir = opendir( path ))) { if (!(dir = opendir( path ))) {
if (isBox && (errno == ENOENT || errno == ENOTDIR)) if (isBox && (errno == ENOENT || errno == ENOTDIR))
return 0; return 0;
sys_error( "Maildir error: cannot list %s", path ); sys_error( "Maildir error: cannot list %s: %m\n", path );
return -1; return -1;
} }
if (isBox > 1 && style == SUB_UNSET) { if (isBox > 1 && style == SUB_UNSET) {
@ -550,7 +550,7 @@ maildir_clear_tmp( char *buf, int bufsz, int bl )
memcpy( buf + bl, "tmp/", 5 ); memcpy( buf + bl, "tmp/", 5 );
bl += 4; bl += 4;
if (!(dirp = opendir( buf ))) { if (!(dirp = opendir( buf ))) {
sys_error( "Maildir error: cannot list %s", buf ); sys_error( "Maildir error: cannot list %s: %m\n", buf );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
time( &now ); time( &now );
@ -558,14 +558,14 @@ maildir_clear_tmp( char *buf, int bufsz, int bl )
nfsnprintf( buf + bl, bufsz - bl, "%s", entry->d_name ); nfsnprintf( buf + bl, bufsz - bl, "%s", entry->d_name );
if (stat( buf, &st )) { if (stat( buf, &st )) {
if (errno != ENOENT) if (errno != ENOENT)
sys_error( "Maildir error: cannot access %s", buf ); sys_error( "Maildir error: cannot access %s: %m\n", buf );
} 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.
*/ */
notice( "Maildir notice: removing stale file %s\n", buf ); notice( "Maildir notice: removing stale file %s\n", buf );
if (unlink( buf ) && errno != ENOENT) if (unlink( buf ) && errno != ENOENT)
sys_error( "Maildir error: cannot remove %s", buf ); sys_error( "Maildir error: cannot remove %s: %m\n", buf );
} }
} }
closedir( dirp ); closedir( dirp );
@ -597,13 +597,13 @@ maildir_validate( const char *box, int create, maildir_store_t *ctx )
bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", box ); bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", box );
if (stat( buf, &st )) { if (stat( buf, &st )) {
if (errno != ENOENT) { if (errno != ENOENT) {
sys_error( "Maildir error: cannot access mailbox '%s'", box ); sys_error( "Maildir error: cannot access mailbox '%s': %m\n", box );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
if (!create) if (!create)
return DRV_BOX_BAD; return DRV_BOX_BAD;
if (make_box_dir( buf, bl )) { if (make_box_dir( buf, bl )) {
sys_error( "Maildir error: cannot create mailbox '%s'", box ); sys_error( "Maildir error: cannot create mailbox '%s': %m\n", box );
((maildir_store_conf_t *)ctx->gen.conf)->failed = FAIL_FINAL; ((maildir_store_conf_t *)ctx->gen.conf)->failed = FAIL_FINAL;
maildir_invoke_bad_callback( ctx ); maildir_invoke_bad_callback( ctx );
return DRV_CANCELED; return DRV_CANCELED;
@ -620,7 +620,7 @@ maildir_validate( const char *box, int create, maildir_store_t *ctx )
if (!i && !create) if (!i && !create)
return DRV_BOX_BAD; return DRV_BOX_BAD;
if (mkdir( buf, 0700 )) { if (mkdir( buf, 0700 )) {
sys_error( "Maildir error: cannot create directory %s", buf ); sys_error( "Maildir error: cannot create directory %s: %m\n", buf );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
ctx->fresh[i] = 1; ctx->fresh[i] = 1;
@ -736,7 +736,7 @@ maildir_uidval_lock( maildir_store_t *ctx )
#ifdef USE_DB #ifdef USE_DB
if (ctx->usedb) { if (ctx->usedb) {
if (fstat( ctx->uvfd, &st )) { if (fstat( ctx->uvfd, &st )) {
sys_error( "Maildir error: cannot fstat UID database" ); sys_error( "Maildir error: cannot fstat UID database: %m\n" );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
if (db_create( &ctx->db, 0, 0 )) { if (db_create( &ctx->db, 0, 0 )) {
@ -938,7 +938,7 @@ maildir_scan( maildir_store_t *ctx, msg_t_array_alloc_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 (stat( buf, &st )) { if (stat( buf, &st )) {
sys_error( "Maildir error: cannot stat %s", buf ); sys_error( "Maildir error: cannot stat %s: %m\n", buf );
goto dfail; goto dfail;
} }
if (st.st_mtime == now && !(DFlags & ZERODELAY) && !ctx->fresh[i]) { if (st.st_mtime == now && !(DFlags & ZERODELAY) && !ctx->fresh[i]) {
@ -955,7 +955,7 @@ maildir_scan( maildir_store_t *ctx, msg_t_array_alloc_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 ))) {
sys_error( "Maildir error: cannot list %s", buf ); sys_error( "Maildir error: cannot list %s: %m\n", buf );
rfail: rfail:
maildir_free_scan( msglist ); maildir_free_scan( msglist );
dfail: dfail:
@ -1016,7 +1016,7 @@ maildir_scan( maildir_store_t *ctx, msg_t_array_alloc_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 (stat( buf, &st )) { if (stat( buf, &st )) {
sys_error( "Maildir error: cannot re-stat %s", buf ); sys_error( "Maildir error: cannot re-stat %s: %m\n", buf );
goto rfail; goto rfail;
} }
if (st.st_mtime != stamps[i]) { if (st.st_mtime != stamps[i]) {
@ -1115,7 +1115,7 @@ maildir_scan( maildir_store_t *ctx, msg_t_array_alloc_t *msglist )
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 )) {
if (errno != ENOENT) { if (errno != ENOENT) {
sys_error( "Maildir error: cannot rename %s to %s", nbuf, buf ); sys_error( "Maildir error: cannot rename %s to %s: %m\n", nbuf, buf );
fail: fail:
maildir_free_scan( msglist ); maildir_free_scan( msglist );
return DRV_BOX_BAD; return DRV_BOX_BAD;
@ -1137,7 +1137,7 @@ maildir_scan( maildir_store_t *ctx, msg_t_array_alloc_t *msglist )
if (want_size) { if (want_size) {
if (stat( buf, &st )) { if (stat( buf, &st )) {
if (errno != ENOENT) { if (errno != ENOENT) {
sys_error( "Maildir error: cannot stat %s", buf ); sys_error( "Maildir error: cannot stat %s: %m\n", buf );
goto fail; goto fail;
} }
goto retry; goto retry;
@ -1147,7 +1147,7 @@ maildir_scan( maildir_store_t *ctx, msg_t_array_alloc_t *msglist )
if (want_tuid || want_msgid) { if (want_tuid || want_msgid) {
if (!(f = fopen( buf, "r" ))) { if (!(f = fopen( buf, "r" ))) {
if (errno != ENOENT) { if (errno != ENOENT) {
sys_error( "Maildir error: cannot open %s", buf ); sys_error( "Maildir error: cannot open %s: %m\n", buf );
goto fail; goto fail;
} }
goto retry; goto retry;
@ -1299,7 +1299,7 @@ maildir_open_box( store_t *gctx,
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;
} }
sys_error( "Maildir error: cannot write %s", uvpath ); sys_error( "Maildir error: cannot write %s: %m\n", uvpath );
cb( DRV_BOX_BAD, UIDVAL_BAD, aux ); cb( DRV_BOX_BAD, UIDVAL_BAD, aux );
return; return;
} else { } else {
@ -1356,7 +1356,7 @@ maildir_delete_box( store_t *gctx,
bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", ctx->path ); bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", ctx->path );
if (stat( buf, &st )) { if (stat( buf, &st )) {
if (errno != ENOENT) { if (errno != ENOENT) {
sys_error( "Maildir error: cannot access mailbox '%s'", ctx->path ); sys_error( "Maildir error: cannot access mailbox '%s': %m\n", ctx->path );
ret = DRV_BOX_BAD; ret = DRV_BOX_BAD;
} }
} else if (!S_ISDIR(st.st_mode)) { } else if (!S_ISDIR(st.st_mode)) {
@ -1377,7 +1377,7 @@ maildir_delete_box( store_t *gctx,
memcpy( buf + bl, subdirs[i], 4 ); memcpy( buf + bl, subdirs[i], 4 );
if (rmdir( buf ) && errno != ENOENT) { if (rmdir( buf ) && errno != ENOENT) {
badrm: badrm:
sys_error( "Maildir error: cannot remove '%s'", buf ); sys_error( "Maildir error: cannot remove '%s': %m\n", buf );
ret = DRV_BOX_BAD; ret = DRV_BOX_BAD;
break; break;
} }
@ -1394,7 +1394,7 @@ maildir_finish_delete_box( store_t *gctx )
/* Subfolders are not deleted; the deleted folder is only "stripped of its mailboxness". /* Subfolders are not deleted; the deleted folder is only "stripped of its mailboxness".
* Consequently, the rmdir may legitimately fail. This behavior follows the IMAP spec. */ * Consequently, the rmdir may legitimately fail. This behavior follows the IMAP spec. */
if (rmdir( ctx->path ) && errno != ENOENT && errno != ENOTEMPTY) { if (rmdir( ctx->path ) && errno != ENOENT && errno != ENOTEMPTY) {
sys_error( "Maildir warning: cannot remove '%s'", ctx->path ); sys_error( "Maildir warning: cannot remove '%s': %m\n", ctx->path );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
return DRV_OK; return DRV_OK;
@ -1522,7 +1522,7 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data,
nfsnprintf( buf, sizeof(buf), "%s/%s/%s", ctx->path, subdirs[gmsg->status & M_RECENT], msg->base ); nfsnprintf( buf, sizeof(buf), "%s/%s/%s", ctx->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, "Cannot open %s", buf, 0 )) != DRV_OK) { if ((ret = maildir_again( ctx, msg, "Cannot open %s: %m\n", buf, 0 )) != DRV_OK) {
cb( ret, aux ); cb( ret, aux );
return; return;
} }
@ -1533,7 +1533,7 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data,
data->date = st.st_mtime; data->date = st.st_mtime;
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) {
sys_error( "Maildir error: cannot read %s", buf ); sys_error( "Maildir error: cannot read %s: %m\n", buf );
close( fd ); close( fd );
cb( DRV_MSG_BAD, aux ); cb( DRV_MSG_BAD, aux );
return; return;
@ -1598,7 +1598,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
nfsnprintf( buf, sizeof(buf), "%s/tmp/%s%s", box, base, fbuf ); nfsnprintf( buf, sizeof(buf), "%s/tmp/%s%s", 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 || !to_trash) { if (errno != ENOENT || !to_trash) {
sys_error( "Maildir error: cannot create %s", buf ); sys_error( "Maildir error: cannot create %s: %m\n", buf );
free( data->data ); free( data->data );
cb( DRV_BOX_BAD, 0, aux ); cb( DRV_BOX_BAD, 0, aux );
return; return;
@ -1609,7 +1609,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) {
sys_error( "Maildir error: cannot create %s", buf ); sys_error( "Maildir error: cannot create %s: %m\n", buf );
free( data->data ); free( data->data );
cb( DRV_BOX_BAD, 0, aux ); cb( DRV_BOX_BAD, 0, aux );
return; return;
@ -1619,7 +1619,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
free( data->data ); free( data->data );
if (ret != data->len || (UseFSync && (ret = fsync( fd )))) { if (ret != data->len || (UseFSync && (ret = fsync( fd )))) {
if (ret < 0) if (ret < 0)
sys_error( "Maildir error: cannot write %s", buf ); sys_error( "Maildir error: cannot write %s: %m\n", buf );
else else
error( "Maildir error: cannot write %s. Disk full?\n", buf ); error( "Maildir error: cannot write %s. Disk full?\n", buf );
close( fd ); close( fd );
@ -1628,7 +1628,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
} }
if (close( fd ) < 0) { if (close( fd ) < 0) {
/* Quota exceeded may cause this. */ /* Quota exceeded may cause this. */
sys_error( "Maildir error: cannot write %s", buf ); sys_error( "Maildir error: cannot write %s: %m\n", buf );
cb( DRV_BOX_BAD, 0, aux ); cb( DRV_BOX_BAD, 0, aux );
return; return;
} }
@ -1638,7 +1638,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
struct utimbuf utimebuf; struct utimbuf utimebuf;
utimebuf.actime = utimebuf.modtime = data->date; utimebuf.actime = utimebuf.modtime = data->date;
if (utime( buf, &utimebuf ) < 0) { if (utime( buf, &utimebuf ) < 0) {
sys_error( "Maildir error: cannot set times for %s", buf ); sys_error( "Maildir error: cannot set times for %s: %m\n", buf );
cb( DRV_BOX_BAD, 0, aux ); cb( DRV_BOX_BAD, 0, aux );
return; return;
} }
@ -1647,7 +1647,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
/* 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", box, subdirs[!(data->flags & F_SEEN)], base, fbuf ); nfsnprintf( nbuf, sizeof(nbuf), "%s/%s/%s%s", box, subdirs[!(data->flags & F_SEEN)], base, fbuf );
if (rename( buf, nbuf )) { if (rename( buf, nbuf )) {
sys_error( "Maildir error: cannot rename %s to %s", buf, nbuf ); sys_error( "Maildir error: cannot rename %s to %s: %m\n", buf, nbuf );
cb( DRV_BOX_BAD, 0, aux ); cb( DRV_BOX_BAD, 0, aux );
return; return;
} }
@ -1705,7 +1705,7 @@ maildir_set_msg_flags( store_t *gctx, message_t *gmsg, uint uid ATTR_UNUSED, int
} }
if (!rename( buf, nbuf )) if (!rename( buf, nbuf ))
break; break;
if ((ret = maildir_again( ctx, msg, "Maildir error: cannot rename %s to %s", buf, nbuf )) != DRV_OK) { if ((ret = maildir_again( ctx, msg, "Maildir error: cannot rename %s to %s: %m\n", buf, nbuf )) != DRV_OK) {
cb( ret, aux ); cb( ret, aux );
return; return;
} }
@ -1762,12 +1762,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) {
sys_error( "Maildir error: cannot move %s to %s", buf, nbuf ); sys_error( "Maildir error: cannot move %s to %s: %m\n", buf, nbuf );
cb( DRV_BOX_BAD, aux ); cb( DRV_BOX_BAD, aux );
return; return;
} }
} }
if ((ret = maildir_again( ctx, msg, "Maildir error: cannot move %s to %s", buf, nbuf )) != DRV_OK) { if ((ret = maildir_again( ctx, msg, "Maildir error: cannot move %s to %s: %m\n", buf, nbuf )) != DRV_OK) {
cb( ret, aux ); cb( ret, aux );
return; return;
} }
@ -1803,7 +1803,7 @@ maildir_close_box( store_t *gctx,
if (errno == ENOENT) if (errno == ENOENT)
retry = 1; retry = 1;
else else
sys_error( "Maildir error: cannot remove %s", buf ); sys_error( "Maildir error: cannot remove %s: %m\n", buf );
} else { } else {
msg->status |= M_DEAD; msg->status |= M_DEAD;
ctx->total_msgs--; ctx->total_msgs--;

73
src/socket.c

@ -85,13 +85,13 @@ ssl_return( const char *func, conn_t *conn, int ret )
conn->read_callback( conn->callback_aux ); conn->read_callback( conn->callback_aux );
return -1; return -1;
} }
sys_error( "Socket error: secure %s %s", func, conn->name ); sock_print( PRN_ERROR, "SSL %s %s: %m\n", func, conn->name );
} else { } else {
error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, 0 ) ); sock_print( PRN_ERROR, "SSL %s %s: %s\n", func, conn->name, ERR_error_string( err, 0 ) );
} }
break; break;
default: default:
error( "Socket error: secure %s %s: unhandled SSL error %d\n", func, conn->name, err ); sock_print( PRN_ERROR, "SSL %s %s: unhandled error %d\n", func, conn->name, err );
break; break;
} }
if (conn->state == SCK_STARTTLS) if (conn->state == SCK_STARTTLS)
@ -117,7 +117,7 @@ host_matches( const char *host, const char *pattern )
} }
static int static int
verify_hostname( X509 *cert, const char *hostname ) verify_hostname( conn_t *conn, X509 *cert, const char *hostname )
{ {
int i, len, found; int i, len, found;
X509_NAME *subj; X509_NAME *subj;
@ -145,17 +145,17 @@ verify_hostname( X509 *cert, const char *hostname )
/* try the common name */ /* try the common name */
if (!(subj = X509_get_subject_name( cert ))) { if (!(subj = X509_get_subject_name( cert ))) {
error( "Error, cannot get certificate subject\n" ); sock_print( PRN_ERROR, "Cannot get subject from SSL certificate for %s\n", conn->name );
return -1; return -1;
} }
if ((len = X509_NAME_get_text_by_NID( subj, NID_commonName, cname, sizeof(cname) )) < 0) { if ((len = X509_NAME_get_text_by_NID( subj, NID_commonName, cname, sizeof(cname) )) < 0) {
error( "Error, cannot get certificate common name\n" ); sock_print( PRN_ERROR, "Cannot get SSL certificate subject's common name for %s\n", conn->name );
return -1; return -1;
} }
if (strlen( cname ) == (size_t)len && host_matches( hostname, cname )) if (strlen( cname ) == (size_t)len && host_matches( hostname, cname ))
return 0; return 0;
error( "Error, certificate owner does not match hostname %s\n", hostname ); sock_print( PRN_ERROR, "SSL certificate subject for %s does not match hostname %s\n", conn->name, hostname );
return -1; return -1;
} }
@ -169,7 +169,7 @@ verify_cert_host( const server_conf_t *conf, conn_t *sock )
cert = SSL_get_peer_certificate( sock->ssl ); cert = SSL_get_peer_certificate( sock->ssl );
if (!cert) { if (!cert) {
error( "Error, no server certificate\n" ); sock_print( PRN_ERROR, "No SSL certificate provided by %s\n", sock->name );
return -1; return -1;
} }
@ -181,21 +181,22 @@ verify_cert_host( const server_conf_t *conf, conn_t *sock )
err = SSL_get_verify_result( sock->ssl ); err = SSL_get_verify_result( sock->ssl );
if (err != X509_V_OK) { if (err != X509_V_OK) {
error( "SSL error connecting %s: %s\n", sock->name, X509_verify_cert_error_string( err ) ); sock_print( PRN_ERROR, "Cannot verify SSL certificate for %s: %s\n", sock->name, X509_verify_cert_error_string( err ) );
return -1; return -1;
} }
if (!conf->host) { if (!conf->host) {
error( "SSL error connecting %s: Neither host nor matching certificate specified\n", sock->name ); sock_print( PRN_ERROR, "Neither hostname nor matching SSL certificate specified for %s\n", sock->name );
return -1; return -1;
} }
return verify_hostname( cert, conf->host ); return verify_hostname( sock, cert, conf->host );
} }
static int static int
init_ssl_ctx( const server_conf_t *conf ) init_ssl_ctx( conn_t *conn )
{ {
const server_conf_t *conf = conn->conf;
server_conf_t *mconf = (server_conf_t *)conf; server_conf_t *mconf = (server_conf_t *)conf;
int options = 0; int options = 0;
@ -222,24 +223,24 @@ init_ssl_ctx( const server_conf_t *conf )
SSL_CTX_set_options( mconf->SSLContext, options ); SSL_CTX_set_options( mconf->SSLContext, options );
if (conf->cert_file && !SSL_CTX_load_verify_locations( mconf->SSLContext, conf->cert_file, 0 )) { if (conf->cert_file && !SSL_CTX_load_verify_locations( mconf->SSLContext, conf->cert_file, 0 )) {
error( "Error while loading certificate file '%s': %s\n", sock_print( PRN_ERROR, "Cannot load certificate file '%s': %s\n",
conf->cert_file, ERR_error_string( ERR_get_error(), 0 ) ); conf->cert_file, ERR_error_string( ERR_get_error(), 0 ) );
return 0; return 0;
} }
mconf->trusted_certs = (_STACK *)sk_X509_OBJECT_dup( X509_STORE_get0_objects( SSL_CTX_get_cert_store( mconf->SSLContext ) ) ); mconf->trusted_certs = (_STACK *)sk_X509_OBJECT_dup( X509_STORE_get0_objects( SSL_CTX_get_cert_store( mconf->SSLContext ) ) );
if (mconf->system_certs && !SSL_CTX_set_default_verify_paths( mconf->SSLContext )) if (mconf->system_certs && !SSL_CTX_set_default_verify_paths( mconf->SSLContext ))
warn( "Warning: Unable to load default certificate files: %s\n", sock_print( PRN_WARN, "Cannot load default certificate files: %s\n",
ERR_error_string( ERR_get_error(), 0 ) ); ERR_error_string( ERR_get_error(), 0 ) );
SSL_CTX_set_verify( mconf->SSLContext, SSL_VERIFY_NONE, NULL ); SSL_CTX_set_verify( mconf->SSLContext, SSL_VERIFY_NONE, NULL );
if (conf->client_certfile && !SSL_CTX_use_certificate_chain_file( mconf->SSLContext, conf->client_certfile)) { if (conf->client_certfile && !SSL_CTX_use_certificate_chain_file( mconf->SSLContext, conf->client_certfile)) {
error( "Error while loading client certificate file '%s': %s\n", sock_print( PRN_ERROR, "Cannot load client certificate file '%s': %s\n",
conf->client_certfile, ERR_error_string( ERR_get_error(), 0 ) ); conf->client_certfile, ERR_error_string( ERR_get_error(), 0 ) );
return 0; return 0;
} }
if (conf->client_keyfile && !SSL_CTX_use_PrivateKey_file( mconf->SSLContext, conf->client_keyfile, SSL_FILETYPE_PEM)) { if (conf->client_keyfile && !SSL_CTX_use_PrivateKey_file( mconf->SSLContext, conf->client_keyfile, SSL_FILETYPE_PEM)) {
error( "Error while loading client private key '%s': %s\n", sock_print( PRN_ERROR, "Cannot load client private key '%s': %s\n",
conf->client_keyfile, ERR_error_string( ERR_get_error(), 0 ) ); conf->client_keyfile, ERR_error_string( ERR_get_error(), 0 ) );
return 0; return 0;
} }
@ -265,7 +266,7 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) )
ssl_inited = 1; ssl_inited = 1;
} }
if (!init_ssl_ctx( conn->conf )) { if (!init_ssl_ctx( conn )) {
start_tls_p3( conn, 0 ); start_tls_p3( conn, 0 );
return; return;
} }
@ -286,7 +287,7 @@ start_tls_p2( conn_t *conn )
if (verify_cert_host( conn->conf, conn )) { if (verify_cert_host( conn->conf, conn )) {
start_tls_p3( conn, 0 ); start_tls_p3( conn, 0 );
} else { } else {
info( "Connection is now encrypted\n" ); sock_print( PRN_INFO, "Connection is now encrypted\n" );
start_tls_p3( conn, 1 ); start_tls_p3( conn, 1 );
} }
} }
@ -379,7 +380,7 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
int a[2]; int a[2];
nfasprintf( &sock->name, "tunnel '%s'", conf->tunnel ); nfasprintf( &sock->name, "tunnel '%s'", conf->tunnel );
infon( "Starting %s... ", sock->name ); sock_print( PRN_INFO | PRN_NONL, "Starting %s... ", sock->name );
if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) { if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) {
perror( "socketpair" ); perror( "socketpair" );
@ -398,9 +399,10 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
close( a[0] ); close( a[0] );
socket_open_internal( sock, a[1] ); socket_open_internal( sock, a[1] );
info( "\vok\n" ); sock_print( PRN_INFO | PRN_CONT, "ok\n" );
socket_connected( sock ); socket_connected( sock );
} else { } else {
sock_print( PRN_INFO | PRN_NONL, "Resolving %s... ", conf->host );
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
int gaierr; int gaierr;
struct addrinfo hints; struct addrinfo hints;
@ -409,29 +411,26 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG; hints.ai_flags = AI_ADDRCONFIG;
infon( "Resolving %s... ", conf->host );
if ((gaierr = getaddrinfo( conf->host, NULL, &hints, &sock->addrs ))) { if ((gaierr = getaddrinfo( conf->host, NULL, &hints, &sock->addrs ))) {
error( "Error: Cannot resolve server '%s': %s\n", conf->host, gai_strerror( gaierr ) ); sock_print( PRN_ERROR, "Cannot resolve server '%s': %s\n", conf->host, gai_strerror( gaierr ) );
socket_connect_bail( sock ); socket_connect_bail( sock );
return; return;
} }
info( "\vok\n" );
sock->curr_addr = sock->addrs; sock->curr_addr = sock->addrs;
#else #else
struct hostent *he; struct hostent *he;
infon( "Resolving %s... ", conf->host );
he = gethostbyname( conf->host ); he = gethostbyname( conf->host );
if (!he) { if (!he) {
error( "Error: Cannot resolve server '%s': %s\n", conf->host, hstrerror( h_errno ) ); sock_print( PRN_ERROR, "Cannot resolve server '%s': %s\n", conf->host, hstrerror( h_errno ) );
socket_connect_bail( sock ); socket_connect_bail( sock );
return; return;
} }
info( "\vok\n" );
sock->curr_addr = he->h_addr_list; sock->curr_addr = he->h_addr_list;
#endif #endif
sock_print( PRN_INFO | PRN_CONT, "ok\n" );
socket_connect_one( sock ); socket_connect_one( sock );
} }
} }
@ -453,7 +452,7 @@ socket_connect_one( conn_t *sock )
#else #else
if (!*sock->curr_addr) { if (!*sock->curr_addr) {
#endif #endif
error( "No working address found for %s\n", sock->conf->host ); sock_print( PRN_ERROR, "No working address found for %s\n", sock->conf->host );
socket_connect_bail( sock ); socket_connect_bail( sock );
return; return;
} }
@ -490,7 +489,7 @@ socket_connect_one( conn_t *sock )
} }
socket_open_internal( sock, s ); socket_open_internal( sock, s );
infon( "Connecting to %s... ", sock->name ); sock_print( PRN_INFO | PRN_NONL, "Connecting to %s... ", sock->name );
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (connect( s, ai->ai_addr, ai->ai_addrlen )) { if (connect( s, ai->ai_addr, ai->ai_addrlen )) {
#else #else
@ -503,17 +502,17 @@ socket_connect_one( conn_t *sock )
conf_notifier( &sock->notify, 0, POLLOUT ); conf_notifier( &sock->notify, 0, POLLOUT );
socket_expect_read( sock, 1 ); socket_expect_read( sock, 1 );
sock->state = SCK_CONNECTING; sock->state = SCK_CONNECTING;
info( "\v\n" ); sock_print( PRN_INFO | PRN_CONT, "\n" );
return; return;
} }
info( "\vok\n" ); sock_print( PRN_INFO | PRN_CONT, "ok\n" );
socket_connected( sock ); socket_connected( sock );
} }
static void static void
socket_connect_failed( conn_t *conn ) socket_connect_failed( conn_t *conn )
{ {
sys_error( "Cannot connect to %s", conn->name ); sock_print( PRN_ERROR, "Cannot connect to %s: %m\n", conn->name );
socket_close_internal( conn ); socket_close_internal( conn );
free( conn->name ); free( conn->name );
conn->name = 0; conn->name = 0;
@ -595,7 +594,7 @@ prepare_read( conn_t *sock, char **buf, int *len )
{ {
int n = sock->offset + sock->bytes; int n = sock->offset + sock->bytes;
if (!(*len = sizeof(sock->buf) - n)) { if (!(*len = sizeof(sock->buf) - n)) {
error( "Socket error: receive buffer full. Probably protocol error.\n" ); sock_print( PRN_ERROR, "Socket receive buffer full. Probably protocol error.\n" );
socket_fail( sock ); socket_fail( sock );
return -1; return -1;
} }
@ -622,7 +621,7 @@ do_read( conn_t *sock, char *buf, int len )
#endif #endif
{ {
if ((n = read( sock->fd, buf, len )) < 0) { if ((n = read( sock->fd, buf, len )) < 0) {
sys_error( "Socket error: read from %s", sock->name ); sock_print( PRN_ERROR, "Read from %s failed", sock->name );
socket_fail( sock ); socket_fail( sock );
} else if (!n) { } else if (!n) {
/* EOF. Callers take the short path out, so signal higher layers from here. */ /* EOF. Callers take the short path out, so signal higher layers from here. */
@ -649,7 +648,7 @@ socket_fill_z( conn_t *sock )
ret = inflate( sock->in_z, Z_SYNC_FLUSH ); ret = inflate( sock->in_z, Z_SYNC_FLUSH );
if (ret != Z_OK && ret != Z_STREAM_END) { if (ret != Z_OK && ret != Z_STREAM_END) {
error( "Error decompressing data from %s: %s\n", sock->name, sock->in_z->msg ); sock_print( PRN_ERROR, "Cannot decompress data from %s: %s\n", sock->name, sock->in_z->msg );
socket_fail( sock ); socket_fail( sock );
return; return;
} }
@ -758,7 +757,7 @@ do_write( conn_t *sock, char *buf, int len )
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) {
sys_error( "Socket error: write to %s", sock->name ); sock_print( PRN_ERROR, "Write to %s failed: %m\n", sock->name );
socket_fail( sock ); socket_fail( sock );
} else { } else {
n = 0; n = 0;
@ -967,7 +966,7 @@ socket_fd_cb( int events, void *aux )
socket_connected( conn ); socket_connected( conn );
return; return;
} }
sys_error( "Socket error from %s", conn->name ); sock_print( PRN_ERROR, "Unexpected event on %s: %m\n", conn->name );
socket_fail( conn ); socket_fail( conn );
return; return;
} }
@ -1015,7 +1014,7 @@ socket_timeout_cb( void *aux )
errno = ETIMEDOUT; errno = ETIMEDOUT;
socket_connect_failed( conn ); socket_connect_failed( conn );
} else { } else {
error( "Socket error on %s: timeout.\n", conn->name ); sock_print( PRN_ERROR, "Timeout on %s\n", conn->name );
socket_fail( conn ); socket_fail( conn );
} }
} }

18
src/sync.c

@ -69,7 +69,7 @@ void
Fclose( FILE *f, int safe ) Fclose( FILE *f, int safe )
{ {
if ((safe && (fflush( f ) || (UseFSync && fdatasync( fileno( f ) )))) || fclose( f ) == EOF) { if ((safe && (fflush( f ) || (UseFSync && fdatasync( fileno( f ) )))) || fclose( f ) == EOF) {
sys_error( "Error: cannot close file" ); glbl_print( PRN_ERROR, "Cannot close file: %m\n" );
exit( 1 ); exit( 1 );
} }
} }
@ -81,7 +81,7 @@ vFprintf( FILE *f, const char *msg, va_list va )
r = vfprintf( f, msg, va ); r = vfprintf( f, msg, va );
if (r < 0) { if (r < 0) {
sys_error( "Error: cannot write file" ); glbl_print( PRN_ERROR, "Cannot write file: %m\n" );
exit( 1 ); exit( 1 );
} }
} }
@ -614,7 +614,7 @@ prepare_state( sync_vars_t *svars )
} }
*s = 0; *s = 0;
if (mkdir( svars->dname, 0700 ) && errno != EEXIST) { if (mkdir( svars->dname, 0700 ) && errno != EEXIST) {
sys_error( "Error: cannot create SyncState directory '%s'", svars->dname ); drv_print( PRN_ERROR, "Cannot create SyncState directory '%s': %m\n", svars->dname );
return 0; return 0;
} }
*s = '/'; *s = '/';
@ -640,7 +640,7 @@ lock_state( sync_vars_t *svars )
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) {
sys_error( "Error: cannot create lock file %s", svars->lname ); drv_print( PRN_ERROR, "Cannot create lock file %s: %m\n", svars->lname );
return 0; return 0;
} }
if (fcntl( svars->lfd, F_SETLK, &lck )) { if (fcntl( svars->lfd, F_SETLK, &lck )) {
@ -801,7 +801,7 @@ load_state( sync_vars_t *svars )
svars->existing = 1; svars->existing = 1;
} else { } else {
if (errno != ENOENT) { if (errno != ENOENT) {
sys_error( "Error: cannot read sync state %s", svars->dname ); drv_print( PRN_ERROR, "Cannot read sync state %s: %m\n", svars->dname );
return 0; return 0;
} }
svars->existing = 0; svars->existing = 0;
@ -963,7 +963,7 @@ load_state( sync_vars_t *svars )
fclose( jfp ); fclose( jfp );
} else { } else {
if (errno != ENOENT) { if (errno != ENOENT) {
sys_error( "Error: cannot read journal %s", svars->jname ); drv_print( PRN_ERROR, "Cannot read journal %s: %m\n", svars->jname );
return 0; return 0;
} }
} }
@ -978,7 +978,7 @@ delete_state( sync_vars_t *svars )
unlink( svars->nname ); unlink( svars->nname );
unlink( svars->jname ); unlink( svars->jname );
if (unlink( svars->dname ) || unlink( svars->lname )) { if (unlink( svars->dname ) || unlink( svars->lname )) {
sys_error( "Error: channel %s: sync state cannot be deleted", svars->chan->name ); drv_print( PRN_ERROR, "Channel %s: sync state cannot be deleted: %m\n", svars->chan->name );
svars->ret = SYNC_FAIL; svars->ret = SYNC_FAIL;
} }
} }
@ -1221,11 +1221,11 @@ box_opened2( sync_vars_t *svars, int t )
if (!lock_state( svars )) if (!lock_state( svars ))
goto bail; goto bail;
if (!(svars->nfp = fopen( svars->nname, "w" ))) { if (!(svars->nfp = fopen( svars->nname, "w" ))) {
sys_error( "Error: cannot create new sync state %s", svars->nname ); drv_print( PRN_ERROR, "Cannot create new sync state %s: %m\n", svars->nname );
goto bail; goto bail;
} }
if (!(svars->jfp = fopen( svars->jname, "a" ))) { if (!(svars->jfp = fopen( svars->jname, "a" ))) {
sys_error( "Error: cannot create journal %s", svars->jname ); drv_print( PRN_ERROR, "Cannot create journal %s: %m\n", svars->jname );
fclose( svars->nfp ); fclose( svars->nfp );
goto bail; goto bail;
} }

127
src/util.c

@ -23,8 +23,10 @@
#include "common.h" #include "common.h"
#include <assert.h> #include <assert.h>
#include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
@ -43,16 +45,27 @@ flushn( void )
} }
static void static void
printn( const char *msg, va_list va ) vprintn( const char *msg, va_list va )
{ {
if (*msg == '\v') if (*msg == '\v')
msg++; msg++;
else else
flushn(); flushn();
vprintf( msg, va ); char buf[1000];
fwrite( buf, 1, nfevprintf( buf, buf + sizeof(buf), msg, va ) - buf, stdout );
fflush( stdout ); fflush( stdout );
} }
static void
printn( const char *msg, ... )
{
va_list va;
va_start( va, msg );
vprintn( msg, va );
va_end( va );
}
void void
vdebug( int cat, const char *msg, va_list va ) vdebug( int cat, const char *msg, va_list va )
{ {
@ -92,7 +105,7 @@ info( const char *msg, ... )
if (DFlags & VERBOSE) { if (DFlags & VERBOSE) {
va_start( va, msg ); va_start( va, msg );
printn( msg, va ); vprintn( msg, va );
va_end( va ); va_end( va );
need_nl = 0; need_nl = 0;
} }
@ -105,7 +118,7 @@ infon( const char *msg, ... )
if (DFlags & VERBOSE) { if (DFlags & VERBOSE) {
va_start( va, msg ); va_start( va, msg );
printn( msg, va ); vprintn( msg, va );
va_end( va ); va_end( va );
need_nl = 1; need_nl = 1;
} }
@ -115,10 +128,12 @@ void
notice( const char *msg, ... ) notice( const char *msg, ... )
{ {
va_list va; va_list va;
char buf[1000];
if (!(DFlags & QUIET)) { if (!(DFlags & QUIET)) {
flushn();
va_start( va, msg ); va_start( va, msg );
printn( msg, va ); fwrite( buf, 1, nfeprintf( buf, buf + sizeof(buf), "Notice: %v", msg, &va ) - buf, stdout );
va_end( va ); va_end( va );
need_nl = 0; need_nl = 0;
} }
@ -128,11 +143,12 @@ void
warn( const char *msg, ... ) warn( const char *msg, ... )
{ {
va_list va; va_list va;
char buf[1000];
if (!(DFlags & VERYQUIET)) { if (!(DFlags & VERYQUIET)) {
flushn(); flushn();
va_start( va, msg ); va_start( va, msg );
vfprintf( stderr, msg, va ); fwrite( buf, 1, nfeprintf( buf, buf + sizeof(buf), "WARNING: %v", msg, &va ) - buf, stderr );
va_end( va ); va_end( va );
} }
} }
@ -141,10 +157,11 @@ void
error( const char *msg, ... ) error( const char *msg, ... )
{ {
va_list va; va_list va;
char buf[1000];
flushn(); flushn();
va_start( va, msg ); va_start( va, msg );
vfprintf( stderr, msg, va ); fwrite( buf, 1, nfeprintf( buf, buf + sizeof(buf), "ERROR: %v", msg, &va ) - buf, stderr );
va_end( va ); va_end( va );
} }
@ -162,6 +179,98 @@ sys_error( const char *msg, ... )
perror( buf ); perror( buf );
} }
/* Minimal printf() core with some extensions:
* - %m to print strerror(errno) (GNU compatible extension)
* - %\s to print backslash-escaped IMAP string literals
* - %v to recurse formatting; expects (const char *fmt, va_list *ap) as parameters
*/
char *
nfevprintf( char *d, char *ed, const char *fmt, va_list ap )
{
const char *s = fmt;
for (;;) {
char c = *fmt;
if (!c || c == '%') {
int l = fmt - s;
if (d + l > ed)
oob();
memcpy( d, s, l );
d += l;
if (!c)
return d;
int maxlen = INT_MAX;
c = *++fmt;
if (c == '\\') {
c = *++fmt;
if (c != 's') {
fputs( "Fatal: unsupported escaped format specifier. Please report a bug.\n", stderr );
abort();
}
s = va_arg( ap, const char * );
while ((c = *s++)) {
if (d + 2 > ed)
oob();
if (c == '\\' || c == '"')
*d++ = '\\';
*d++ = c;
}
} else { /* \\ cannot be combined with anything else. */
if (c == '.') {
c = *++fmt;
if (c != '*') {
fputs( "Fatal: unsupported string length specification. Please report a bug.\n", stderr );
abort();
}
maxlen = va_arg( ap, int );
c = *++fmt;
}
if (c == 'c') {
if (d + 1 > ed)
oob();
*d++ = (char)va_arg( ap, int );
} else if (c == 's') {
s = va_arg( ap, const char * );
l = strnlen( s, maxlen );
cpstr:
if (d + l > ed)
oob();
memcpy( d, s, l );
d += l;
} else if (c == 'm') {
s = strerror( errno );
l = strlen( s );
goto cpstr;
} else if (c == 'v') {
s = va_arg( ap, const char * );
va_list *nap = va_arg( ap, va_list * );
d = nfevprintf( d, ed, s, *nap );
} else if (c == 'd') {
d += nfsnprintf( d, ed - d, "%d", va_arg( ap, int ) );
} else if (c == 'u') {
d += nfsnprintf( d, ed - d, "%u", va_arg( ap , uint ) );
} else {
fputs( "Fatal: unsupported format specifier. Please report a bug.\n", stderr );
abort();
}
}
s = ++fmt;
} else {
fmt++;
}
}
}
char *
nfeprintf( char *d, char *ed, const char *fmt, ... )
{
va_list va;
va_start( va, fmt );
char *ret = nfevprintf( d, ed, fmt, va );
va_end( va );
return ret;
}
void void
add_string_list_n( string_list_t **list, const char *str, int len ) add_string_list_n( string_list_t **list, const char *str, int len )
{ {
@ -579,11 +688,11 @@ arc4_init( void )
uchar j, si, dat[128]; uchar j, si, dat[128];
if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) { if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) {
error( "Fatal: no random number source available.\n" ); glbl_print( PRN_FATAL, "No random number source available.\n" );
exit( 3 ); exit( 3 );
} }
if (read( fd, dat, 128 ) != 128) { if (read( fd, dat, 128 ) != 128) {
error( "Fatal: cannot read random number source.\n" ); glbl_print( PRN_FATAL, "Cannot read random number source.\n" );
exit( 3 ); exit( 3 );
} }
close( fd ); close( fd );

Loading…
Cancel
Save