Mercurial > dovecot > core-2.2
changeset 15162:e3175ee39483
Merged changes from v2.1 tree.
line wrap: on
line diff
--- a/.hgsigs Wed Sep 26 17:17:08 2012 +0300 +++ b/.hgsigs Wed Sep 26 18:01:01 2012 +0300 @@ -49,3 +49,4 @@ c92fb8b928f69ca01681a2c2976304b7e4bc3afc 0 iEYEABECAAYFAk/FIeIACgkQyUhSUUBVisk4IgCfUiXVXntqzPjJcALYRpqw4Zc7a/0An3HKWwgb6PBCbmvxBfTezNkqjqVK 7e5f36fd989d27a2fb48250adbab8fa54ddb6083 0 iEYEABECAAYFAk/yVakACgkQyUhSUUBVismekwCfSEVQjd6fwdChjd53LSt03b4UWKoAoIxd/IjLatTISlHm44iiQwzRKByo bc86680293d256d5a8009690caeb73ab2e34e359 0 iEYEABECAAYFAlAZaTUACgkQyUhSUUBVisnTAACfU1pB34RrXEyLnpnL4Ee/oeNBYcoAnRWxTqx870Efjwf+eBPzafO0C/NU +1a6c3b4e92e4174d3b1eb0a7c841f97e8fb9e590 0 iEYEABECAAYFAlBYwJMACgkQyUhSUUBVisn2PwCeIJxfB5ebXlAbtMcjrZBCmB8Kg1sAn39BC9rQoR/wjD2/ix1JaxH7gHOT
--- a/.hgtags Wed Sep 26 17:17:08 2012 +0300 +++ b/.hgtags Wed Sep 26 18:01:01 2012 +0300 @@ -86,3 +86,4 @@ c92fb8b928f69ca01681a2c2976304b7e4bc3afc 2.1.7 7e5f36fd989d27a2fb48250adbab8fa54ddb6083 2.1.8 bc86680293d256d5a8009690caeb73ab2e34e359 2.1.9 +1a6c3b4e92e4174d3b1eb0a7c841f97e8fb9e590 2.1.10
--- a/NEWS Wed Sep 26 17:17:08 2012 +0300 +++ b/NEWS Wed Sep 26 18:01:01 2012 +0300 @@ -19,6 +19,32 @@ + LMTP proxy: Implemented XCLIENT extension for passing remote IP address through proxy. +v2.1.10 2012-09-18 Timo Sirainen <tss@iki.fi> + + + imap: Implemented THREAD=ORDEREDSUBJECT extension. + + Added "doveadm exec" command to easily execute commands from + libexec_dir, e.g. "doveadm exec imap -u user@domain" + + Added "doveadm copy" command. + + doveadm copy/move: Added optional user parameter to specify the + source username. This allows easily copying mails between different + users. + + Added namespace { disabled } setting to quickly enable/disable + namespaces. This is especially useful when its value is returned by + userdb. + + Added mailbox_alias plugin. It allows creating mailbox aliases using + symlinks. + + imapc storage: Added imapc_max_idle_time setting to force activity + on connection. + + fts-solr: Expunging multiple messages is now faster. + - director: In some conditions director may have disconnected from + another director (without logging about it), thinking it was sending + invalid data. + - imap: Various fixes to listing mailboxes. + - pop3-migration plugin: Avoid disconnection from POP3 server due + to idling. + - login processes crashed if there were a lot of local {} or remote {} + settings blocks. + v2.1.9 2012-08-01 Timo Sirainen <tss@iki.fi> * mail-log plugin: Log mailbox names with UTF-8 everywhere
--- a/configure.in Wed Sep 26 17:17:08 2012 +0300 +++ b/configure.in Wed Sep 26 18:01:01 2012 +0300 @@ -2820,6 +2820,7 @@ src/plugins/lazy-expunge/Makefile src/plugins/listescape/Makefile src/plugins/mail-log/Makefile +src/plugins/mailbox-alias/Makefile src/plugins/notify/Makefile src/plugins/pop3-migration/Makefile src/plugins/quota/Makefile
--- a/doc/example-config/conf.d/20-lmtp.conf Wed Sep 26 17:17:08 2012 +0300 +++ b/doc/example-config/conf.d/20-lmtp.conf Wed Sep 26 18:01:01 2012 +0300 @@ -10,7 +10,11 @@ # lda_mailbox_autocreate settings. #lmtp_save_to_detail_mailbox = no +# Verify quota before replying to RCPT TO. This adds a small overhead. +#lmtp_rcpt_check_quota = no + protocol lmtp { # Space separated list of plugins to load (default is global mail_plugins). #mail_plugins = $mail_plugins } + \ No newline at end of file
--- a/src/auth/auth-request.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/auth/auth-request.c Wed Sep 26 18:01:01 2012 +0300 @@ -1820,18 +1820,27 @@ { '\0', NULL, NULL } }; -const struct var_expand_table * -auth_request_get_var_expand_table(const struct auth_request *auth_request, - auth_request_escape_func_t *escape_func) +struct var_expand_table * +auth_request_get_var_expand_table_full(const struct auth_request *auth_request, + auth_request_escape_func_t *escape_func, + unsigned int *count) { - struct var_expand_table *tab; + const unsigned int auth_count = + N_ELEMENTS(auth_request_var_expand_static_tab); + struct var_expand_table *tab, *ret_tab; if (escape_func == NULL) escape_func = escape_none; - tab = t_malloc(sizeof(auth_request_var_expand_static_tab)); + /* keep the extra fields at the beginning. the last static_tab field + contains the ending NULL-fields. */ + tab = ret_tab = t_malloc((*count + auth_count) * sizeof(*tab)); + memset(tab, 0, *count * sizeof(*tab)); + tab += *count; + *count += auth_count; + memcpy(tab, auth_request_var_expand_static_tab, - sizeof(auth_request_var_expand_static_tab)); + auth_count * sizeof(*tab)); tab[0].value = escape_func(auth_request->user, auth_request); tab[1].value = escape_func(t_strcut(auth_request->user, '@'), @@ -1878,7 +1887,17 @@ } tab[18].value = auth_request->session_id == NULL ? NULL : escape_func(auth_request->session_id, auth_request); - return tab; + return ret_tab; +} + +const struct var_expand_table * +auth_request_get_var_expand_table(const struct auth_request *auth_request, + auth_request_escape_func_t *escape_func) +{ + unsigned int count = 0; + + return auth_request_get_var_expand_table_full(auth_request, escape_func, + &count); } static void get_log_prefix(string_t *str, struct auth_request *auth_request,
--- a/src/auth/auth-request.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/auth/auth-request.h Wed Sep 26 18:01:01 2012 +0300 @@ -212,6 +212,10 @@ auth_request_get_var_expand_table(const struct auth_request *auth_request, auth_request_escape_func_t *escape_func) ATTR_NULL(2); +struct var_expand_table * +auth_request_get_var_expand_table_full(const struct auth_request *auth_request, + auth_request_escape_func_t *escape_func, + unsigned int *count) ATTR_NULL(2); const char *auth_request_str_escape(const char *string, const struct auth_request *request);
--- a/src/auth/db-ldap.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/auth/db-ldap.c Wed Sep 26 18:01:01 2012 +0300 @@ -63,7 +63,6 @@ /* attribute name => value */ HASH_TABLE(char *, struct db_ldap_value *) ldap_attrs; - struct var_expand_table *var_table; const char *val_1_arr[2]; string_t *var, *debug; @@ -1068,21 +1067,17 @@ *attr_names_r = array_idx_modifiable(&ctx.attr_names, 0); } -static struct var_expand_table * -db_ldap_value_get_var_expand_table(pool_t pool, - struct auth_request *auth_request) +static const struct var_expand_table * +db_ldap_value_get_var_expand_table(struct auth_request *auth_request, + const char *ldap_value) { - const struct var_expand_table *auth_table = NULL; struct var_expand_table *table; - unsigned int count; + unsigned int count = 1; - auth_table = auth_request_get_var_expand_table(auth_request, NULL); - for (count = 0; auth_table[count].key != '\0'; count++) ; - count++; - - table = p_new(pool, struct var_expand_table, count + 2); + table = auth_request_get_var_expand_table_full(auth_request, NULL, + &count); table[0].key = '$'; - memcpy(table + 1, auth_table, sizeof(*table) * count); + table[0].value = ldap_value; return table; } @@ -1238,6 +1233,7 @@ { "ldap", db_ldap_field_expand }, { NULL, NULL } }; + const struct var_expand_table *var_table; const char *const *values; if (ldap_value != NULL) @@ -1263,14 +1259,18 @@ "using value '%s'", field->name, values[0]); } - if (ctx->var_table == NULL) { - ctx->var_table = db_ldap_value_get_var_expand_table( - ctx->pool, ctx->auth_request); + + /* do this lookup separately for each expansion, because: + 1) the values are allocated from data stack + 2) if "user" field is updated, we want %u/%n/%d updated + (and less importantly the same for other variables) */ + var_table = db_ldap_value_get_var_expand_table(ctx->auth_request, + values[0]); + if (ctx->var == NULL) ctx->var = str_new(ctx->pool, 256); - } - ctx->var_table[0].value = values[0]; - str_truncate(ctx->var, 0); - var_expand_with_funcs(ctx->var, field->value, ctx->var_table, + else + str_truncate(ctx->var, 0); + var_expand_with_funcs(ctx->var, field->value, var_table, var_funcs_table, ctx); ctx->val_1_arr[0] = str_c(ctx->var); values = ctx->val_1_arr;
--- a/src/auth/passdb-imap.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/auth/passdb-imap.c Wed Sep 26 18:01:01 2012 +0300 @@ -85,6 +85,7 @@ t_strconcat(auth_request->set->base_dir, "/", DNS_CLIENT_SOCKET_NAME, NULL); set.password = password; + set.max_idle_time = IMAPC_DEFAULT_MAX_IDLE_TIME; if (module->set_have_vars) { str = t_str_new(128);
--- a/src/auth/userdb-static.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/auth/userdb-static.c Wed Sep 26 18:01:01 2012 +0300 @@ -42,6 +42,8 @@ { struct static_context *ctx = auth_request->context; + auth_request->userdb_lookup = TRUE; + auth_request->private_callback.userdb = ctx->old_callback; auth_request->context = ctx->old_context; auth_request_set_state(auth_request, AUTH_REQUEST_STATE_USERDB); @@ -92,6 +94,10 @@ auth_request->context = ctx; if (auth_request->passdb != NULL) { + /* kludge: temporarily work as if we weren't doing + a userdb lookup. this is to get auth cache to use + passdb caching instead of userdb caching. */ + auth_request->userdb_lookup = FALSE; auth_request_lookup_credentials(auth_request, "", static_credentials_callback); } else {
--- a/src/config/config-parser.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/config/config-parser.c Wed Sep 26 18:01:01 2012 +0300 @@ -255,10 +255,14 @@ } max_bits = IPADDR_IS_V4(&ips[0]) ? 32 : 128; - if (p == NULL || str_to_uint(p, &bits) < 0 || bits > max_bits) + if (p == NULL) *bits_r = max_bits; - else + else if (str_to_uint(p, &bits) == 0 && bits <= max_bits) *bits_r = bits; + else { + *error_r = t_strdup_printf("Invalid network mask: %s", p); + return -1; + } return 0; }
--- a/src/director/director-connection.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/director/director-connection.c Wed Sep 26 18:01:01 2012 +0300 @@ -71,6 +71,8 @@ mark the host as failed so we won't try to reconnect to it immediately */ #define DIRECTOR_SUCCESS_MIN_CONNECT_SECS 40 #define DIRECTOR_WAIT_DISCONNECT_SECS 10 +#define DIRECTOR_HANDSHAKE_WARN_SECS 29 +#define DIRECTOR_HANDSHAKE_BYTES_LOG_MIN_SECS (60*30) #if DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS <= DIRECTOR_CONNECTION_PING_TIMEOUT_MSECS # error DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS is too low @@ -92,6 +94,8 @@ /* for incoming connections the director host isn't known until ME-line is received */ struct director_host *host; + /* this is set only for wrong connections: */ + struct director_host *connect_request_to; int fd; struct io *io; @@ -118,7 +122,9 @@ }; static void director_connection_disconnected(struct director_connection **conn); -static void director_connection_reconnect(struct director_connection **conn); +static void director_connection_reconnect(struct director_connection **conn, + const char *reason); +static void director_connection_log_disconnect(struct director_connection *conn); static void ATTR_FORMAT(2, 3) director_cmd_error(struct director_connection *conn, const char *fmt, ...) @@ -169,7 +175,9 @@ static void director_connection_wait_timeout(struct director_connection *conn) { - director_connection_deinit(&conn); + director_connection_log_disconnect(conn); + director_connection_deinit(&conn, + "Timeout waiting for disconnect after CONNECT"); } static void director_connection_send_connect(struct director_connection *conn, @@ -185,6 +193,12 @@ director_connection_send(conn, connect_str); o_stream_uncork(conn->output); + /* wait for a while for the remote to disconnect, so it will hopefully + see our CONNECT command. we'll also log the warning later to avoid + multiple log lines about it. */ + conn->connect_request_to = host; + director_host_ref(conn->connect_request_to); + conn->to_disconnect = timeout_add(DIRECTOR_WAIT_DISCONNECT_SECS*1000, director_connection_wait_timeout, conn); @@ -219,10 +233,10 @@ } else if (dir->left == NULL) { /* no conflicts yet */ } else if (dir->left->host == conn->host) { - i_info("Dropping existing connection %s " - "in favor of its new connection %s", - dir->left->host->name, conn->host->name); - director_connection_deinit(&dir->left); + i_warning("Replacing left director connection %s with %s", + dir->left->host->name, conn->host->name); + director_connection_deinit(&dir->left, t_strdup_printf( + "Replacing with %s", conn->host->name)); } else if (dir->left->verifying_left) { /* we're waiting to verify if our current left is still working. if we don't receive a PONG, the current left @@ -234,9 +248,6 @@ dir->self_host) < 0) { /* the old connection is the correct one. refer the client there (FIXME: do we ever get here?) */ - i_warning("Director connection %s tried to connect to " - "us, should use %s instead", - conn->name, dir->left->host->name); director_connection_send_connect(conn, dir->left->host); return TRUE; } else { @@ -266,7 +277,8 @@ /* either use this or disconnect it */ if (!director_connection_assign_left(conn)) { /* we don't want this */ - director_connection_deinit(&conn); + director_connection_deinit(&conn, + "Unwanted incoming connection"); director_assign_left(dir); break; } @@ -303,9 +315,10 @@ conn->wrong_host = TRUE; return FALSE; } - i_info("Replacing right director connection %s with %s", - dir->right->host->name, conn->host->name); - director_connection_deinit(&dir->right); + i_warning("Replacing right director connection %s with %s", + dir->right->host->name, conn->host->name); + director_connection_deinit(&dir->right, t_strdup_printf( + "Replacing with %s", conn->host->name)); } dir->right = conn; i_free(conn->name); @@ -409,7 +422,8 @@ } else if (dir->left->host == conn->host) { /* b) */ i_assert(dir->left != conn); - director_connection_deinit(&dir->left); + director_connection_deinit(&dir->left, + "Replacing with new incoming connection"); } else if (director_host_cmp_to_self(conn->host, dir->left->host, dir->self_host) < 0) { /* c) */ @@ -712,7 +726,7 @@ int ret; if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0) - return FALSE; + return ret > 0; if (str_array_length(args) != 2 || str_to_uint(args[0], &username_hash) < 0 || @@ -919,14 +933,19 @@ static bool director_handshake_cmd_done(struct director_connection *conn) { struct director *dir = conn->dir; - - if (dir->debug) { - unsigned int secs = time(NULL)-conn->created; + unsigned int handshake_secs = time(NULL) - conn->created; + string_t *str; - i_debug("director(%s): Handshake took %u secs, " - "bytes in=%"PRIuUOFF_T" out=%"PRIuUOFF_T, - conn->name, secs, conn->input->v_offset, - conn->output->offset); + if (handshake_secs >= DIRECTOR_HANDSHAKE_WARN_SECS || dir->debug) { + str = t_str_new(128); + str_printfa(str, "director(%s): Handshake took %u secs, " + "bytes in=%"PRIuUOFF_T" out=%"PRIuUOFF_T, + conn->name, handshake_secs, conn->input->v_offset, + conn->output->offset); + if (handshake_secs >= DIRECTOR_HANDSHAKE_WARN_SECS) + i_warning("%s", str_c(str)); + else + i_debug("%s", str_c(str)); } /* the host is up now, make sure we can connect to it immediately @@ -1143,12 +1162,8 @@ if (conn->in && conn != dir->left && conn->me_received && conn->to_disconnect == NULL && director_host_cmp_to_self(dir->left->host, conn->host, - dir->self_host) < 0) { - i_warning("Director connection %s tried to connect to " - "us, should use %s instead", - conn->name, dir->left->host->name); + dir->self_host) < 0) director_connection_send_connect(conn, dir->left->host); - } } } @@ -1220,6 +1235,11 @@ return director_connection_sync(conn, args); if (strcmp(cmd, "CONNECT") == 0) return director_cmd_connect(conn, args); + if (strcmp(cmd, "QUIT") == 0) { + i_warning("Director %s disconnected us with reason: %s", + conn->name, t_strarray_join(args, " ")); + return FALSE; + } director_cmd_error(conn, "Unknown command %s", cmd); return FALSE; @@ -1247,6 +1267,37 @@ return ret; } +static void +director_connection_log_disconnect(struct director_connection *conn) +{ + unsigned int secs = ioloop_time - conn->created; + string_t *str = t_str_new(128); + + i_assert(conn->connected); + + if (conn->connect_request_to != NULL) { + i_warning("Director %s tried to connect to us, " + "should use %s instead", + conn->name, conn->connect_request_to->name); + return; + } + + str_printfa(str, "Director %s disconnected: ", conn->name); + str_append(str, "Connection closed"); + if (errno != 0 && errno != EPIPE) + str_printfa(str, ": %m"); + + str_printfa(str, " (connected %u secs, " + "in=%"PRIuUOFF_T" out=%"PRIuUOFF_T, + secs, conn->input->v_offset, conn->output->offset); + + if (!conn->me_received) + str_append(str, ", handshake ME not received"); + else if (!conn->handshake_received) + str_append(str, ", handshake DONE not received"); + i_error("%s", str_c(str)); +} + static void director_connection_input(struct director_connection *conn) { struct director *dir = conn->dir; @@ -1258,16 +1309,14 @@ return; case -1: /* disconnected */ - i_error("Director %s disconnected%s", conn->name, - conn->handshake_received ? "" : - " before handshake finished"); + director_connection_log_disconnect(conn); director_connection_disconnected(&conn); return; case -2: /* buffer full */ director_cmd_error(conn, "Director sent us more than %d bytes", MAX_INBUF_SIZE); - director_connection_reconnect(&conn); + director_connection_reconnect(&conn, "Too long input line"); return; } @@ -1286,7 +1335,8 @@ } T_END; if (!ret) { - director_connection_reconnect(&conn); + director_connection_reconnect(&conn, t_strdup_printf( + "Invalid input: %s", line)); break; } } @@ -1472,7 +1522,8 @@ return conn; } -void director_connection_deinit(struct director_connection **_conn) +void director_connection_deinit(struct director_connection **_conn, + const char *remote_reason) { struct director_connection *const *conns, *conn = *_conn; struct director *dir = conn->dir; @@ -1480,8 +1531,15 @@ *_conn = NULL; - if (dir->debug && conn->host != NULL) - i_debug("Disconnecting from %s", conn->host->name); + if (dir->debug && conn->host != NULL) { + i_debug("Disconnecting from %s: %s", + conn->host->name, remote_reason); + } + if (*remote_reason != '\0' && + conn->minor_version >= DIRECTOR_VERSION_QUIT) { + o_stream_send_str(conn->output, t_strdup_printf( + "QUIT\t%s\n", remote_reason)); + } conns = array_get(&dir->connections, &count); for (i = 0; i < count; i++) { @@ -1502,6 +1560,8 @@ if (conn->host != NULL) director_host_unref(conn->host); + if (conn->connect_request_to != NULL) + director_host_unref(conn->connect_request_to); if (conn->user_iter != NULL) user_directory_iter_deinit(&conn->user_iter); if (conn->to_disconnect != NULL) @@ -1540,17 +1600,18 @@ conn->host->last_network_failure = ioloop_time; } - director_connection_deinit(_conn); + director_connection_deinit(_conn, ""); if (dir->right == NULL) director_connect(dir); } -void director_connection_reconnect(struct director_connection **_conn) +static void director_connection_reconnect(struct director_connection **_conn, + const char *reason) { struct director_connection *conn = *_conn; struct director *dir = conn->dir; - director_connection_deinit(_conn); + director_connection_deinit(_conn, reason); if (dir->right == NULL) director_connect(dir); }
--- a/src/director/director-connection.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/director/director-connection.h Wed Sep 26 18:01:01 2012 +0300 @@ -10,7 +10,8 @@ struct director_connection * director_connection_init_out(struct director *dir, int fd, struct director_host *host); -void director_connection_deinit(struct director_connection **conn); +void director_connection_deinit(struct director_connection **conn, + const char *remote_reason); void director_connection_send(struct director_connection *conn, const char *data);
--- a/src/director/director-request.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/director/director-request.c Wed Sep 26 18:01:01 2012 +0300 @@ -136,6 +136,15 @@ its existing connections have been killed */ return FALSE; } + if (dir->right == NULL && dir->ring_synced) { + /* looks like all the other directors have died. we can do + whatever we want without breaking anything. remove the + user's weakness just in case it was set to TRUE when we + had more directors. */ + user->weak = FALSE; + return TRUE; + } + if (user->weak) { /* wait for user to become non-weak */ return FALSE;
--- a/src/director/director.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/director/director.c Wed Sep 26 18:01:01 2012 +0300 @@ -216,7 +216,10 @@ if (dir->left != NULL) { /* since we couldn't connect to it, it must have failed recently */ - director_connection_deinit(&dir->left); + i_warning("director: Assuming %s is dead, disconnecting", + director_connection_get_name(dir->left)); + director_connection_deinit(&dir->left, + "This connection is dead?"); } dir->ring_min_version = DIRECTOR_VERSION_MINOR; if (!dir->ring_handshaked) @@ -482,7 +485,7 @@ if (director_connection_get_host(conn) != removed_host) i++; else { - director_connection_deinit(&conn); + director_connection_deinit(&conn, "Removing from ring"); conns = array_get(&dir->connections, &count); } } @@ -845,7 +848,7 @@ while (array_count(&dir->connections) > 0) { connp = array_idx(&dir->connections, 0); conn = *connp; - director_connection_deinit(&conn); + director_connection_deinit(&conn, "Shutting down"); } user_directory_deinit(&dir->users);
--- a/src/director/director.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/director/director.h Wed Sep 26 18:01:01 2012 +0300 @@ -6,12 +6,14 @@ #define DIRECTOR_VERSION_NAME "director" #define DIRECTOR_VERSION_MAJOR 1 -#define DIRECTOR_VERSION_MINOR 2 +#define DIRECTOR_VERSION_MINOR 3 /* weak users supported in protocol v1.1+ */ #define DIRECTOR_VERSION_WEAK_USERS 1 /* director removes supported in v1.2+ */ #define DIRECTOR_VERSION_RING_REMOVE 2 +/* quit reason supported in v1.3+ */ +#define DIRECTOR_VERSION_QUIT 3 /* Minimum time between even attempting to communicate with a director that failed due to a protocol error. */
--- a/src/doveadm/Makefile.am Wed Sep 26 17:17:08 2012 +0300 +++ b/src/doveadm/Makefile.am Wed Sep 26 18:01:01 2012 +0300 @@ -22,6 +22,7 @@ -DDOVEADM_MODULEDIR=\""$(doveadm_moduledir)"\" \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DPKG_STATEDIR=\""$(statedir)"\" \ + -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ -DBINDIR=\""$(bindir)"\" \ -DMANDIR=\""$(mandir)"\"
--- a/src/doveadm/doveadm-settings.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/doveadm/doveadm-settings.c Wed Sep 26 18:01:01 2012 +0300 @@ -53,6 +53,7 @@ static const struct setting_define doveadm_setting_defines[] = { DEF(SET_STR, base_dir), + DEF(SET_STR, libexec_dir), DEF(SET_STR, mail_plugins), DEF(SET_STR, mail_plugin_dir), DEF(SET_STR, doveadm_socket_path), @@ -70,6 +71,7 @@ const struct doveadm_settings doveadm_default_settings = { .base_dir = PKG_RUNDIR, + .libexec_dir = PKG_LIBEXECDIR, .mail_plugins = "", .mail_plugin_dir = MODULEDIR, .doveadm_socket_path = "doveadm-server",
--- a/src/doveadm/doveadm-settings.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/doveadm/doveadm-settings.h Wed Sep 26 18:01:01 2012 +0300 @@ -3,6 +3,7 @@ struct doveadm_settings { const char *base_dir; + const char *libexec_dir; const char *mail_plugins; const char *mail_plugin_dir; const char *doveadm_socket_path;
--- a/src/doveadm/doveadm.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/doveadm/doveadm.c Wed Sep 26 18:01:01 2012 +0300 @@ -174,6 +174,21 @@ cmd_config, "config", "[doveconf parameters]" }; +static void cmd_exec(int argc ATTR_UNUSED, char *argv[]) +{ + const char *path, *binary = argv[1]; + + path = t_strdup_printf("%s/%s", doveadm_settings->libexec_dir, binary); + argv++; + argv[0] = t_strdup_noconst(path); + (void)execv(argv[0], argv); + i_fatal("execv(%s) failed: %m", argv[0]); +} + +static struct doveadm_cmd doveadm_cmd_exec = { + cmd_exec, "exec", "<binary> [binary parameters]" +}; + static bool doveadm_try_run_multi_word(const struct doveadm_cmd *cmd, const char *cmdname, int argc, char *argv[]) @@ -273,6 +288,7 @@ static struct doveadm_cmd *doveadm_commands[] = { &doveadm_cmd_help, &doveadm_cmd_config, + &doveadm_cmd_exec, &doveadm_cmd_stop, &doveadm_cmd_reload, &doveadm_cmd_dump,
--- a/src/lib-imap-client/imapc-client.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-imap-client/imapc-client.h Wed Sep 26 18:01:01 2012 +0300 @@ -1,6 +1,9 @@ #ifndef IMAPC_CLIENT_H #define IMAPC_CLIENT_H +/* IMAP RFC defines this to be at least 30 minutes. */ +#define IMAPC_DEFAULT_MAX_IDLE_TIME (60*29) + enum imapc_command_state { IMAPC_COMMAND_STATE_OK, IMAPC_COMMAND_STATE_NO,
--- a/src/lib-imap-client/imapc-connection.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-imap-client/imapc-connection.c Wed Sep 26 18:01:01 2012 +0300 @@ -496,7 +496,8 @@ ret = imap_parser_read_args(conn->parser, 0, IMAP_PARSE_FLAG_LITERAL_SIZE | - IMAP_PARSE_FLAG_ATOM_ALLCHARS, imap_args_r); + IMAP_PARSE_FLAG_ATOM_ALLCHARS | + IMAP_PARSE_FLAG_SERVER_TEXT, imap_args_r); if (ret == -2) { /* need more data */ return 0; @@ -816,6 +817,9 @@ if ((ret = imapc_connection_read_line(conn, &imap_args)) <= 0) return ret; + /* we already verified that the banner beigns with OK */ + i_assert(imap_arg_atom_equals(imap_args, "OK")); + imap_args++; if (imapc_connection_handle_imap_resp_text(conn, imap_args, &key, &value) < 0) @@ -839,6 +843,8 @@ static int imapc_connection_input_untagged(struct imapc_connection *conn) { const struct imap_arg *imap_args; + const unsigned char *data; + size_t size; const char *name, *value; struct imap_parser *parser; struct imapc_untagged_reply reply; @@ -846,13 +852,13 @@ if (conn->state == IMAPC_CONNECTION_STATE_CONNECTING) { /* input banner */ - name = imap_parser_read_word(conn->parser); - if (name == NULL) + data = i_stream_get_data(conn->input, &size); + if (size < 3 && memchr(data, '\n', size) == NULL) return 0; - - if (strcasecmp(name, "OK") != 0) { + if (i_memcasecmp(data, "OK ", 3) != 0) { imapc_connection_input_error(conn, - "Banner doesn't begin with OK: %s", name); + "Banner doesn't begin with OK: %s", + t_strcut(t_strndup(data, size), '\n')); return -1; } conn->input_callback = imapc_connection_input_banner;
--- a/src/lib-imap/imap-parser.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-imap/imap-parser.c Wed Sep 26 18:01:01 2012 +0300 @@ -18,7 +18,8 @@ ARG_PARSE_LITERAL, ARG_PARSE_LITERAL8, ARG_PARSE_LITERAL_DATA, - ARG_PARSE_LITERAL_DATA_FORCED + ARG_PARSE_LITERAL_DATA_FORCED, + ARG_PARSE_TEXT }; struct imap_parser { @@ -38,6 +39,7 @@ enum arg_parse_type cur_type; size_t cur_pos; /* parser position in input buffer */ + bool cur_resp_text; /* we're parsing [resp-text-code] */ int str_first_escape; /* ARG_PARSE_STRING: index to first '\' */ uoff_t literal_size; /* ARG_PARSE_LITERAL: string size */ @@ -62,7 +64,7 @@ parser = i_new(struct imap_parser, 1); parser->refcount = 1; parser->pool = pool_alloconly_create(MEMPOOL_GROWING"IMAP parser", - 1024*10); + 1024); parser->input = input; parser->output = output; parser->max_line_size = max_line_size; @@ -103,6 +105,7 @@ parser->cur_type = ARG_PARSE_NONE; parser->cur_pos = 0; + parser->cur_resp_text = FALSE; parser->str_first_escape = 0; parser->literal_size = 0; @@ -219,6 +222,7 @@ switch (parser->cur_type) { case ARG_PARSE_ATOM: + case ARG_PARSE_TEXT: if (size == 3 && memcmp(data, "NIL", 3) == 0) { /* NIL argument */ arg->type = IMAP_ARG_NIL; @@ -480,6 +484,56 @@ } } +static bool imap_parser_is_next_resp_text(struct imap_parser *parser) +{ + const struct imap_arg *arg; + + if (parser->cur_list != &parser->root_list || + array_count(parser->cur_list) != 1) + return FALSE; + + arg = array_idx(&parser->root_list, 0); + if (arg->type != IMAP_ARG_ATOM) + return FALSE; + + return strcasecmp(arg->_data.str, "OK") == 0 || + strcasecmp(arg->_data.str, "NO") == 0 || + strcasecmp(arg->_data.str, "BAD") == 0 || + strcasecmp(arg->_data.str, "BYE") == 0; +} + +static bool imap_parser_is_next_text(struct imap_parser *parser) +{ + const struct imap_arg *arg; + unsigned int len; + + if (parser->cur_list != &parser->root_list) + return FALSE; + + arg = array_idx(&parser->root_list, array_count(&parser->root_list)-1); + if (arg->type != IMAP_ARG_ATOM) + return FALSE; + + len = strlen(arg->_data.str); + return len > 0 && arg->_data.str[len-1] == ']'; +} + +static bool imap_parser_read_text(struct imap_parser *parser, + const unsigned char *data, size_t data_size) +{ + size_t i; + + /* read until end of line */ + for (i = parser->cur_pos; i < data_size; i++) { + if (is_linebreak(data[i])) { + imap_parser_save_arg(parser, data, i); + break; + } + } + parser->cur_pos = i; + return parser->cur_type == ARG_PARSE_NONE; +} + /* Returns TRUE if argument was fully processed. Also returns TRUE if an argument inside a list was processed. */ static int imap_parser_read_arg(struct imap_parser *parser) @@ -497,6 +551,13 @@ return FALSE; i_assert(parser->cur_pos == 0); + if (parser->cur_resp_text && + imap_parser_is_next_text(parser)) { + /* we just parsed [resp-text-code] */ + parser->cur_type = ARG_PARSE_TEXT; + break; + } + switch (data[0]) { case '\r': if (data_size == 1) { @@ -565,6 +626,16 @@ case ARG_PARSE_ATOM: if (!imap_parser_read_atom(parser, data, data_size)) return FALSE; + if ((parser->flags & IMAP_PARSE_FLAG_SERVER_TEXT) == 0) + break; + + if (imap_parser_is_next_resp_text(parser)) { + /* we just parsed OK/NO/BAD/BYE. after parsing the + [resp-text-code] the rest of the message can contain + pretty much any random text, which we can't parse + as if it was valid IMAP input */ + parser->cur_resp_text = TRUE; + } break; case ARG_PARSE_STRING: if (!imap_parser_read_string(parser, data, data_size)) @@ -594,6 +665,10 @@ if (!imap_parser_read_literal_data(parser, data, data_size)) return FALSE; break; + case ARG_PARSE_TEXT: + if (!imap_parser_read_text(parser, data, data_size)) + return FALSE; + break; default: i_unreached(); } @@ -616,6 +691,7 @@ parser->line_size += parser->cur_pos; i_stream_skip(parser->input, parser->cur_pos); parser->cur_pos = 0; + parser->cur_resp_text = FALSE; if (parser->list_arg != NULL && !parser->literal_size_return) { parser->error = "Missing ')'";
--- a/src/lib-imap/imap-parser.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-imap/imap-parser.h Wed Sep 26 18:01:01 2012 +0300 @@ -20,7 +20,11 @@ /* Parse in list context; ')' parses as EOL */ IMAP_PARSE_FLAG_INSIDE_LIST = 0x20, /* Parse literal8 and set it as flag to imap_arg. */ - IMAP_PARSE_FLAG_LITERAL8 = 0x40 + IMAP_PARSE_FLAG_LITERAL8 = 0x40, + /* We're parsing IMAP server replies. Parse the "text" after + OK/NO/BAD/BYE replies as a single atom. We assume that the initial + "*" or tag was already skipped over. */ + IMAP_PARSE_FLAG_SERVER_TEXT = 0x80 }; struct imap_parser;
--- a/src/lib-master/master-service-settings-cache.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-master/master-service-settings-cache.c Wed Sep 26 18:01:01 2012 +0300 @@ -59,7 +59,8 @@ struct master_service_settings_cache *cache; pool_t pool; - pool = pool_alloconly_create("master service settings cache", 1024*32); + pool = pool_alloconly_create(MEMPOOL_GROWING"master service settings cache", + 1024*12); cache = p_new(pool, struct master_service_settings_cache, 1); cache->pool = pool; cache->service = service;
--- a/src/lib-settings/settings-parser.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-settings/settings-parser.c Wed Sep 26 18:01:01 2012 +0300 @@ -204,7 +204,7 @@ i_assert(count > 0); parser_pool = pool_alloconly_create(MEMPOOL_GROWING"settings parser", - 8192); + 1024); ctx = p_new(parser_pool, struct setting_parser_context, 1); ctx->set_pool = set_pool; ctx->parser_pool = parser_pool; @@ -1745,7 +1745,7 @@ pool_ref(new_pool); parser_pool = pool_alloconly_create(MEMPOOL_GROWING"dup settings parser", - 8192); + 1024); new_ctx = p_new(parser_pool, struct setting_parser_context, 1); new_ctx->set_pool = new_pool; new_ctx->parser_pool = parser_pool;
--- a/src/lib-storage/index/dbox-common/dbox-storage.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/index/dbox-common/dbox-storage.c Wed Sep 26 18:01:01 2012 +0300 @@ -111,7 +111,8 @@ } } - dbox_verify_alt_path(ns->list); + if (!ns->list->set.alt_dir_nocheck) + dbox_verify_alt_path(ns->list); return 0; }
--- a/src/lib-storage/index/dbox-multi/mdbox-file.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/index/dbox-multi/mdbox-file.c Wed Sep 26 18:01:01 2012 +0300 @@ -302,11 +302,14 @@ { struct mdbox_file *mfile = (struct mdbox_file *)file; struct mdbox_map *map = mfile->storage->map; + struct mailbox_permissions perm; mode_t old_mask; const char *p, *dir; int fd; - old_mask = umask(0666 & ~map->perm.file_create_mode); + mailbox_list_get_root_permissions(map->root_list, &perm); + + old_mask = umask(0666 & ~perm.file_create_mode); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); umask(old_mask); if (fd == -1 && errno == ENOENT && parents && @@ -321,25 +324,25 @@ return -1; } /* try again */ - old_mask = umask(0666 & ~map->perm.file_create_mode); + old_mask = umask(0666 & ~perm.file_create_mode); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); umask(old_mask); } if (fd == -1) { mail_storage_set_critical(&file->storage->storage, "open(%s, O_CREAT) failed: %m", path); - } else if (map->perm.file_create_gid == (gid_t)-1) { + } else if (perm.file_create_gid == (gid_t)-1) { /* no group change */ - } else if (fchown(fd, (uid_t)-1, map->perm.file_create_gid) < 0) { + } else if (fchown(fd, (uid_t)-1, perm.file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(&file->storage->storage, "%s", eperm_error_get_chgrp("fchown", path, - map->perm.file_create_gid, - map->perm.file_create_gid_origin)); + perm.file_create_gid, + perm.file_create_gid_origin)); } else { mail_storage_set_critical(&file->storage->storage, "fchown(%s, -1, %ld) failed: %m", - path, (long)map->perm.file_create_gid); + path, (long)perm.file_create_gid); } /* continue anyway */ }
--- a/src/lib-storage/index/dbox-multi/mdbox-map-private.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/index/dbox-multi/mdbox-map-private.h Wed Sep 26 18:01:01 2012 +0300 @@ -20,7 +20,6 @@ uint32_t map_ext_id, ref_ext_id; struct mailbox_list *root_list; - struct mailbox_permissions perm; unsigned int verify_existing_file_ids:1; };
--- a/src/lib-storage/index/dbox-multi/mdbox-map.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/index/dbox-multi/mdbox-map.c Wed Sep 26 18:01:01 2012 +0300 @@ -74,11 +74,6 @@ sizeof(uint32_t)); map->ref_ext_id = mail_index_ext_register(map->index, "ref", 0, sizeof(uint16_t), sizeof(uint16_t)); - - mailbox_list_get_root_permissions(root_list, &map->perm); - mail_index_set_permissions(map->index, map->perm.file_create_mode, - map->perm.file_create_gid, - map->perm.file_create_gid_origin); return map; } @@ -140,6 +135,7 @@ static int mdbox_map_open_internal(struct mdbox_map *map, bool create_missing) { enum mail_index_open_flags open_flags; + struct mailbox_permissions perm; int ret = 0; if (map->view != NULL) { @@ -147,6 +143,11 @@ return 1; } + mailbox_list_get_root_permissions(map->root_list, &perm); + mail_index_set_permissions(map->index, perm.file_create_mode, + perm.file_create_gid, + perm.file_create_gid_origin); + open_flags = MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY | mail_storage_settings_to_index_flags(MAP_STORAGE(map)->set); if (create_missing) {
--- a/src/lib-storage/index/dbox-multi/mdbox-storage.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/index/dbox-multi/mdbox-storage.c Wed Sep 26 18:01:01 2012 +0300 @@ -87,7 +87,8 @@ bool debug = ns->mail_set->mail_debug; const char *home, *path; - if (mail_user_get_home(ns->owner, &home) > 0) { + if (ns->owner != NULL && + mail_user_get_home(ns->owner, &home) > 0) { path = t_strconcat(home, "/mdbox", NULL); if (access(path, R_OK|W_OK|X_OK) == 0) { if (debug)
--- a/src/lib-storage/index/dbox-single/sdbox-storage.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/index/dbox-single/sdbox-storage.c Wed Sep 26 18:01:01 2012 +0300 @@ -34,7 +34,8 @@ bool debug = ns->mail_set->mail_debug; const char *home, *path; - if (mail_user_get_home(ns->owner, &home) > 0) { + if (ns->owner != NULL && + mail_user_get_home(ns->owner, &home) > 0) { path = t_strconcat(home, "/sdbox", NULL); if (access(path, R_OK|W_OK|X_OK) == 0) { if (debug)
--- a/src/lib-storage/index/imapc/imapc-settings.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/index/imapc/imapc-settings.c Wed Sep 26 18:01:01 2012 +0300 @@ -131,6 +131,10 @@ return FALSE; } #endif + if (set->imapc_max_idle_time == 0) { + *error_r = "imapc_max_idle_time must not be 0"; + return FALSE; + } if (imapc_settings_parse_features(set, error_r) < 0) return FALSE; return TRUE;
--- a/src/lib-storage/index/index-status.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/index/index-status.c Wed Sep 26 18:01:01 2012 +0300 @@ -293,6 +293,7 @@ } if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; + mail_search_args_unref(&search_args); if (ret == 0) { /* success, cache all */
--- a/src/lib-storage/index/maildir/maildir-storage.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/index/maildir/maildir-storage.c Wed Sep 26 18:01:01 2012 +0300 @@ -96,7 +96,8 @@ /* we'll need to figure out the maildir location ourself. It's ~/Maildir unless we are chrooted. */ - if (mail_user_get_home(ns->owner, &home) > 0) { + if (ns->owner != NULL && + mail_user_get_home(ns->owner, &home) > 0) { path = t_strconcat(home, "/Maildir", NULL); if (access(path, R_OK|W_OK|X_OK) == 0) { if (debug)
--- a/src/lib-storage/index/mbox/mbox-storage.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/index/mbox/mbox-storage.c Wed Sep 26 18:01:01 2012 +0300 @@ -244,7 +244,8 @@ bool debug = ns->mail_set->mail_debug; const char *home, *path; - if (mail_user_get_home(ns->owner, &home) <= 0) { + if (ns->owner == NULL || + mail_user_get_home(ns->owner, &home) <= 0) { if (debug) i_debug("maildir: Home directory not set"); home = "";
--- a/src/lib-storage/index/pop3c/pop3c-mail.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/index/pop3c/pop3c-mail.c Wed Sep 26 18:01:01 2012 +0300 @@ -30,7 +30,7 @@ struct message_size hdr_size, body_size; struct istream *input; - if (mail->data.virtual_size != 0) { + if (mail->data.virtual_size != (uoff_t)-1) { /* virtual size is already known. it's the same as our (correct) physical size */ *size_r = mail->data.virtual_size;
--- a/src/lib-storage/list/mailbox-list-fs-iter.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/list/mailbox-list-fs-iter.c Wed Sep 26 18:01:01 2012 +0300 @@ -183,6 +183,12 @@ /* mailbox doesn't match any patterns, we don't care about it */ return 0; } + if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) { + ret = mailbox_list_dirent_is_alias_symlink(ctx->ctx.list, + dir_path, d); + if (ret != 0) + return ret < 0 ? -1 : 0; + } ret = ctx->ctx.list->v. get_mailbox_flags(ctx->ctx.list, dir_path, d->d_name, mailbox_list_get_file_type(d), &info_flags); @@ -756,6 +762,13 @@ if (ret <= 0) return NULL; + if (_ctx->list->ns->type == MAIL_NAMESPACE_TYPE_SHARED && + !_ctx->list->ns->list->mail_set->mail_shared_explicit_inbox && + strlen(ctx->info.vname) < _ctx->list->ns->prefix_len) { + /* shared/user INBOX, IMAP code already lists it */ + return fs_list_iter_next(_ctx); + } + if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0) { mailbox_list_set_subscription_flags(ctx->ctx.list, ctx->info.vname,
--- a/src/lib-storage/list/mailbox-list-maildir-iter.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/list/mailbox-list-maildir-iter.c Wed Sep 26 18:01:01 2012 +0300 @@ -323,6 +323,11 @@ if (maildir_delete_trash_dir(ctx, fname)) return 0; + if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) { + ret = mailbox_list_dirent_is_alias_symlink(list, ctx->dir, d); + if (ret != 0) + return ret < 0 ? -1 : 0; + } T_BEGIN { ret = list->v.get_mailbox_flags(list, ctx->dir, fname, mailbox_list_get_file_type(d), &flags);
--- a/src/lib-storage/mail-storage-service.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/mail-storage-service.c Wed Sep 26 18:01:01 2012 +0300 @@ -962,7 +962,7 @@ pool_t user_pool, temp_pool; int ret = 1; - user_pool = pool_alloconly_create("mail storage service user", 1024*8); + user_pool = pool_alloconly_create("mail storage service user", 1024*6); if (mail_storage_service_read_settings(ctx, input, user_pool, &user_info, &set_parser,
--- a/src/lib-storage/mail-storage.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/mail-storage.h Wed Sep 26 18:01:01 2012 +0300 @@ -75,7 +75,8 @@ STATUS_HIGHESTMODSEQ = 0x80, STATUS_PERMANENT_FLAGS = 0x200, STATUS_FIRST_RECENT_UID = 0x400, - STATUS_LAST_CACHED_SEQ = 0x800 + STATUS_LAST_CACHED_SEQ = 0x800, + STATUS_CHECK_OVER_QUOTA = 0x1000 /* return error if over quota */ }; enum mailbox_metadata_items {
--- a/src/lib-storage/mailbox-guid-cache.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/mailbox-guid-cache.c Wed Sep 26 18:01:01 2012 +0300 @@ -56,6 +56,7 @@ list->guid_cache_errors = FALSE; ctx = mailbox_list_iter_init(list, "*", + MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_NO_AUTO_BOXES); while ((info = mailbox_list_iter_next(ctx)) != NULL) { if ((info->flags &
--- a/src/lib-storage/mailbox-list-iter.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/mailbox-list-iter.h Wed Sep 26 18:01:01 2012 +0300 @@ -11,7 +11,9 @@ physically exist */ MAILBOX_LIST_ITER_NO_AUTO_BOXES = 0x000004, - /* For mailbox_list_iter_init_namespaces(): Skip namespaces that + /* Skip all kinds of mailbox aliases. This typically includes symlinks + that point to the same directory. Also when iterating with + mailbox_list_iter_init_namespaces() skip namespaces that have alias_for set. */ MAILBOX_LIST_ITER_SKIP_ALIASES = 0x000008, /* For mailbox_list_iter_init_namespaces(): '*' in a pattern doesn't
--- a/src/lib-storage/mailbox-list-private.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/mailbox-list-private.h Wed Sep 26 18:01:01 2012 +0300 @@ -184,6 +184,9 @@ bool mailbox_list_name_is_too_large(const char *name, char sep); enum mailbox_list_file_type mailbox_list_get_file_type(const struct dirent *d); +int mailbox_list_dirent_is_alias_symlink(struct mailbox_list *list, + const char *dir_path, + const struct dirent *d); bool mailbox_list_try_get_absolute_path(struct mailbox_list *list, const char **name);
--- a/src/lib-storage/mailbox-list.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/mailbox-list.c Wed Sep 26 18:01:01 2012 +0300 @@ -2,6 +2,7 @@ #include "lib.h" #include "array.h" +#include "abspath.h" #include "ioloop.h" #include "mkdir-parents.h" #include "str.h" @@ -165,6 +166,7 @@ list->set.mailbox_dir_name = p_strdup(list->pool, set->mailbox_dir_name); list->set.alt_dir = p_strdup(list->pool, set->alt_dir); + list->set.alt_dir_nocheck = set->alt_dir_nocheck; if (*set->mailbox_dir_name == '\0') list->set.mailbox_dir_name = ""; @@ -299,7 +301,10 @@ dest = &set_r->control_dir; else if (strcmp(key, "ALT") == 0) dest = &set_r->alt_dir; - else if (strcmp(key, "LAYOUT") == 0) + else if (strcmp(key, "ALTNOCHECK") == 0) { + set_r->alt_dir_nocheck = TRUE; + continue; + } else if (strcmp(key, "LAYOUT") == 0) dest = &set_r->layout; else if (strcmp(key, "SUBSCRIPTIONS") == 0) dest = &set_r->subscription_fname; @@ -1384,6 +1389,39 @@ return type; } +int mailbox_list_dirent_is_alias_symlink(struct mailbox_list *list, + const char *dir_path, + const struct dirent *d) +{ + struct stat st; + int ret; + + if (mailbox_list_get_file_type(d) == MAILBOX_LIST_FILE_TYPE_SYMLINK) + return 1; + + T_BEGIN { + const char *path, *linkpath; + + path = t_strconcat(dir_path, "/", d->d_name, NULL); + if (lstat(path, &st) < 0) { + mailbox_list_set_critical(list, + "lstat(%s) failed: %m", path); + ret = -1; + } else if (!S_ISLNK(st.st_mode)) { + ret = 0; + } else if (t_readlink(path, &linkpath) < 0) { + i_error("readlink(%s) failed: %m", path); + ret = -1; + } else { + /* it's an alias only if it points to the same + directory */ + ret = strchr(linkpath, '/') == NULL ? 1 : 0; + } + } T_END; + return ret; +} + + static bool mailbox_list_try_get_home_path(struct mailbox_list *list, const char **name) {
--- a/src/lib-storage/mailbox-list.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib-storage/mailbox-list.h Wed Sep 26 18:01:01 2012 +0300 @@ -105,6 +105,8 @@ char escape_char; /* Use UTF-8 mailbox names on filesystem instead of mUTF-7 */ bool utf8; + /* Don't check/create the alt-dir symlink. */ + bool alt_dir_nocheck; }; struct mailbox_permissions {
--- a/src/lib/hash.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lib/hash.c Wed Sep 26 18:01:01 2012 +0300 @@ -8,7 +8,7 @@ #include <ctype.h> -#define HASH_TABLE_MIN_SIZE 131 +#define HASH_TABLE_MIN_SIZE 67 #undef hash_table_create #undef hash_table_create_direct
--- a/src/lmtp/client.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lmtp/client.c Wed Sep 26 18:01:01 2012 +0300 @@ -298,6 +298,9 @@ { struct mail_recipient *rcpt; + if (client->proxy != NULL) + lmtp_proxy_deinit(&client->proxy); + if (array_is_created(&client->state.rcpt_to)) { array_foreach_modifiable(&client->state.rcpt_to, rcpt) mail_storage_service_user_free(&rcpt->service_user);
--- a/src/lmtp/commands.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lmtp/commands.c Wed Sep 26 18:01:01 2012 +0300 @@ -463,6 +463,42 @@ *address = str_c(username); } +static int +lmtp_rcpt_to_is_over_quota(struct client *client, + const struct mail_recipient *rcpt) +{ + struct mail_user *user; + struct mail_namespace *ns; + struct mailbox *box; + struct mailbox_status status; + const char *errstr; + enum mail_error error; + int ret; + + if (!client->lmtp_set->lmtp_rcpt_check_quota) + return 0; + + ret = mail_storage_service_next(storage_service, + rcpt->service_user, &user); + if (ret < 0) + return -1; + + ns = mail_namespace_find_inbox(user->namespaces); + box = mailbox_alloc(ns->list, "INBOX", 0); + ret = mailbox_get_status(box, STATUS_CHECK_OVER_QUOTA, &status); + if (ret < 0) { + errstr = mailbox_get_last_error(box, &error); + if (error == MAIL_ERROR_NOSPACE) { + client_send_line(client, "552 5.2.2 <%s> %s", + rcpt->address, errstr); + ret = 1; + } + } + mailbox_free(&box); + mail_user_unref(&user); + return ret; +} + int cmd_rcpt(struct client *client, const char *args) { struct mail_recipient rcpt; @@ -498,16 +534,6 @@ return 0; } - if (client->proxy != NULL) { - /* NOTE: if this restriction is ever removed, we'll also need - to send different message bodies to local and proxy - (with and without Return-Path: header) */ - client_send_line(client, "451 4.3.0 <%s> " - "Can't handle mixed proxy/non-proxy destinations", - address); - return 0; - } - memset(&input, 0, sizeof(input)); input.module = input.service = "lmtp"; input.username = username; @@ -531,14 +557,29 @@ address, username); return 0; } + if (client->proxy != NULL) { + /* NOTE: if this restriction is ever removed, we'll also need + to send different message bodies to local and proxy + (with and without Return-Path: header) */ + client_send_line(client, "451 4.3.0 <%s> " + "Can't handle mixed proxy/non-proxy destinations", + address); + return 0; + } lmtp_address_translate(client, &address); rcpt.address = p_strdup(client->state_pool, address); rcpt.detail = p_strdup(client->state_pool, detail); - array_append(&client->state.rcpt_to, &rcpt, 1); - - client_send_line(client, "250 2.1.5 OK"); + if ((ret = lmtp_rcpt_to_is_over_quota(client, &rcpt)) < 0) { + client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, + rcpt.address); + return 0; + } + if (ret == 0) { + array_append(&client->state.rcpt_to, &rcpt, 1); + client_send_line(client, "250 2.1.5 OK"); + } return 0; }
--- a/src/lmtp/lmtp-settings.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lmtp/lmtp-settings.c Wed Sep 26 18:01:01 2012 +0300 @@ -59,6 +59,7 @@ static const struct setting_define lmtp_setting_defines[] = { DEF(SET_BOOL, lmtp_proxy), DEF(SET_BOOL, lmtp_save_to_detail_mailbox), + DEF(SET_BOOL, lmtp_rcpt_check_quota), DEF(SET_STR, lmtp_address_translate), DEF(SET_STR_VARS, login_greeting), DEF(SET_STR, login_trusted_networks), @@ -69,6 +70,7 @@ static const struct lmtp_settings lmtp_default_settings = { .lmtp_proxy = FALSE, .lmtp_save_to_detail_mailbox = FALSE, + .lmtp_rcpt_check_quota = FALSE, .lmtp_address_translate = "", .login_greeting = PACKAGE_NAME" ready.", .login_trusted_networks = ""
--- a/src/lmtp/lmtp-settings.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/lmtp/lmtp-settings.h Wed Sep 26 18:01:01 2012 +0300 @@ -7,6 +7,7 @@ struct lmtp_settings { bool lmtp_proxy; bool lmtp_save_to_detail_mailbox; + bool lmtp_rcpt_check_quota; const char *lmtp_address_translate; const char *login_greeting; const char *login_trusted_networks;
--- a/src/plugins/Makefile.am Wed Sep 26 17:17:08 2012 +0300 +++ b/src/plugins/Makefile.am Wed Sep 26 18:01:01 2012 +0300 @@ -21,6 +21,7 @@ listescape \ notify \ mail-log \ + mailbox-alias \ quota \ imap-quota \ pop3-migration \
--- a/src/plugins/acl/acl-lookup-dict.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/plugins/acl/acl-lookup-dict.c Wed Sep 26 18:01:01 2012 +0300 @@ -91,6 +91,13 @@ } } +static bool +acl_rights_is_same_user(const struct acl_rights *right, struct mail_user *user) +{ + return right->id_type == ACL_ID_USER && + strcmp(right->identifier, user->username) == 0; +} + static int acl_lookup_dict_rebuild_add_backend(struct mail_namespace *ns, ARRAY_TYPE(const_string) *ids) { @@ -114,7 +121,10 @@ iter = acl_object_list_init(aclobj); while ((ret = acl_object_list_next(iter, &rights)) > 0) { - if (acl_rights_has_nonowner_lookup_changes(&rights)) { + /* avoid pointless user -> user entries, + which some clients do */ + if (acl_rights_has_nonowner_lookup_changes(&rights) && + !acl_rights_is_same_user(&rights, ns->owner)) { str_truncate(id, 0); acl_lookup_dict_write_rights_id(id, &rights); str_append_c(id, '/');
--- a/src/plugins/fts-lucene/lucene-wrapper.cc Wed Sep 26 17:17:08 2012 +0300 +++ b/src/plugins/fts-lucene/lucene-wrapper.cc Wed Sep 26 18:01:01 2012 +0300 @@ -110,7 +110,7 @@ index->path = i_strdup(path); index->list = list; index->normalizer = !set->normalize ? NULL : - list->ns->user->default_normalizer; + mailbox_list_get_namespace(list)->user->default_normalizer; if (set != NULL) index->set = *set; else {
--- a/src/plugins/fts-solr/fts-backend-solr.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/plugins/fts-solr/fts-backend-solr.c Wed Sep 26 18:01:01 2012 +0300 @@ -570,6 +570,7 @@ /* FIXME: proper rescan needed. for now we'll just reset the last-uids */ iter = mailbox_list_iter_init(backend->ns->list, "*", + MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_NO_AUTO_BOXES); while ((info = mailbox_list_iter_next(iter)) != NULL) { if ((info->flags &
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/mailbox-alias/Makefile.am Wed Sep 26 18:01:01 2012 +0300 @@ -0,0 +1,18 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-mail \ + -I$(top_srcdir)/src/lib-imap \ + -I$(top_srcdir)/src/lib-index \ + -I$(top_srcdir)/src/lib-storage + +NOPLUGIN_LDFLAGS = +lib20_mailbox_alias_plugin_la_LDFLAGS = -module -avoid-version + +module_LTLIBRARIES = \ + lib20_mailbox_alias_plugin.la + +lib20_mailbox_alias_plugin_la_SOURCES = \ + mailbox-alias-plugin.c + +noinst_HEADERS = \ + mailbox-alias-plugin.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/mailbox-alias/mailbox-alias-plugin.c Wed Sep 26 18:01:01 2012 +0300 @@ -0,0 +1,334 @@ +/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "mail-storage-hooks.h" +#include "mail-storage-private.h" +#include "mailbox-list-private.h" +#include "mailbox-alias-plugin.h" + +#define MAILBOX_ALIAS_USER_CONTEXT(obj) \ + MODULE_CONTEXT(obj, mailbox_alias_user_module) +#define MAILBOX_ALIAS_CONTEXT(obj) \ + MODULE_CONTEXT(obj, mailbox_alias_storage_module) +#define MAILBOX_ALIAS_LIST_CONTEXT(obj) \ + MODULE_CONTEXT(obj, mailbox_alias_mailbox_list_module) + +struct mailbox_alias { + const char *old_vname, *new_vname; +}; + +struct mailbox_alias_user { + union mail_user_module_context module_ctx; + + ARRAY(struct mailbox_alias) aliases; +}; + +struct mailbox_alias_mailbox_list { + union mailbox_list_module_context module_ctx; +}; + +struct mailbox_alias_mailbox { + union mailbox_module_context module_ctx; +}; + +enum mailbox_symlink_existence { + MAILBOX_SYMLINK_EXISTENCE_NONEXISTENT, + MAILBOX_SYMLINK_EXISTENCE_SYMLINK, + MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK +}; + +static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_user_module, + &mail_user_module_register); +static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_storage_module, + &mail_storage_module_register); +static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_mailbox_list_module, + &mailbox_list_module_register); + +const char *mailbox_alias_plugin_version = DOVECOT_VERSION; + +static const char * +mailbox_alias_find_new(struct mail_user *user, const char *new_vname) +{ + struct mailbox_alias_user *auser = MAILBOX_ALIAS_USER_CONTEXT(user); + const struct mailbox_alias *alias; + + array_foreach(&auser->aliases, alias) { + if (strcmp(alias->new_vname, new_vname) == 0) + return alias->old_vname; + } + return NULL; +} + +static int mailbox_symlink_exists(struct mailbox_list *list, const char *vname, + enum mailbox_symlink_existence *existence_r) +{ + struct mailbox_alias_mailbox_list *alist = + MAILBOX_ALIAS_LIST_CONTEXT(list); + struct stat st; + const char *symlink_name, *symlink_path; + + symlink_name = alist->module_ctx.super.get_storage_name(list, vname); + symlink_path = mailbox_list_get_path(list, symlink_name, + MAILBOX_LIST_PATH_TYPE_DIR); + if (lstat(symlink_path, &st) < 0) { + if (errno == ENOENT) { + *existence_r = MAILBOX_SYMLINK_EXISTENCE_NONEXISTENT; + return 0; + } + mailbox_list_set_critical(list, + "lstat(%s) failed: %m", symlink_path); + return -1; + } + if (S_ISLNK(st.st_mode)) + *existence_r = MAILBOX_SYMLINK_EXISTENCE_SYMLINK; + else + *existence_r = MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK; + return 0; +} + +static int mailbox_is_alias_symlink(struct mailbox *box) +{ + enum mailbox_symlink_existence existence; + + if (mailbox_alias_find_new(box->storage->user, box->vname) == NULL) + return 0; + if (mailbox_symlink_exists(box->list, box->vname, &existence) < 0) { + mail_storage_copy_list_error(box->storage, box->list); + return -1; + } + return existence == MAILBOX_SYMLINK_EXISTENCE_SYMLINK ? 1 : 0; +} + +static int +mailbox_has_aliases(struct mailbox_list *list, const char *old_vname) +{ + struct mailbox_alias_user *auser = + MAILBOX_ALIAS_USER_CONTEXT(list->ns->user); + const struct mailbox_alias *alias; + enum mailbox_symlink_existence existence; + int ret = 0; + + array_foreach(&auser->aliases, alias) { + if (strcmp(alias->old_vname, old_vname) == 0) { + if (mailbox_symlink_exists(list, alias->new_vname, + &existence) < 0) + ret = -1; + if (existence == MAILBOX_SYMLINK_EXISTENCE_SYMLINK) + return 1; + } + } + return ret; +} + +static int +mailbox_alias_create_symlink(struct mailbox *box, + const char *old_name, const char *new_name) +{ + const char *old_path, *new_path, *fname; + + old_path = mailbox_list_get_path(box->list, old_name, + MAILBOX_LIST_PATH_TYPE_DIR); + new_path = mailbox_list_get_path(box->list, new_name, + MAILBOX_LIST_PATH_TYPE_DIR); + fname = strrchr(old_path, '/'); + i_assert(fname != NULL); + fname++; + i_assert(strncmp(new_path, old_path, fname-old_path) == 0); + + if (symlink(fname, new_path) < 0) { + if (errno == EEXIST) { + mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, + "Mailbox already exists"); + return -1; + } + mail_storage_set_critical(box->storage, + "symlink(%s, %s) failed: %m", fname, new_path); + return -1; + } + return 0; +} + +static const char * +mailbox_alias_get_storage_name(struct mailbox_list *list, const char *vname) +{ + struct mailbox_alias_mailbox_list *alist = + MAILBOX_ALIAS_LIST_CONTEXT(list); + const char *old_vname; + enum mailbox_symlink_existence existence; + + /* access the old mailbox so that e.g. full text search won't + index the mailbox twice. this also means that deletion must be + careful to delete the symlink, box->name. */ + old_vname = mailbox_alias_find_new(list->ns->user, vname); + if (old_vname != NULL && + mailbox_symlink_exists(list, vname, &existence) == 0 && + existence != MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK) + vname = old_vname; + + return alist->module_ctx.super.get_storage_name(list, vname); +} + +static int +mailbox_alias_create(struct mailbox *box, const struct mailbox_update *update, + bool directory) +{ + struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(box); + struct mailbox_alias_mailbox_list *alist = + MAILBOX_ALIAS_LIST_CONTEXT(box->list); + const char *symlink_name; + int ret; + + ret = abox->module_ctx.super.create_box(box, update, directory); + if (mailbox_alias_find_new(box->storage->user, box->vname) == NULL) + return ret; + if (ret < 0 && mailbox_get_last_mail_error(box) != MAIL_ERROR_EXISTS) + return ret; + + /* all the code so far has actually only created the original + mailbox. now we'll create the symlink if it's missing. */ + symlink_name = alist->module_ctx.super. + get_storage_name(box->list, box->vname); + return mailbox_alias_create_symlink(box, box->name, symlink_name); +} + +static int mailbox_alias_delete(struct mailbox *box) +{ + struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(box); + struct mailbox_alias_mailbox_list *alist = + MAILBOX_ALIAS_LIST_CONTEXT(box->list); + const char *symlink_name; + int ret; + + ret = mailbox_has_aliases(box->list, box->vname); + if (ret < 0) + return -1; + if (ret > 0) { + mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, + "Can't delete mailbox while it has aliases"); + return -1; + } + + if (mailbox_is_alias_symlink(box)) { + /* we're deleting an alias mailbox. we'll need to handle this + explicitly since box->name points to the original mailbox */ + symlink_name = alist->module_ctx.super. + get_storage_name(box->list, box->vname); + if (mailbox_list_delete_symlink(box->list, symlink_name) < 0) { + mail_storage_copy_list_error(box->storage, box->list); + return -1; + } + return 0; + } + + return abox->module_ctx.super.delete_box(box); +} + +static int mailbox_alias_rename(struct mailbox *src, struct mailbox *dest) +{ + struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(src); + int ret; + + if (mailbox_is_alias_symlink(src)) { + mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, + "Can't rename alias mailboxes"); + return -1; + } + if (mailbox_is_alias_symlink(dest)) { + mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, + "Can't rename to mailbox alias"); + return -1; + } + ret = mailbox_has_aliases(src->list, src->vname); + if (ret < 0) + return -1; + if (ret > 0) { + mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, + "Can't rename mailbox while it has aliases"); + return -1; + } + + return abox->module_ctx.super.rename_box(src, dest); +} + +static void mailbox_alias_mail_user_created(struct mail_user *user) +{ + struct mail_user_vfuncs *v = user->vlast; + struct mailbox_alias_user *auser; + struct mailbox_alias *alias; + string_t *oldkey, *newkey; + const char *old_vname, *new_vname; + unsigned int i; + + auser = p_new(user->pool, struct mailbox_alias_user, 1); + auser->module_ctx.super = *v; + user->vlast = &auser->module_ctx.super; + + p_array_init(&auser->aliases, user->pool, 8); + + oldkey = t_str_new(32); + newkey = t_str_new(32); + str_append(oldkey, "mailbox_alias_old"); + str_append(newkey, "mailbox_alias_new"); + for (i = 2;; i++) { + old_vname = mail_user_plugin_getenv(user, str_c(oldkey)); + new_vname = mail_user_plugin_getenv(user, str_c(newkey)); + if (old_vname == NULL || new_vname == NULL) + break; + + alias = array_append_space(&auser->aliases); + alias->old_vname = old_vname; + alias->new_vname = new_vname; + + str_truncate(oldkey, 0); + str_truncate(newkey, 0); + str_printfa(oldkey, "mailbox_alias_old%u", i); + str_printfa(newkey, "mailbox_alias_new%u", i); + } + + MODULE_CONTEXT_SET(user, mailbox_alias_user_module, auser); +} + +static void mailbox_alias_mailbox_list_created(struct mailbox_list *list) +{ + struct mailbox_list_vfuncs *v = list->vlast; + struct mailbox_alias_mailbox_list *alist; + + alist = p_new(list->pool, struct mailbox_alias_mailbox_list, 1); + alist->module_ctx.super = *v; + list->vlast = &alist->module_ctx.super; + + v->get_storage_name = mailbox_alias_get_storage_name; + MODULE_CONTEXT_SET(list, mailbox_alias_mailbox_list_module, alist); +} + +static void mailbox_alias_mailbox_allocated(struct mailbox *box) +{ + struct mailbox_vfuncs *v = box->vlast; + struct mailbox_alias_mailbox *abox; + + abox = p_new(box->pool, struct mailbox_alias_mailbox, 1); + abox->module_ctx.super = *v; + box->vlast = &abox->module_ctx.super; + + v->create_box = mailbox_alias_create; + v->delete_box = mailbox_alias_delete; + v->rename_box = mailbox_alias_rename; + MODULE_CONTEXT_SET(box, mailbox_alias_storage_module, abox); +} + +static struct mail_storage_hooks mailbox_alias_mail_storage_hooks = { + .mail_user_created = mailbox_alias_mail_user_created, + .mailbox_list_created = mailbox_alias_mailbox_list_created, + .mailbox_allocated = mailbox_alias_mailbox_allocated +}; + +void mailbox_alias_plugin_init(struct module *module) +{ + mail_storage_hooks_add(module, &mailbox_alias_mail_storage_hooks); +} + +void mailbox_alias_plugin_deinit(void) +{ + mail_storage_hooks_remove(&mailbox_alias_mail_storage_hooks); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/mailbox-alias/mailbox-alias-plugin.h Wed Sep 26 18:01:01 2012 +0300 @@ -0,0 +1,7 @@ +#ifndef MAILBOX_ALIAS_PLUGIN_H +#define MAILBOX_ALIAS_PLUGIN_H + +void mailbox_alias_plugin_init(struct module *module); +void mailbox_alias_plugin_deinit(void); + +#endif
--- a/src/plugins/quota/quota-count.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/plugins/quota/quota-count.c Wed Sep 26 18:01:01 2012 +0300 @@ -72,6 +72,7 @@ int ret = 0; ctx = mailbox_list_iter_init(ns->list, "*", + MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(ctx)) != NULL) { if ((info->flags & (MAILBOX_NONEXISTENT |
--- a/src/plugins/quota/quota-maildir.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/plugins/quota/quota-maildir.c Wed Sep 26 18:01:01 2012 +0300 @@ -131,6 +131,7 @@ ctx->path = str_new(default_pool, 512); ctx->list = list; ctx->iter = mailbox_list_iter_init(list, "*", + MAILBOX_LIST_ITER_SKIP_ALIASES | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); return ctx; }
--- a/src/plugins/quota/quota-private.h Wed Sep 26 17:17:08 2012 +0300 +++ b/src/plugins/quota/quota-private.h Wed Sep 26 18:01:01 2012 +0300 @@ -33,7 +33,7 @@ int64_t bytes_limit, count_limit; /* relative to default_rule */ - unsigned int bytes_percent, count_percent; + int bytes_percent, count_percent; /* Don't include this mailbox in quota */ unsigned int ignore:1;
--- a/src/plugins/quota/quota-storage.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/plugins/quota/quota-storage.c Wed Sep 26 18:01:01 2012 +0300 @@ -66,6 +66,36 @@ qmail->super.expunge(_mail); } +static int +quota_get_status(struct mailbox *box, enum mailbox_status_items items, + struct mailbox_status *status_r) +{ + struct quota_mailbox *qbox = QUOTA_CONTEXT(box); + struct quota_transaction_context *qt; + bool too_large; + int ret = 0; + + if ((items & STATUS_CHECK_OVER_QUOTA) != 0) { + qt = quota_transaction_begin(box); + if ((ret = quota_test_alloc(qt, 1, &too_large)) == 0) { + mail_storage_set_error(box->storage, MAIL_ERROR_NOSPACE, + qt->quota->set->quota_exceeded_msg); + ret = -1; + } + quota_transaction_rollback(&qt); + + if ((items & ~STATUS_CHECK_OVER_QUOTA) == 0) { + /* don't bother calling parent, it may unnecessarily + try to open the mailbox */ + return ret; + } + } + + if (qbox->module_ctx.super.get_status(box, items, status_r) < 0) + ret = -1; + return ret < 0 ? -1 : 0; +} + static struct mailbox_transaction_context * quota_mailbox_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) @@ -390,6 +420,7 @@ qbox->module_ctx.super = *v; box->vlast = &qbox->module_ctx.super; + v->get_status = quota_get_status; v->transaction_begin = quota_mailbox_transaction_begin; v->transaction_commit = quota_mailbox_transaction_commit; v->transaction_rollback = quota_mailbox_transaction_rollback;
--- a/src/plugins/quota/quota.c Wed Sep 26 17:17:08 2012 +0300 +++ b/src/plugins/quota/quota.c Wed Sep 26 18:01:01 2012 +0300 @@ -361,7 +361,7 @@ { int64_t percentage = *limit; - if (percentage <= 0 || percentage >= -1U) { + if (percentage <= -100 || percentage >= -1U) { *error_r = p_strdup_printf(root_set->set->pool, "Invalid rule percentage: %lld", (long long)percentage); return -1; @@ -385,9 +385,9 @@ quota_rule_recalculate_relative_rules(struct quota_rule *rule, int64_t bytes_limit, int64_t count_limit) { - if (rule->bytes_percent > 0) + if (rule->bytes_percent != 0) rule->bytes_limit = bytes_limit * rule->bytes_percent / 100; - if (rule->count_percent > 0) + if (rule->count_percent != 0) rule->count_limit = count_limit * rule->count_percent / 100; }