add support for hierarchical mailboxes
This commit is contained in:
parent
4f94197e41
commit
2585dd3324
|
@ -12,7 +12,7 @@ fi
|
||||||
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
|
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
|
||||||
|
|
||||||
AC_CHECK_HEADERS(sys/poll.h sys/select.h)
|
AC_CHECK_HEADERS(sys/poll.h sys/select.h)
|
||||||
AC_CHECK_FUNCS(vasprintf)
|
AC_CHECK_FUNCS(vasprintf memrchr)
|
||||||
|
|
||||||
AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
|
AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
|
||||||
AC_CHECK_LIB(nsl, inet_ntoa, [SOCK_LIBS="$SOCK_LIBS -lnsl"])
|
AC_CHECK_LIB(nsl, inet_ntoa, [SOCK_LIBS="$SOCK_LIBS -lnsl"])
|
||||||
|
|
126
src/drv_imap.c
126
src/drv_imap.c
|
@ -50,6 +50,7 @@ typedef struct imap_store_conf {
|
||||||
store_conf_t gen;
|
store_conf_t gen;
|
||||||
imap_server_conf_t *server;
|
imap_server_conf_t *server;
|
||||||
unsigned use_namespace:1;
|
unsigned use_namespace:1;
|
||||||
|
char delimiter;
|
||||||
} imap_store_conf_t;
|
} imap_store_conf_t;
|
||||||
|
|
||||||
typedef struct imap_message {
|
typedef struct imap_message {
|
||||||
|
@ -82,6 +83,7 @@ typedef struct imap_store {
|
||||||
/* trash folder's existence is not confirmed yet */
|
/* trash folder's existence is not confirmed yet */
|
||||||
enum { TrashUnknown, TrashChecking, TrashKnown } trashnc;
|
enum { TrashUnknown, TrashChecking, TrashKnown } trashnc;
|
||||||
unsigned got_namespace:1;
|
unsigned got_namespace:1;
|
||||||
|
char delimiter; /* hierarchy delimiter */
|
||||||
list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
|
list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
|
||||||
message_t **msgapp; /* FETCH results */
|
message_t **msgapp; /* FETCH results */
|
||||||
unsigned caps; /* CAPABILITY results */
|
unsigned caps; /* CAPABILITY results */
|
||||||
|
@ -808,21 +810,63 @@ parse_list_rsp( imap_store_t *ctx, char *cmd )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
free_list( list );
|
free_list( list );
|
||||||
(void) next_arg( &cmd ); /* skip delimiter */
|
|
||||||
arg = next_arg( &cmd );
|
arg = next_arg( &cmd );
|
||||||
|
if (!ctx->delimiter)
|
||||||
|
ctx->delimiter = *arg;
|
||||||
|
arg = next_arg( &cmd );
|
||||||
|
if (memcmp( arg, "INBOX", 5 ) || (arg[5] && arg[5] != ctx->delimiter)) {
|
||||||
l = strlen( ctx->gen.conf->path );
|
l = strlen( ctx->gen.conf->path );
|
||||||
if (memcmp( arg, ctx->gen.conf->path, l ))
|
if (memcmp( arg, ctx->gen.conf->path, l ))
|
||||||
return;
|
return;
|
||||||
arg += l;
|
arg += l;
|
||||||
if (l && !strcmp( arg, "INBOX" )) {
|
if (!memcmp( arg, "INBOX", 5 ) && (!arg[5] || arg[5] == ctx->delimiter)) {
|
||||||
|
if (!arg[5])
|
||||||
warn( "IMAP warning: ignoring INBOX in %s\n", ctx->gen.conf->path );
|
warn( "IMAP warning: ignoring INBOX in %s\n", ctx->gen.conf->path );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!memcmp( arg + strlen( arg ) - 5, ".lock", 5 )) /* workaround broken servers */
|
if (!memcmp( arg + strlen( arg ) - 5, ".lock", 5 )) /* workaround broken servers */
|
||||||
return;
|
return;
|
||||||
|
if (map_name( arg, ctx->delimiter, '/') < 0) {
|
||||||
|
warn( "IMAP warning: ignoring mailbox %s (reserved character '/' in name)\n", arg );
|
||||||
|
return;
|
||||||
|
}
|
||||||
add_string_list( &ctx->gen.boxes, arg );
|
add_string_list( &ctx->gen.boxes, arg );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
prepare_name( char *buf, const imap_store_t *ctx, const char *prefix, const char *name )
|
||||||
|
{
|
||||||
|
int pl;
|
||||||
|
|
||||||
|
nfsnprintf( buf, 1024, "%s%n%s", prefix, &pl, name );
|
||||||
|
switch (map_name( buf + pl, '/', ctx->delimiter )) {
|
||||||
|
case -1:
|
||||||
|
error( "IMAP error: mailbox name %s contains server's hierarchy delimiter\n", buf + pl );
|
||||||
|
return -1;
|
||||||
|
case -2:
|
||||||
|
error( "IMAP error: server's hierarchy delimiter not known\n" );
|
||||||
|
return -1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
prepare_box( char *buf, const imap_store_t *ctx )
|
||||||
|
{
|
||||||
|
const char *name = ctx->gen.name;
|
||||||
|
|
||||||
|
return prepare_name( buf, ctx,
|
||||||
|
(!memcmp( name, "INBOX", 5 ) && (!name[5] || name[5] == '/')) ? "" : ctx->prefix, name );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
prepare_trash( char *buf, const imap_store_t *ctx )
|
||||||
|
{
|
||||||
|
return prepare_name( buf, ctx, ctx->prefix, ctx->gen.conf->trash );
|
||||||
|
}
|
||||||
|
|
||||||
struct imap_cmd_trycreate {
|
struct imap_cmd_trycreate {
|
||||||
struct imap_cmd gen;
|
struct imap_cmd gen;
|
||||||
struct imap_cmd *orig_cmd;
|
struct imap_cmd *orig_cmd;
|
||||||
|
@ -1157,6 +1201,7 @@ imap_open_store( store_conf_t *conf,
|
||||||
ctx->gen.boxes = 0;
|
ctx->gen.boxes = 0;
|
||||||
ctx->gen.listed = 0;
|
ctx->gen.listed = 0;
|
||||||
ctx->gen.conf = conf;
|
ctx->gen.conf = conf;
|
||||||
|
ctx->delimiter = 0;
|
||||||
ctx->callbacks.imap_open = cb;
|
ctx->callbacks.imap_open = cb;
|
||||||
ctx->callback_aux = aux;
|
ctx->callback_aux = aux;
|
||||||
set_bad_callback( &ctx->gen, (void (*)(void *))imap_open_store_bail, ctx );
|
set_bad_callback( &ctx->gen, (void (*)(void *))imap_open_store_bail, ctx );
|
||||||
|
@ -1367,10 +1412,9 @@ imap_open_store_namespace( imap_store_t *ctx )
|
||||||
{
|
{
|
||||||
imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf;
|
imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf;
|
||||||
|
|
||||||
ctx->prefix = "";
|
|
||||||
if (*cfg->gen.path)
|
|
||||||
ctx->prefix = cfg->gen.path;
|
ctx->prefix = cfg->gen.path;
|
||||||
else if (cfg->use_namespace && CAP(NAMESPACE)) {
|
ctx->delimiter = cfg->delimiter;
|
||||||
|
if (((!*ctx->prefix && cfg->use_namespace) || !cfg->delimiter) && CAP(NAMESPACE)) {
|
||||||
/* get NAMESPACE info */
|
/* get NAMESPACE info */
|
||||||
if (!ctx->got_namespace)
|
if (!ctx->got_namespace)
|
||||||
imap_exec( ctx, 0, imap_open_store_namespace_p2, "NAMESPACE" );
|
imap_exec( ctx, 0, imap_open_store_namespace_p2, "NAMESPACE" );
|
||||||
|
@ -1395,11 +1439,20 @@ imap_open_store_namespace_p2( imap_store_t *ctx, struct imap_cmd *cmd ATTR_UNUSE
|
||||||
static void
|
static void
|
||||||
imap_open_store_namespace2( imap_store_t *ctx )
|
imap_open_store_namespace2( imap_store_t *ctx )
|
||||||
{
|
{
|
||||||
/* XXX for now assume personal namespace */
|
imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf;
|
||||||
if (is_list( ctx->ns_personal ) &&
|
list_t *nsp, *nsp_1st, *nsp_1st_ns, *nsp_1st_dl;
|
||||||
is_list( ctx->ns_personal->child ) &&
|
|
||||||
is_atom( ctx->ns_personal->child->child ))
|
/* XXX for now assume 1st personal namespace */
|
||||||
ctx->prefix = ctx->ns_personal->child->child->val;
|
if (is_list( (nsp = ctx->ns_personal) ) &&
|
||||||
|
is_list( (nsp_1st = nsp->child) ) &&
|
||||||
|
is_atom( (nsp_1st_ns = nsp_1st->child) ) &&
|
||||||
|
is_atom( (nsp_1st_dl = nsp_1st_ns->next) ))
|
||||||
|
{
|
||||||
|
if (!*ctx->prefix && cfg->use_namespace)
|
||||||
|
ctx->prefix = nsp_1st_ns->val;
|
||||||
|
if (!ctx->delimiter)
|
||||||
|
ctx->delimiter = *nsp_1st_dl->val;
|
||||||
|
}
|
||||||
imap_open_store_finalize( ctx );
|
imap_open_store_finalize( ctx );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1446,15 +1499,14 @@ imap_select( store_t *gctx, int create,
|
||||||
{
|
{
|
||||||
imap_store_t *ctx = (imap_store_t *)gctx;
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
||||||
struct imap_cmd_simple *cmd;
|
struct imap_cmd_simple *cmd;
|
||||||
const char *prefix;
|
char buf[1024];
|
||||||
|
|
||||||
free_generic_messages( gctx->msgs );
|
free_generic_messages( gctx->msgs );
|
||||||
gctx->msgs = 0;
|
gctx->msgs = 0;
|
||||||
|
|
||||||
if (!strcmp( gctx->name, "INBOX" )) {
|
if (prepare_box( buf, ctx ) < 0) {
|
||||||
prefix = "";
|
cb( DRV_BOX_BAD, aux );
|
||||||
} else {
|
return;
|
||||||
prefix = ctx->prefix;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->gen.uidnext = 0;
|
ctx->gen.uidnext = 0;
|
||||||
|
@ -1463,7 +1515,7 @@ imap_select( store_t *gctx, int create,
|
||||||
cmd->gen.param.create = create;
|
cmd->gen.param.create = create;
|
||||||
cmd->gen.param.trycreate = 1;
|
cmd->gen.param.trycreate = 1;
|
||||||
imap_exec( ctx, &cmd->gen, imap_done_simple_box,
|
imap_exec( ctx, &cmd->gen, imap_done_simple_box,
|
||||||
"SELECT \"%s%s\"", prefix, gctx->name );
|
"SELECT \"%s\"", buf );
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************* imap_load *******************/
|
/******************* imap_load *******************/
|
||||||
|
@ -1636,13 +1688,17 @@ imap_trash_msg( store_t *gctx, message_t *msg,
|
||||||
{
|
{
|
||||||
imap_store_t *ctx = (imap_store_t *)gctx;
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
||||||
struct imap_cmd_simple *cmd;
|
struct imap_cmd_simple *cmd;
|
||||||
|
char buf[1024];
|
||||||
|
|
||||||
INIT_IMAP_CMD(imap_cmd_simple, cmd, cb, aux)
|
INIT_IMAP_CMD(imap_cmd_simple, cmd, cb, aux)
|
||||||
cmd->gen.param.create = 1;
|
cmd->gen.param.create = 1;
|
||||||
cmd->gen.param.to_trash = 1;
|
cmd->gen.param.to_trash = 1;
|
||||||
|
if (prepare_trash( buf, ctx ) < 0) {
|
||||||
|
cb( DRV_BOX_BAD, aux );
|
||||||
|
return;
|
||||||
|
}
|
||||||
imap_exec( ctx, &cmd->gen, imap_done_simple_msg,
|
imap_exec( ctx, &cmd->gen, imap_done_simple_msg,
|
||||||
"UID COPY %d \"%s%s\"",
|
"UID COPY %d \"%s\"", msg->uid, buf );
|
||||||
msg->uid, ctx->prefix, gctx->conf->trash );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************* imap_store_msg *******************/
|
/******************* imap_store_msg *******************/
|
||||||
|
@ -1655,9 +1711,8 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
|
||||||
{
|
{
|
||||||
imap_store_t *ctx = (imap_store_t *)gctx;
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
||||||
struct imap_cmd_out_uid *cmd;
|
struct imap_cmd_out_uid *cmd;
|
||||||
const char *prefix, *box;
|
|
||||||
int d;
|
int d;
|
||||||
char flagstr[128];
|
char flagstr[128], buf[1024];
|
||||||
|
|
||||||
d = 0;
|
d = 0;
|
||||||
if (data->flags) {
|
if (data->flags) {
|
||||||
|
@ -1672,16 +1727,20 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
|
||||||
cmd->out_uid = -2;
|
cmd->out_uid = -2;
|
||||||
|
|
||||||
if (to_trash) {
|
if (to_trash) {
|
||||||
box = gctx->conf->trash;
|
|
||||||
prefix = ctx->prefix;
|
|
||||||
cmd->gen.param.create = 1;
|
cmd->gen.param.create = 1;
|
||||||
cmd->gen.param.to_trash = 1;
|
cmd->gen.param.to_trash = 1;
|
||||||
|
if (prepare_trash( buf, ctx ) < 0) {
|
||||||
|
cb( DRV_BOX_BAD, -1, aux );
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
box = gctx->name;
|
if (prepare_box( buf, ctx ) < 0) {
|
||||||
prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix;
|
cb( DRV_BOX_BAD, -1, aux );
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
|
imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
|
||||||
"APPEND \"%s%s\" %s", prefix, box, flagstr );
|
"APPEND \"%s\" %s", buf, flagstr );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1710,15 +1769,20 @@ imap_find_new_msgs( store_t *gctx,
|
||||||
/******************* imap_list *******************/
|
/******************* imap_list *******************/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
imap_list( store_t *gctx,
|
imap_list( store_t *gctx, int flags,
|
||||||
void (*cb)( int sts, void *aux ), void *aux )
|
void (*cb)( int sts, void *aux ), void *aux )
|
||||||
{
|
{
|
||||||
imap_store_t *ctx = (imap_store_t *)gctx;
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
||||||
struct imap_cmd_simple *cmd;
|
struct imap_cmd_refcounted_state *sts = imap_refcounted_new_state( cb, aux );
|
||||||
|
|
||||||
INIT_IMAP_CMD(imap_cmd_simple, cmd, cb, aux)
|
if (((flags & LIST_PATH) &&
|
||||||
imap_exec( ctx, &cmd->gen, imap_done_simple_box,
|
imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
|
||||||
"LIST \"\" \"%s%%\"", ctx->prefix );
|
"LIST \"\" \"%s*\"", ctx->prefix ) < 0) ||
|
||||||
|
((flags & LIST_INBOX) && (!(flags & LIST_PATH) || *ctx->prefix) &&
|
||||||
|
imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
|
||||||
|
"LIST \"\" INBOX*" ) < 0))
|
||||||
|
{}
|
||||||
|
imap_refcounted_done( sts );
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************* imap_cancel *******************/
|
/******************* imap_cancel *******************/
|
||||||
|
@ -1853,6 +1917,8 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep, int *err )
|
||||||
store->use_namespace = parse_bool( cfg );
|
store->use_namespace = parse_bool( cfg );
|
||||||
else if (!strcasecmp( "Path", cfg->cmd ))
|
else if (!strcasecmp( "Path", cfg->cmd ))
|
||||||
store->gen.path = nfstrdup( cfg->val );
|
store->gen.path = nfstrdup( cfg->val );
|
||||||
|
else if (!strcasecmp( "PathDelimiter", cfg->cmd ))
|
||||||
|
store->delimiter = *cfg->val;
|
||||||
else
|
else
|
||||||
parse_generic_store( &store->gen, cfg, err );
|
parse_generic_store( &store->gen, cfg, err );
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -94,6 +94,29 @@ maildir_parse_flags( const char *base )
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
maildir_join_path( const char *prefix, const char *box )
|
||||||
|
{
|
||||||
|
char *out, *p;
|
||||||
|
int pl, bl, n;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
pl = strlen( prefix );
|
||||||
|
for (bl = 0, n = 0; (c = box[bl]); bl++)
|
||||||
|
if (c == '/')
|
||||||
|
n++;
|
||||||
|
out = nfmalloc( pl + bl + n + 1 );
|
||||||
|
memcpy( out, prefix, pl );
|
||||||
|
p = out + pl;
|
||||||
|
while ((c = *box++)) {
|
||||||
|
*p++ = c;
|
||||||
|
if (c == '/')
|
||||||
|
*p++ = '.';
|
||||||
|
}
|
||||||
|
*p = 0;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
maildir_open_store( store_conf_t *conf,
|
maildir_open_store( store_conf_t *conf,
|
||||||
void (*cb)( store_t *ctx, void *aux ), void *aux )
|
void (*cb)( store_t *ctx, void *aux ), void *aux )
|
||||||
|
@ -109,7 +132,8 @@ maildir_open_store( store_conf_t *conf,
|
||||||
ctx = nfcalloc( sizeof(*ctx) );
|
ctx = nfcalloc( sizeof(*ctx) );
|
||||||
ctx->gen.conf = conf;
|
ctx->gen.conf = conf;
|
||||||
ctx->uvfd = -1;
|
ctx->uvfd = -1;
|
||||||
nfasprintf( &ctx->trash, "%s%s", conf->path, conf->trash );
|
if (conf->trash)
|
||||||
|
ctx->trash = maildir_join_path( conf->path, conf->trash );
|
||||||
cb( &ctx->gen, aux );
|
cb( &ctx->gen, aux );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,40 +192,87 @@ maildir_invoke_bad_callback( store_t *ctx )
|
||||||
ctx->bad_callback( ctx->bad_callback_aux );
|
ctx->bad_callback( ctx->bad_callback_aux );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int maildir_list_part( store_t *gctx, int doInbox, int *flags );
|
||||||
maildir_list( store_t *gctx,
|
|
||||||
void (*cb)( int sts, void *aux ), void *aux )
|
static int
|
||||||
|
maildir_list_recurse( store_t *gctx, int isBox, int *flags, const char *inbox,
|
||||||
|
char *path, int pathLen, char *name, int nameLen )
|
||||||
{
|
{
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
|
int pl, nl;
|
||||||
struct dirent *de;
|
struct dirent *de;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
if (!(dir = opendir( gctx->conf->path ))) {
|
if (isBox) {
|
||||||
sys_error( "Maildir error: cannot list %s", gctx->conf->path );
|
nfsnprintf( path + pathLen, _POSIX_PATH_MAX - pathLen, "/cur" );
|
||||||
maildir_invoke_bad_callback( gctx );
|
if (stat( path, &st ) || !S_ISDIR(st.st_mode))
|
||||||
cb( DRV_CANCELED, aux );
|
return 0;
|
||||||
return;
|
path[pathLen] = 0;
|
||||||
|
add_string_list( &gctx->boxes, name );
|
||||||
|
name[nameLen++] = '/';
|
||||||
|
}
|
||||||
|
if (!(dir = opendir( path ))) {
|
||||||
|
sys_error( "Maildir error: cannot list %s", path );
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
while ((de = readdir( dir ))) {
|
while ((de = readdir( dir ))) {
|
||||||
const char *inbox = ((maildir_store_conf_t *)gctx->conf)->inbox;
|
const char *ent = de->d_name;
|
||||||
int bl, isibx;
|
pl = pathLen + nfsnprintf( path + pathLen, _POSIX_PATH_MAX - pathLen, "%s", ent );
|
||||||
struct stat st;
|
if (inbox && !memcmp( path, inbox, pl ) && !inbox[pl]) {
|
||||||
char buf[PATH_MAX];
|
if (maildir_list_part( gctx, 1, flags ) < 0)
|
||||||
|
return -1;
|
||||||
if (*de->d_name == '.')
|
} else {
|
||||||
continue;
|
if (!memcmp( ent, "INBOX", 6 )) {
|
||||||
bl = nfsnprintf( buf, sizeof(buf), "%s%s/cur", gctx->conf->path, de->d_name );
|
path[pathLen] = 0;
|
||||||
if (stat( buf, &st ) || !S_ISDIR(st.st_mode))
|
warn( "Maildir warning: ignoring INBOX in %s\n", path );
|
||||||
continue;
|
|
||||||
isibx = !memcmp( buf, inbox, bl - 4 ) && !inbox[bl - 4];
|
|
||||||
if (!isibx && !strcmp( de->d_name, "INBOX" )) {
|
|
||||||
warn( "Maildir warning: ignoring INBOX in %s\n", gctx->conf->path );
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
add_string_list( &gctx->boxes, isibx ? "INBOX" : de->d_name );
|
if (*ent == '.') {
|
||||||
|
if (!isBox)
|
||||||
|
continue;
|
||||||
|
ent++;
|
||||||
|
} else {
|
||||||
|
if (isBox)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nl = nameLen + nfsnprintf( name + nameLen, _POSIX_PATH_MAX - nameLen, "%s", ent );
|
||||||
|
if (maildir_list_recurse( gctx, 1, flags, inbox, path, pl, name, nl ) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
closedir (dir);
|
closedir (dir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
maildir_list_part( store_t *gctx, int doInbox, int *flags )
|
||||||
|
{
|
||||||
|
int pl, nl;
|
||||||
|
const char *inbox = ((maildir_store_conf_t *)gctx->conf)->inbox;
|
||||||
|
char path[_POSIX_PATH_MAX], name[_POSIX_PATH_MAX];
|
||||||
|
|
||||||
|
if (doInbox) {
|
||||||
|
*flags &= ~LIST_INBOX;
|
||||||
|
pl = nfsnprintf( path, _POSIX_PATH_MAX, "%s", inbox );
|
||||||
|
nl = nfsnprintf( name, _POSIX_PATH_MAX, "INBOX" );
|
||||||
|
return maildir_list_recurse( gctx, 1, flags, 0, path, pl, name, nl );
|
||||||
|
} else {
|
||||||
|
pl = nfsnprintf( path, _POSIX_PATH_MAX, "%s", gctx->conf->path );
|
||||||
|
return maildir_list_recurse( gctx, 0, flags, inbox, path, pl, name, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
maildir_list( store_t *gctx, int flags,
|
||||||
|
void (*cb)( int sts, void *aux ), void *aux )
|
||||||
|
{
|
||||||
|
if (((flags & LIST_PATH) && maildir_list_part( gctx, 0, &flags ) < 0) ||
|
||||||
|
((flags & LIST_INBOX) && maildir_list_part( gctx, 1, &flags ) < 0)) {
|
||||||
|
maildir_invoke_bad_callback( gctx );
|
||||||
|
cb( DRV_CANCELED, aux );
|
||||||
|
} else {
|
||||||
cb( DRV_OK, aux );
|
cb( DRV_OK, aux );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *subdirs[] = { "cur", "new", "tmp" };
|
static const char *subdirs[] = { "cur", "new", "tmp" };
|
||||||
|
@ -237,8 +308,9 @@ maildir_validate( const char *box, int create, maildir_store_t *ctx )
|
||||||
{
|
{
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
|
char *p;
|
||||||
time_t now;
|
time_t now;
|
||||||
int i, bl;
|
int i, bl, ret;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
char buf[_POSIX_PATH_MAX];
|
char buf[_POSIX_PATH_MAX];
|
||||||
|
|
||||||
|
@ -246,6 +318,13 @@ maildir_validate( const char *box, int create, maildir_store_t *ctx )
|
||||||
if (stat( buf, &st )) {
|
if (stat( buf, &st )) {
|
||||||
if (errno == ENOENT) {
|
if (errno == ENOENT) {
|
||||||
if (create) {
|
if (create) {
|
||||||
|
p = memrchr( buf, '/', bl - 1 );
|
||||||
|
if (*(p + 1) == '.') {
|
||||||
|
*p = 0;
|
||||||
|
if ((ret = maildir_validate( buf, 1, ctx )) != DRV_OK)
|
||||||
|
return ret;
|
||||||
|
*p = '/';
|
||||||
|
}
|
||||||
if (mkdir( buf, 0700 )) {
|
if (mkdir( buf, 0700 )) {
|
||||||
sys_error( "Maildir error: cannot create mailbox '%s'", buf );
|
sys_error( "Maildir error: cannot create mailbox '%s'", buf );
|
||||||
maildir_invoke_bad_callback( &ctx->gen );
|
maildir_invoke_bad_callback( &ctx->gen );
|
||||||
|
@ -822,10 +901,10 @@ maildir_select( store_t *gctx, int create,
|
||||||
#ifdef USE_DB
|
#ifdef USE_DB
|
||||||
ctx->db = 0;
|
ctx->db = 0;
|
||||||
#endif /* USE_DB */
|
#endif /* USE_DB */
|
||||||
if (!strcmp( gctx->name, "INBOX" ))
|
gctx->path =
|
||||||
gctx->path = nfstrdup( ((maildir_store_conf_t *)gctx->conf)->inbox );
|
(!memcmp( gctx->name, "INBOX", 5 ) && (!gctx->name[5] || gctx->name[5] == '/')) ?
|
||||||
else
|
maildir_join_path( ((maildir_store_conf_t *)gctx->conf)->inbox, gctx->name + 5 ) :
|
||||||
nfasprintf( &gctx->path, "%s%s", gctx->conf->path, gctx->name );
|
maildir_join_path( gctx->conf->path, gctx->name );
|
||||||
|
|
||||||
if ((ret = maildir_validate( gctx->path, create, ctx )) != DRV_OK) {
|
if ((ret = maildir_validate( gctx->path, create, ctx )) != DRV_OK) {
|
||||||
cb( ret, aux );
|
cb( ret, aux );
|
||||||
|
|
13
src/isync.h
13
src/isync.h
|
@ -259,6 +259,9 @@ typedef struct {
|
||||||
*/
|
*/
|
||||||
#define DRV_CRLF 1
|
#define DRV_CRLF 1
|
||||||
|
|
||||||
|
#define LIST_PATH 1
|
||||||
|
#define LIST_INBOX 2
|
||||||
|
|
||||||
struct driver {
|
struct driver {
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
|
@ -283,8 +286,8 @@ struct driver {
|
||||||
* Pending commands will have their callbacks synchronously invoked with DRV_CANCELED. */
|
* Pending commands will have their callbacks synchronously invoked with DRV_CANCELED. */
|
||||||
void (*cancel_store)( store_t *ctx );
|
void (*cancel_store)( store_t *ctx );
|
||||||
|
|
||||||
/* List the mailboxes in this store. */
|
/* List the mailboxes in this store. Flags are ORed LIST_* values. */
|
||||||
void (*list)( store_t *ctx,
|
void (*list)( store_t *ctx, int flags,
|
||||||
void (*cb)( int sts, void *aux ), void *aux );
|
void (*cb)( int sts, void *aux ), void *aux );
|
||||||
|
|
||||||
/* Invoked before select(), this informs the driver which operations (OP_*)
|
/* Invoked before select(), this informs the driver which operations (OP_*)
|
||||||
|
@ -415,6 +418,10 @@ void free_string_list( string_list_t *list );
|
||||||
|
|
||||||
void free_generic_messages( message_t * );
|
void free_generic_messages( message_t * );
|
||||||
|
|
||||||
|
#ifndef HAVE_MEMRCHR
|
||||||
|
void *memrchr( const void *s, int c, size_t n );
|
||||||
|
#endif
|
||||||
|
|
||||||
void *nfmalloc( size_t sz );
|
void *nfmalloc( size_t sz );
|
||||||
void *nfcalloc( size_t sz );
|
void *nfcalloc( size_t sz );
|
||||||
void *nfrealloc( void *mem, size_t sz );
|
void *nfrealloc( void *mem, size_t sz );
|
||||||
|
@ -426,6 +433,8 @@ void ATTR_NORETURN oob( void );
|
||||||
|
|
||||||
char *expand_strdup( const char *s );
|
char *expand_strdup( const char *s );
|
||||||
|
|
||||||
|
int map_name( char *arg, char in, char out );
|
||||||
|
|
||||||
void sort_ints( int *arr, int len );
|
void sort_ints( int *arr, int len );
|
||||||
|
|
||||||
void arc4_init( void );
|
void arc4_init( void );
|
||||||
|
|
11
src/main.c
11
src/main.c
|
@ -129,7 +129,7 @@ matches( const char *t, const char *p )
|
||||||
} else if (*p == '%') {
|
} else if (*p == '%') {
|
||||||
p++;
|
p++;
|
||||||
do {
|
do {
|
||||||
if (*t == '.' || *t == '/') /* this is "somewhat" hacky ... */
|
if (*t == '/')
|
||||||
return 0;
|
return 0;
|
||||||
if (matches( t, p ))
|
if (matches( t, p ))
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -690,6 +690,8 @@ static void
|
||||||
store_opened( store_t *ctx, void *aux )
|
store_opened( store_t *ctx, void *aux )
|
||||||
{
|
{
|
||||||
MVARS(aux)
|
MVARS(aux)
|
||||||
|
string_list_t *cpat;
|
||||||
|
int flags;
|
||||||
|
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
mvars->ret = mvars->skip = 1;
|
mvars->ret = mvars->skip = 1;
|
||||||
|
@ -699,8 +701,13 @@ store_opened( store_t *ctx, void *aux )
|
||||||
}
|
}
|
||||||
mvars->ctx[t] = ctx;
|
mvars->ctx[t] = ctx;
|
||||||
if (!mvars->skip && !mvars->boxlist && mvars->chan->patterns && !ctx->listed) {
|
if (!mvars->skip && !mvars->boxlist && mvars->chan->patterns && !ctx->listed) {
|
||||||
|
for (flags = 0, cpat = mvars->chan->patterns; cpat; cpat = cpat->next) {
|
||||||
|
const char *pat = cpat->string;
|
||||||
|
if (*pat != '!')
|
||||||
|
flags |= (!memcmp( pat, "INBOX", 5 ) && (!pat[5] || pat[5] == '/')) ? LIST_INBOX : LIST_PATH;
|
||||||
|
}
|
||||||
set_bad_callback( ctx, store_bad, AUX );
|
set_bad_callback( ctx, store_bad, AUX );
|
||||||
mvars->drv[t]->list( ctx, store_listed, AUX );
|
mvars->drv[t]->list( ctx, flags, store_listed, AUX );
|
||||||
} else {
|
} else {
|
||||||
mvars->state[t] = ST_OPEN;
|
mvars->state[t] = ST_OPEN;
|
||||||
sync_chans( mvars, E_OPEN );
|
sync_chans( mvars, E_OPEN );
|
||||||
|
|
13
src/mbsync.1
13
src/mbsync.1
|
@ -105,6 +105,13 @@ There are two auxiliary object classes: Accounts and Groups. An Account
|
||||||
describes the connection part of remote Stores, so a server connection can be
|
describes the connection part of remote Stores, so a server connection can be
|
||||||
shared between multiple Stores. A Group aggregates multiple Channels to
|
shared between multiple Stores. A Group aggregates multiple Channels to
|
||||||
save typing on the command line.
|
save typing on the command line.
|
||||||
|
.P
|
||||||
|
File system locations (in particular, \fBPath\fR and \fBInbox\fR) use the
|
||||||
|
Store's internal path separators, which may be slashes, periods, etc., or
|
||||||
|
even combinations thereof.
|
||||||
|
.br
|
||||||
|
Mailbox names, OTOH, always use canonical path separators, which are
|
||||||
|
Unix-like forward slashes.
|
||||||
..
|
..
|
||||||
.SS All Stores
|
.SS All Stores
|
||||||
These options can be used in all supported Store types.
|
These options can be used in all supported Store types.
|
||||||
|
@ -140,6 +147,7 @@ If \fIsize\fR is 0, the maximum message size is \fBunlimited\fR.
|
||||||
Create a virtual mailbox (relative to \fBPath\fR), which is backed by
|
Create a virtual mailbox (relative to \fBPath\fR), which is backed by
|
||||||
the \fBINBOX\fR. Makes sense in conjunction with \fBPatterns\fR in the
|
the \fBINBOX\fR. Makes sense in conjunction with \fBPatterns\fR in the
|
||||||
Channels section.
|
Channels section.
|
||||||
|
This virtual mailbox does not support subfolders.
|
||||||
..
|
..
|
||||||
.TP
|
.TP
|
||||||
\fBTrash\fR \fImailbox\fR
|
\fBTrash\fR \fImailbox\fR
|
||||||
|
@ -306,6 +314,11 @@ mailbox names. Disabling this makes sense for some broken IMAP servers.
|
||||||
This option is meaningless if a \fBPath\fR was specified.
|
This option is meaningless if a \fBPath\fR was specified.
|
||||||
(Default: \fIyes\fR)
|
(Default: \fIyes\fR)
|
||||||
..
|
..
|
||||||
|
.TP
|
||||||
|
\fBPathDelimiter\fR \fIdelim\fR
|
||||||
|
Specify the server's hierarchy delimiter character.
|
||||||
|
(Default: taken from the server's first "personal" NAMESPACE)
|
||||||
|
..
|
||||||
.SS Channels
|
.SS Channels
|
||||||
.TP
|
.TP
|
||||||
\fBChannel\fR \fIname\fR
|
\fBChannel\fR \fIname\fR
|
||||||
|
|
36
src/util.c
36
src/util.c
|
@ -229,6 +229,19 @@ vasprintf( char **strp, const char *fmt, va_list ap )
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_MEMRCHR
|
||||||
|
void *
|
||||||
|
memrchr( const void *s, int c, size_t n )
|
||||||
|
{
|
||||||
|
u_char *b = (u_char *)s, *e = b + n;
|
||||||
|
|
||||||
|
while (--e >= b)
|
||||||
|
if (*e == c)
|
||||||
|
return (void *)e;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
oob( void )
|
oob( void )
|
||||||
{
|
{
|
||||||
|
@ -378,6 +391,29 @@ expand_strdup( const char *s )
|
||||||
return nfstrdup( s );
|
return nfstrdup( s );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return value: 0 = ok, -1 = out found in arg, -2 = in found in arg but no out specified */
|
||||||
|
int
|
||||||
|
map_name( char *arg, char in, char out )
|
||||||
|
{
|
||||||
|
int l, k;
|
||||||
|
|
||||||
|
if (!in || in == out)
|
||||||
|
return 0;
|
||||||
|
for (l = 0; arg[l]; l++)
|
||||||
|
if (arg[l] == in) {
|
||||||
|
if (!out)
|
||||||
|
return -2;
|
||||||
|
arg[l] = out;
|
||||||
|
} else if (arg[l] == out) {
|
||||||
|
/* restore original name for printing error message */
|
||||||
|
for (k = 0; k < l; k++)
|
||||||
|
if (arg[k] == out)
|
||||||
|
arg[k] = in;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
compare_ints( const void *l, const void *r )
|
compare_ints( const void *l, const void *r )
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user