+dovecot: Jan 17 17:04:38 Panic: dict: file driver-pgsql.c: line 202 (driver_pgsql_init_v): assertion failed: (connect_string != NULL)
+ ^ kun puuttui connect setting
+
+dovecot: Jan 17 17:05:58 Error: dict: io_loop_handle_remove: epoll_ctl(2, 9): No such file or directory
+dovecot: Jan 17 17:05:58 Error: dict: pgsql: Connect failed to mails: FATAL: password authentication failed for user "timo"
+
+dovecot: Jan 17 17:13:07 Error: dict: dict sql lookup failed: FATAL: terminating connection due to administrator command
+dovecot: Jan 17 17:13:07 Error: dict: io_loop_handle_remove: epoll_ctl(2, 9): Bad file descriptor
+
+Jan 16 02:30:12 vh3 dovecot: auth(default): sql(user@ex2.com): User query failed: FATAL: terminating connection due to administrator command
+Jan 16 02:30:12 vh3 deliver(user@ex2.com): Auth lookup returned failure
+Jan 16 02:30:12 vh3 dovecot: auth(default): kevent(EV_DELETE, 10) failed: Bad file descriptor
+
+ - namespace shared: prefix = shared/%%d/%%u/: LIST shared/% doesn't work
+ - index_removal_timeout leak when copy&pasting:
+ 10 myrights "INBOX"
+ 11 getacl "INBOX"
- proxying: support fallbacking to local (or other?) server if the first
one is down
user_attrs {
safe_mkstemp(/usr/local/var/run/dovecot/user-not-found/test/temp.hurina.12890.87eb6b37b351b733) failed: No such file or directory
- i_panic("Message count decreased") happens - why?
+ - at least one backtrace shows client_destroy -> client_command_cancel ->
+ imap_sync_deinit
- fts-solr: handle DELETE, RENAME
- fsck -> log_file_tail_offset 2273345664 -> 996 ->
mail-transaction-log.c: line 341 (mail_transaction_log_set_mailbox_sync_pos):
dovecot-acl files? at least not that often..
- virtual mailboxes: backend mailbox where to save mails if they're tried to
be saved to the virtual mailbox.
- - acl example in wiki with acl_dict = file:/var/mail/%d/dovecot.shared
+ - acl example in wiki with acl_shared_dict = file:/var/mail/%d/dovecot.shared
- with list=children
- - change auth_worker_max_request_count to non-zero by default? at least with
- PAM it should be non-zero.. perhaps remove the entire setting and make it
- a pam args?
- add anonymous environment for anon logins
- fs quota: getquotaroot inbox vs. other-box should return different quotas
if two quotas are defined
dnl **
capability_banner="IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE"
-capability="$capability_banner SORT THREAD=REFERENCES MULTIAPPEND UNSELECT IDLE CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH SEARCHRES WITHIN CONTEXT=SEARCH"
+capability="$capability_banner SORT THREAD=REFERENCES MULTIAPPEND UNSELECT IDLE CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH"
AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities)
AC_DEFINE_UNQUOTED(CAPABILITY_BANNER_STRING, "$capability_banner", IMAP capabilities advertised in banner)
imap-expunge.c \
imap-fetch.c \
imap-fetch-body.c \
+ imap-search.c \
imap-search-args.c \
- imap-sort.c \
imap-status.c \
imap-sync.c \
mail-storage-callbacks.c \
common.h \
imap-expunge.h \
imap-fetch.h \
+ imap-search.h \
imap-search-args.h \
- imap-sort.h \
imap-status.h \
imap-sync.h
/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
#include "common.h"
-#include "ostream.h"
-#include "str.h"
-#include "seq-range-array.h"
-#include "imap-resp-code.h"
-#include "imap-quote.h"
-#include "imap-seqset.h"
-#include "imap-util.h"
-#include "mail-search-build.h"
-#include "commands.h"
#include "imap-search-args.h"
-
-enum search_return_options {
- SEARCH_RETURN_ESEARCH = 0x0001,
- SEARCH_RETURN_MIN = 0x0002,
- SEARCH_RETURN_MAX = 0x0004,
- SEARCH_RETURN_ALL = 0x0008,
- SEARCH_RETURN_COUNT = 0x0010,
- SEARCH_RETURN_MODSEQ = 0x0020,
- SEARCH_RETURN_SAVE = 0x0040,
- SEARCH_RETURN_UPDATE = 0x0080,
- SEARCH_RETURN_PARTIAL = 0x0100
-/* Options that don't return any seq/uid results */
-#define SEARCH_RETURN_NORESULTS \
- (SEARCH_RETURN_ESEARCH | SEARCH_RETURN_MODSEQ | SEARCH_RETURN_SAVE | \
- SEARCH_RETURN_UPDATE)
-};
-
-struct imap_search_context {
- struct client_command_context *cmd;
- struct mailbox *box;
- struct mailbox_transaction_context *trans;
- struct mail_search_context *search_ctx;
- struct mail *mail;
-
- struct mail_search_args *sargs;
- enum search_return_options return_options;
- uint32_t partial1, partial2;
-
- struct timeout *to;
- ARRAY_TYPE(seq_range) result;
- unsigned int result_count;
-
- uint64_t highest_seen_modseq;
- struct timeval start_time;
-
- unsigned int have_seqsets:1;
- unsigned int have_modseqs:1;
-};
-
-static int
-imap_partial_range_parse(struct imap_search_context *ctx, const char *str)
-{
- ctx->partial1 = 0;
- ctx->partial2 = 0;
- for (; *str >= '0' && *str <= '9'; str++)
- ctx->partial1 = ctx->partial1 * 10 + *str-'0';
- if (*str != ':' || ctx->partial1 == 0)
- return -1;
- for (str++; *str >= '0' && *str <= '9'; str++)
- ctx->partial2 = ctx->partial2 * 10 + *str-'0';
- if (*str != '\0' || ctx->partial2 == 0)
- return -1;
-
- if (ctx->partial1 > ctx->partial2) {
- uint32_t temp = ctx->partial2;
- ctx->partial2 = ctx->partial1;
- ctx->partial1 = temp;
- }
-
- return 0;
-}
-
-static bool
-search_parse_return_options(struct imap_search_context *ctx,
- const struct imap_arg *args)
-{
- struct client_command_context *cmd = ctx->cmd;
- const char *name, *str;
- unsigned int idx;
-
- while (args->type != IMAP_ARG_EOL) {
- if (args->type != IMAP_ARG_ATOM) {
- client_send_command_error(cmd,
- "SEARCH return options contain non-atoms.");
- return FALSE;
- }
- name = t_str_ucase(IMAP_ARG_STR_NONULL(args));
- args++;
- if (strcmp(name, "MIN") == 0)
- ctx->return_options |= SEARCH_RETURN_MIN;
- else if (strcmp(name, "MAX") == 0)
- ctx->return_options |= SEARCH_RETURN_MAX;
- else if (strcmp(name, "ALL") == 0)
- ctx->return_options |= SEARCH_RETURN_ALL;
- else if (strcmp(name, "COUNT") == 0)
- ctx->return_options |= SEARCH_RETURN_COUNT;
- else if (strcmp(name, "SAVE") == 0)
- ctx->return_options |= SEARCH_RETURN_SAVE;
- else if (strcmp(name, "UPDATE") == 0)
- ctx->return_options |= SEARCH_RETURN_UPDATE;
- else if (strcmp(name, "PARTIAL") == 0) {
- if (ctx->partial1 != 0) {
- client_send_command_error(cmd,
- "PARTIAL can be used only once.");
- return FALSE;
- }
- ctx->return_options |= SEARCH_RETURN_PARTIAL;
- if (args->type != IMAP_ARG_ATOM) {
- client_send_command_error(cmd,
- "PARTIAL range missing.");
- return FALSE;
- }
- str = IMAP_ARG_STR_NONULL(args);
- if (imap_partial_range_parse(ctx, str) < 0) {
- client_send_command_error(cmd,
- "PARTIAL range broken.");
- return FALSE;
- }
- args++;
- } else {
- client_send_command_error(cmd,
- "Unknown SEARCH return option");
- return FALSE;
- }
- }
-
- if ((ctx->return_options & SEARCH_RETURN_UPDATE) != 0 &&
- client_search_update_lookup(cmd->client, cmd->tag, &idx) != NULL) {
- client_send_command_error(cmd, "Duplicate search update tag");
- return FALSE;
- }
- if ((ctx->return_options & SEARCH_RETURN_PARTIAL) != 0 &&
- (ctx->return_options & SEARCH_RETURN_ALL) != 0) {
- client_send_command_error(cmd, "PARTIAL conflicts with ALL");
- return FALSE;
- }
-
- if (ctx->return_options == 0)
- ctx->return_options = SEARCH_RETURN_ALL;
- ctx->return_options |= SEARCH_RETURN_ESEARCH;
- return TRUE;
-}
-
-static void imap_search_args_check(struct imap_search_context *ctx,
- const struct mail_search_arg *sargs)
-{
- for (; sargs != NULL; sargs = sargs->next) {
- switch (sargs->type) {
- case SEARCH_SEQSET:
- ctx->have_seqsets = TRUE;
- break;
- case SEARCH_MODSEQ:
- ctx->have_modseqs = TRUE;
- break;
- case SEARCH_OR:
- case SEARCH_SUB:
- imap_search_args_check(ctx, sargs->value.subargs);
- break;
- default:
- break;
- }
- }
-}
-
-static void imap_search_result_save(struct imap_search_context *ctx)
-{
- struct client *client = ctx->cmd->client;
- struct mail_search_result *result;
- struct imap_search_update *update;
-
- if (!array_is_created(&client->search_updates))
- i_array_init(&client->search_updates, 32);
- else if (array_count(&client->search_updates) >=
- CLIENT_MAX_SEARCH_UPDATES) {
- /* too many updates */
- string_t *str = t_str_new(256);
- str_append(str, "* NO [NOUPDATE ");
- imap_quote_append_string(str, ctx->cmd->tag, FALSE);
- str_append_c(str, ']');
- client_send_line(client, str_c(str));
- ctx->return_options &= ~SEARCH_RETURN_UPDATE;
- return;
- }
- result = mailbox_search_result_save(ctx->search_ctx,
- MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
- MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC);
-
- update = array_append_space(&client->search_updates);
- update->tag = i_strdup(ctx->cmd->tag);
- update->result = result;
- update->return_uids = ctx->cmd->uid;
-}
-
-static void
-imap_search_init(struct imap_search_context *ctx,
- struct mail_search_args *sargs)
-{
- imap_search_args_check(ctx, sargs->args);
-
- if (ctx->have_modseqs) {
- ctx->return_options |= SEARCH_RETURN_MODSEQ;
- client_enable(ctx->cmd->client, MAILBOX_FEATURE_CONDSTORE);
- }
-
- ctx->box = ctx->cmd->client->mailbox;
- ctx->trans = mailbox_transaction_begin(ctx->box, 0);
- ctx->sargs = sargs;
- ctx->search_ctx = mailbox_search_init(ctx->trans, sargs, NULL);
- ctx->mail = mail_alloc(ctx->trans, 0, NULL);
- (void)gettimeofday(&ctx->start_time, NULL);
- i_array_init(&ctx->result, 128);
- if ((ctx->return_options & SEARCH_RETURN_UPDATE) != 0)
- imap_search_result_save(ctx);
-}
-
-static void imap_search_send_result_standard(struct imap_search_context *ctx)
-{
- const struct seq_range *range;
- unsigned int i, count;
- string_t *str;
- uint32_t seq;
-
- str = t_str_new(1024);
- range = array_get(&ctx->result, &count);
- str_append(str, "* SEARCH");
- for (i = 0; i < count; i++) {
- for (seq = range[i].seq1; seq <= range[i].seq2; seq++)
- str_printfa(str, " %u", seq);
- if (str_len(str) >= 1024-32) {
- o_stream_send(ctx->cmd->client->output,
- str_data(str), str_len(str));
- str_truncate(str, 0);
- }
- }
-
- if (ctx->highest_seen_modseq != 0) {
- str_printfa(str, " (MODSEQ %llu)",
- (unsigned long long)ctx->highest_seen_modseq);
- }
- str_append(str, "\r\n");
- o_stream_send(ctx->cmd->client->output,
- str_data(str), str_len(str));
-}
-
-static void
-imap_search_send_partial(struct imap_search_context *ctx, string_t *str)
-{
- struct seq_range_iter iter;
- uint32_t n1, n2;
-
- str_printfa(str, " PARTIAL (%u:%u ", ctx->partial1, ctx->partial2);
- seq_range_array_iter_init(&iter, &ctx->result);
- if (!seq_range_array_iter_nth(&iter, ctx->partial1 - 1, &n1)) {
- /* no results (in range) */
- str_append(str, "NIL)");
- return;
- }
- if (!seq_range_array_iter_nth(&iter, ctx->partial2 - 1, &n2))
- n2 = (uint32_t)-1;
-
- /* FIXME: we should save the search result for later use */
- if (n1 > 1)
- seq_range_array_remove_range(&ctx->result, 1, n1 - 1);
- if (n2 != (uint32_t)-1) {
- seq_range_array_remove_range(&ctx->result,
- n2 + 1, (uint32_t)-1);
- }
- imap_write_seq_range(str, &ctx->result);
- str_append_c(str, ')');
-}
-
-static void imap_search_send_result(struct imap_search_context *ctx)
-{
- struct client *client = ctx->cmd->client;
- const struct seq_range *range;
- unsigned int count;
- string_t *str;
-
- if ((ctx->return_options & SEARCH_RETURN_ESEARCH) == 0) {
- imap_search_send_result_standard(ctx);
- return;
- }
-
- if (ctx->return_options ==
- (SEARCH_RETURN_ESEARCH | SEARCH_RETURN_SAVE)) {
- /* we only wanted to save the result, don't return
- ESEARCH result. */
- return;
- }
-
- str = str_new(default_pool, 1024);
- str_append(str, "* ESEARCH (TAG ");
- imap_quote_append_string(str, ctx->cmd->tag, FALSE);
- str_append_c(str, ')');
-
- if (ctx->cmd->uid)
- str_append(str, " UID");
-
- range = array_get(&ctx->result, &count);
- if (count > 0) {
- if ((ctx->return_options & SEARCH_RETURN_MIN) != 0)
- str_printfa(str, " MIN %u", range[0].seq1);
- if ((ctx->return_options & SEARCH_RETURN_MAX) != 0)
- str_printfa(str, " MAX %u", range[count-1].seq2);
- if ((ctx->return_options & SEARCH_RETURN_ALL) != 0) {
- str_append(str, " ALL ");
- imap_write_seq_range(str, &ctx->result);
- }
- }
-
- if ((ctx->return_options & SEARCH_RETURN_PARTIAL) != 0)
- imap_search_send_partial(ctx, str);
-
- if ((ctx->return_options & SEARCH_RETURN_COUNT) != 0)
- str_printfa(str, " COUNT %u", ctx->result_count);
- if (ctx->highest_seen_modseq != 0) {
- str_printfa(str, " MODSEQ %llu",
- (unsigned long long)ctx->highest_seen_modseq);
- }
- str_append(str, "\r\n");
- o_stream_send(client->output, str_data(str), str_len(str));
-}
-
-static int imap_search_deinit(struct imap_search_context *ctx)
-{
- int ret = 0;
-
- mail_free(&ctx->mail);
- if (mailbox_search_deinit(&ctx->search_ctx) < 0)
- ret = -1;
-
- if (ret == 0 && !ctx->cmd->cancel)
- imap_search_send_result(ctx);
- else {
- /* search failed */
- if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0)
- array_clear(&ctx->cmd->client->search_saved_uidset);
- }
-
- (void)mailbox_transaction_commit(&ctx->trans);
-
- if (ctx->to != NULL)
- timeout_remove(&ctx->to);
- array_free(&ctx->result);
- mail_search_args_deinit(ctx->sargs);
- mail_search_args_unref(&ctx->sargs);
-
- ctx->cmd->context = NULL;
- return ret;
-}
-
-static void search_update_mail(struct imap_search_context *ctx)
-{
- uint64_t modseq;
-
- if ((ctx->return_options & SEARCH_RETURN_MODSEQ) != 0) {
- modseq = mail_get_modseq(ctx->mail);
- if (ctx->highest_seen_modseq < modseq)
- ctx->highest_seen_modseq = modseq;
- }
- if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0) {
- seq_range_array_add(&ctx->cmd->client->search_saved_uidset,
- 0, ctx->mail->uid);
- }
-}
-
-static bool cmd_search_more(struct client_command_context *cmd)
-{
- struct imap_search_context *ctx = cmd->context;
- enum search_return_options opts = ctx->return_options;
- enum mailbox_sync_flags sync_flags;
- struct timeval end_time;
- const struct seq_range *range;
- unsigned int count;
- uint32_t id, id_min, id_max;
- const char *ok_reply;
- bool tryagain, minmax, lost_data;
-
- if (cmd->cancel) {
- (void)imap_search_deinit(ctx);
- return TRUE;
- }
-
- range = array_get(&ctx->result, &count);
- if (count == 0) {
- id_min = (uint32_t)-1;
- id_max = 0;
- } else {
- id_min = range[0].seq1;
- id_max = range[count-1].seq2;
- }
-
- minmax = (opts & (SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) != 0 &&
- (opts & ~(SEARCH_RETURN_NORESULTS |
- SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) == 0;
- while (mailbox_search_next_nonblock(ctx->search_ctx, ctx->mail,
- &tryagain) > 0) {
- id = cmd->uid ? ctx->mail->uid : ctx->mail->seq;
- ctx->result_count++;
-
- if (minmax) {
- /* we only care about min/max */
- if (id < id_min && (opts & SEARCH_RETURN_MIN) != 0)
- id_min = id;
- if (id > id_max && (opts & SEARCH_RETURN_MAX) != 0)
- id_max = id;
- if (id == id_min || id == id_max) {
- /* return option updates are delayed until
- we know the actual min/max values */
- seq_range_array_add(&ctx->result, 0, id);
- }
- continue;
- }
-
- search_update_mail(ctx);
- if ((opts & ~(SEARCH_RETURN_NORESULTS |
- SEARCH_RETURN_COUNT)) == 0) {
- /* we only want to count (and get modseqs) */
- continue;
- }
- seq_range_array_add(&ctx->result, 0, id);
- }
- if (tryagain)
- return FALSE;
-
- if (minmax && array_count(&ctx->result) > 0 &&
- (opts & (SEARCH_RETURN_MODSEQ | SEARCH_RETURN_SAVE)) != 0) {
- /* handle MIN/MAX modseq/save updates */
- if ((opts & SEARCH_RETURN_MIN) != 0) {
- i_assert(id_min != (uint32_t)-1);
- if (cmd->uid) {
- if (!mail_set_uid(ctx->mail, id_min))
- i_unreached();
- } else {
- mail_set_seq(ctx->mail, id_min);
- }
- search_update_mail(ctx);
- }
- if ((opts & SEARCH_RETURN_MAX) != 0) {
- i_assert(id_max != 0);
- if (cmd->uid) {
- if (!mail_set_uid(ctx->mail, id_max))
- i_unreached();
- } else {
- mail_set_seq(ctx->mail, id_max);
- }
- search_update_mail(ctx);
- }
- }
-
- lost_data = mailbox_search_seen_lost_data(ctx->search_ctx);
- if (imap_search_deinit(ctx) < 0) {
- client_send_storage_error(cmd,
- mailbox_get_storage(cmd->client->mailbox));
- return TRUE;
- }
-
- if (gettimeofday(&end_time, NULL) < 0)
- memset(&end_time, 0, sizeof(end_time));
- end_time.tv_sec -= ctx->start_time.tv_sec;
- end_time.tv_usec -= ctx->start_time.tv_usec;
- if (end_time.tv_usec < 0) {
- end_time.tv_sec--;
- end_time.tv_usec += 1000000;
- }
-
- sync_flags = MAILBOX_SYNC_FLAG_FAST;
- if (!cmd->uid || ctx->have_seqsets)
- sync_flags |= MAILBOX_SYNC_FLAG_NO_EXPUNGES;
- ok_reply = t_strdup_printf("OK %sSearch completed (%d.%03d secs).",
- lost_data ? "["IMAP_RESP_CODE_EXPUNGEISSUED"] " : "",
- (int)end_time.tv_sec, (int)(end_time.tv_usec/1000));
- return cmd_sync(cmd, sync_flags, 0, ok_reply);
-}
-
-static void cmd_search_more_callback(struct client_command_context *cmd)
-{
- struct client *client = cmd->client;
- bool finished;
-
- o_stream_cork(client->output);
- finished = cmd_search_more(cmd);
- o_stream_uncork(client->output);
-
- if (!finished)
- (void)client_handle_unfinished_cmd(cmd);
- else
- client_command_free(&cmd);
- (void)cmd_sync_delayed(client);
- client_continue_pending_input(&client);
-}
+#include "imap-search.h"
bool cmd_search(struct client_command_context *cmd)
{
- struct client *client = cmd->client;
struct imap_search_context *ctx;
struct mail_search_args *sargs;
const struct imap_arg *args;
"Missing SEARCH arguments.");
return TRUE;
}
- client->input_lock = NULL;
+ cmd->client->input_lock = NULL;
if (!client_verify_open_mailbox(cmd))
return TRUE;
ctx = p_new(cmd->pool, struct imap_search_context, 1);
ctx->cmd = cmd;
- if (args->type == IMAP_ARG_ATOM && args[1].type == IMAP_ARG_LIST &&
- strcasecmp(IMAP_ARG_STR_NONULL(args), "RETURN") == 0) {
- args++;
- if (!search_parse_return_options(ctx, IMAP_ARG_LIST_ARGS(args)))
- return TRUE;
- args++;
-
- if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0) {
- /* wait if there is another SEARCH SAVE command
- running. */
- cmd->search_save_result = TRUE;
- if (client_handle_search_save_ambiguity(cmd))
- return FALSE;
- }
- } else {
- ctx->return_options = SEARCH_RETURN_ALL;
- }
-
- if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0) {
- /* make sure the search result gets cleared if SEARCH fails */
- if (array_is_created(&client->search_saved_uidset))
- array_clear(&client->search_saved_uidset);
- else
- i_array_init(&client->search_saved_uidset, 128);
+ if ((ret = cmd_search_parse_return_if_found(ctx, &args)) <= 0) {
+ /* error / waiting for unambiguity */
+ return ret < 0;
}
if (args->type == IMAP_ARG_ATOM &&
if (ret <= 0)
return ret < 0;
- imap_search_init(ctx, sargs);
- cmd->func = cmd_search_more;
- cmd->context = ctx;
-
- if (cmd_search_more(cmd))
- return TRUE;
-
- /* we could have moved onto syncing by now */
- if (cmd->func == cmd_search_more)
- ctx->to = timeout_add(0, cmd_search_more_callback, cmd);
- return FALSE;
+ return imap_search_start(ctx, sargs, NULL);
}
#include "buffer.h"
#include "commands.h"
#include "imap-search-args.h"
-#include "imap-sort.h"
+#include "imap-search.h"
struct sort_name {
enum mail_sort_type type;
bool cmd_sort(struct client_command_context *cmd)
{
- struct client *client = cmd->client;
+ struct imap_search_context *ctx;
struct mail_search_args *sargs;
- enum mail_sort_type sorting[MAX_SORT_PROGRAM_SIZE];
+ enum mail_sort_type sort_program[MAX_SORT_PROGRAM_SIZE];
const struct imap_arg *args;
int args_count;
const char *charset;
args_count = imap_parser_read_args(cmd->parser, 0, 0, &args);
if (args_count == -2)
return FALSE;
- client->input_lock = NULL;
+ cmd->client->input_lock = NULL;
if (args_count < 3) {
client_send_command_error(cmd, args_count < 0 ? NULL :
return TRUE;
}
- if (get_sort_program(cmd, IMAP_ARG_LIST_ARGS(args), sorting) < 0)
+ if (get_sort_program(cmd, IMAP_ARG_LIST_ARGS(args), sort_program) < 0)
return TRUE;
args++;
charset = IMAP_ARG_STR(args);
args++;
+ ctx = p_new(cmd->pool, struct imap_search_context, 1);
+ ctx->cmd = cmd;
+
+ if ((ret = cmd_search_parse_return_if_found(ctx, &args)) <= 0) {
+ /* error / waiting for unambiguity */
+ return ret < 0;
+ }
+
ret = imap_search_args_build(cmd, args, charset, &sargs);
if (ret <= 0)
return ret < 0;
- ret = imap_sort(cmd, sargs, sorting);
- mail_search_args_unref(&sargs);
- if (ret < 0) {
- client_send_storage_error(cmd,
- mailbox_get_storage(client->mailbox));
- return TRUE;
- }
-
- return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST |
- (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES),
- 0, "OK Sort completed.");
+ return imap_search_start(ctx, sargs, sort_program);
}
--- /dev/null
+/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "ostream.h"
+#include "str.h"
+#include "seq-range-array.h"
+#include "imap-resp-code.h"
+#include "imap-quote.h"
+#include "imap-seqset.h"
+#include "imap-util.h"
+#include "mail-search-build.h"
+#include "commands.h"
+#include "imap-search-args.h"
+#include "imap-search.h"
+
+static int imap_search_deinit(struct imap_search_context *ctx);
+
+static int
+imap_partial_range_parse(struct imap_search_context *ctx, const char *str)
+{
+ ctx->partial1 = 0;
+ ctx->partial2 = 0;
+ for (; *str >= '0' && *str <= '9'; str++)
+ ctx->partial1 = ctx->partial1 * 10 + *str-'0';
+ if (*str != ':' || ctx->partial1 == 0)
+ return -1;
+ for (str++; *str >= '0' && *str <= '9'; str++)
+ ctx->partial2 = ctx->partial2 * 10 + *str-'0';
+ if (*str != '\0' || ctx->partial2 == 0)
+ return -1;
+
+ if (ctx->partial1 > ctx->partial2) {
+ uint32_t temp = ctx->partial2;
+ ctx->partial2 = ctx->partial1;
+ ctx->partial1 = temp;
+ }
+
+ return 0;
+}
+
+static bool
+search_parse_return_options(struct imap_search_context *ctx,
+ const struct imap_arg *args)
+{
+ struct client_command_context *cmd = ctx->cmd;
+ const char *name, *str;
+ unsigned int idx;
+
+ while (args->type != IMAP_ARG_EOL) {
+ if (args->type != IMAP_ARG_ATOM) {
+ client_send_command_error(cmd,
+ "SEARCH return options contain non-atoms.");
+ return FALSE;
+ }
+ name = t_str_ucase(IMAP_ARG_STR_NONULL(args));
+ args++;
+ if (strcmp(name, "MIN") == 0)
+ ctx->return_options |= SEARCH_RETURN_MIN;
+ else if (strcmp(name, "MAX") == 0)
+ ctx->return_options |= SEARCH_RETURN_MAX;
+ else if (strcmp(name, "ALL") == 0)
+ ctx->return_options |= SEARCH_RETURN_ALL;
+ else if (strcmp(name, "COUNT") == 0)
+ ctx->return_options |= SEARCH_RETURN_COUNT;
+ else if (strcmp(name, "SAVE") == 0)
+ ctx->return_options |= SEARCH_RETURN_SAVE;
+ else if (strcmp(name, "UPDATE") == 0)
+ ctx->return_options |= SEARCH_RETURN_UPDATE;
+ else if (strcmp(name, "PARTIAL") == 0) {
+ if (ctx->partial1 != 0) {
+ client_send_command_error(cmd,
+ "PARTIAL can be used only once.");
+ return FALSE;
+ }
+ ctx->return_options |= SEARCH_RETURN_PARTIAL;
+ if (args->type != IMAP_ARG_ATOM) {
+ client_send_command_error(cmd,
+ "PARTIAL range missing.");
+ return FALSE;
+ }
+ str = IMAP_ARG_STR_NONULL(args);
+ if (imap_partial_range_parse(ctx, str) < 0) {
+ client_send_command_error(cmd,
+ "PARTIAL range broken.");
+ return FALSE;
+ }
+ args++;
+ } else {
+ client_send_command_error(cmd,
+ "Unknown SEARCH return option");
+ return FALSE;
+ }
+ }
+
+ if ((ctx->return_options & SEARCH_RETURN_UPDATE) != 0 &&
+ client_search_update_lookup(cmd->client, cmd->tag, &idx) != NULL) {
+ client_send_command_error(cmd, "Duplicate search update tag");
+ return FALSE;
+ }
+ if ((ctx->return_options & SEARCH_RETURN_PARTIAL) != 0 &&
+ (ctx->return_options & SEARCH_RETURN_ALL) != 0) {
+ client_send_command_error(cmd, "PARTIAL conflicts with ALL");
+ return FALSE;
+ }
+
+ if (ctx->return_options == 0)
+ ctx->return_options = SEARCH_RETURN_ALL;
+ ctx->return_options |= SEARCH_RETURN_ESEARCH;
+ return TRUE;
+}
+
+static void imap_search_args_check(struct imap_search_context *ctx,
+ const struct mail_search_arg *sargs)
+{
+ for (; sargs != NULL; sargs = sargs->next) {
+ switch (sargs->type) {
+ case SEARCH_SEQSET:
+ ctx->have_seqsets = TRUE;
+ break;
+ case SEARCH_MODSEQ:
+ ctx->have_modseqs = TRUE;
+ break;
+ case SEARCH_OR:
+ case SEARCH_SUB:
+ imap_search_args_check(ctx, sargs->value.subargs);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void imap_search_result_save(struct imap_search_context *ctx)
+{
+ struct client *client = ctx->cmd->client;
+ struct mail_search_result *result;
+ struct imap_search_update *update;
+
+ if (!array_is_created(&client->search_updates))
+ i_array_init(&client->search_updates, 32);
+ else if (array_count(&client->search_updates) >=
+ CLIENT_MAX_SEARCH_UPDATES) {
+ /* too many updates */
+ string_t *str = t_str_new(256);
+ str_append(str, "* NO [NOUPDATE ");
+ imap_quote_append_string(str, ctx->cmd->tag, FALSE);
+ str_append_c(str, ']');
+ client_send_line(client, str_c(str));
+ ctx->return_options &= ~SEARCH_RETURN_UPDATE;
+ return;
+ }
+ result = mailbox_search_result_save(ctx->search_ctx,
+ MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
+ MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC);
+
+ update = array_append_space(&client->search_updates);
+ update->tag = i_strdup(ctx->cmd->tag);
+ update->result = result;
+ update->return_uids = ctx->cmd->uid;
+}
+
+static void imap_search_send_result_standard(struct imap_search_context *ctx)
+{
+ const struct seq_range *range;
+ unsigned int i, count;
+ string_t *str;
+ uint32_t seq;
+
+ str = t_str_new(1024);
+ range = array_get(&ctx->result, &count);
+ str_append(str, ctx->sorting ? "* SORT" : "* SEARCH");
+ for (i = 0; i < count; i++) {
+ for (seq = range[i].seq1; seq <= range[i].seq2; seq++)
+ str_printfa(str, " %u", seq);
+ if (str_len(str) >= 1024-32) {
+ o_stream_send(ctx->cmd->client->output,
+ str_data(str), str_len(str));
+ str_truncate(str, 0);
+ }
+ }
+
+ if (ctx->highest_seen_modseq != 0) {
+ str_printfa(str, " (MODSEQ %llu)",
+ (unsigned long long)ctx->highest_seen_modseq);
+ }
+ str_append(str, "\r\n");
+ o_stream_send(ctx->cmd->client->output,
+ str_data(str), str_len(str));
+}
+
+static void
+imap_search_send_partial(struct imap_search_context *ctx, string_t *str)
+{
+ struct seq_range *range;
+ uint32_t n, diff;
+ unsigned int i, count, delete_count;
+
+ str_printfa(str, " PARTIAL (%u:%u ", ctx->partial1, ctx->partial2);
+ ctx->partial1--;
+ ctx->partial2--;
+
+ /* we need to be able to handle non-sorted seq ranges, so do this
+ ourself instead of using seq_range_array_*() functions. */
+ range = array_get_modifiable(&ctx->result, &count);
+ delete_count = 0;
+ for (i = n = 0; i < count; i++) {
+ diff = range[i].seq2 - range[i].seq1;
+ if (n + diff >= ctx->partial1) {
+ range[i].seq1 += ctx->partial1 - n;
+ delete_count = i;
+ break;
+ }
+ n += diff + 1;
+ }
+ for (n = ctx->partial1; i < count; i++) {
+ diff = range[i].seq2 - range[i].seq1;
+ if (n + diff >= ctx->partial2) {
+ range[i].seq2 = range[i].seq1 + (ctx->partial2 - n);
+ array_delete(&ctx->result, i + 1, count-(i+1));
+ break;
+ }
+ n += diff + 1;
+ }
+ array_delete(&ctx->result, 0, delete_count);
+
+ if (array_count(&ctx->result) == 0) {
+ /* no results (in range) */
+ str_append(str, "NIL");
+ } else {
+ imap_write_seq_range(str, &ctx->result);
+ }
+ str_append_c(str, ')');
+}
+
+static void imap_search_send_result(struct imap_search_context *ctx)
+{
+ struct client *client = ctx->cmd->client;
+ const struct seq_range *range;
+ unsigned int count;
+ string_t *str;
+
+ if ((ctx->return_options & SEARCH_RETURN_ESEARCH) == 0) {
+ imap_search_send_result_standard(ctx);
+ return;
+ }
+
+ if (ctx->return_options ==
+ (SEARCH_RETURN_ESEARCH | SEARCH_RETURN_SAVE)) {
+ /* we only wanted to save the result, don't return
+ ESEARCH result. */
+ return;
+ }
+
+ str = str_new(default_pool, 1024);
+ str_append(str, "* ESEARCH (TAG ");
+ imap_quote_append_string(str, ctx->cmd->tag, FALSE);
+ str_append_c(str, ')');
+
+ if (ctx->cmd->uid)
+ str_append(str, " UID");
+
+ range = array_get(&ctx->result, &count);
+ if (count > 0) {
+ if ((ctx->return_options & SEARCH_RETURN_MIN) != 0)
+ str_printfa(str, " MIN %u", range[0].seq1);
+ if ((ctx->return_options & SEARCH_RETURN_MAX) != 0)
+ str_printfa(str, " MAX %u", range[count-1].seq2);
+ if ((ctx->return_options & SEARCH_RETURN_ALL) != 0) {
+ str_append(str, " ALL ");
+ imap_write_seq_range(str, &ctx->result);
+ }
+ }
+
+ if ((ctx->return_options & SEARCH_RETURN_PARTIAL) != 0)
+ imap_search_send_partial(ctx, str);
+
+ if ((ctx->return_options & SEARCH_RETURN_COUNT) != 0)
+ str_printfa(str, " COUNT %u", ctx->result_count);
+ if (ctx->highest_seen_modseq != 0) {
+ str_printfa(str, " MODSEQ %llu",
+ (unsigned long long)ctx->highest_seen_modseq);
+ }
+ str_append(str, "\r\n");
+ o_stream_send(client->output, str_data(str), str_len(str));
+}
+
+static void search_update_mail(struct imap_search_context *ctx)
+{
+ uint64_t modseq;
+
+ if ((ctx->return_options & SEARCH_RETURN_MODSEQ) != 0) {
+ modseq = mail_get_modseq(ctx->mail);
+ if (ctx->highest_seen_modseq < modseq)
+ ctx->highest_seen_modseq = modseq;
+ }
+ if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0) {
+ seq_range_array_add(&ctx->cmd->client->search_saved_uidset,
+ 0, ctx->mail->uid);
+ }
+}
+
+static void search_add_result_id(struct imap_search_context *ctx, uint32_t id)
+{
+ struct seq_range *range;
+ unsigned int count;
+
+ /* only append the data. this is especially important when we're
+ returning a sort result. */
+ range = array_get_modifiable(&ctx->result, &count);
+ if (count > 0 && id == range[count-1].seq2 + 1) {
+ range[count-1].seq2++;
+ } else {
+ range = array_append_space(&ctx->result);
+ range->seq1 = range->seq2 = id;
+ }
+}
+
+static bool cmd_search_more(struct client_command_context *cmd)
+{
+ struct imap_search_context *ctx = cmd->context;
+ enum search_return_options opts = ctx->return_options;
+ enum mailbox_sync_flags sync_flags;
+ struct timeval end_time;
+ const struct seq_range *range;
+ unsigned int count;
+ uint32_t id, id_min, id_max;
+ const char *ok_reply;
+ bool tryagain, minmax, lost_data;
+
+ if (cmd->cancel) {
+ (void)imap_search_deinit(ctx);
+ return TRUE;
+ }
+
+ range = array_get(&ctx->result, &count);
+ if (count == 0) {
+ id_min = 0;
+ id_max = 0;
+ } else {
+ id_min = range[0].seq1;
+ id_max = range[count-1].seq2;
+ }
+
+ minmax = (opts & (SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) != 0 &&
+ (opts & ~(SEARCH_RETURN_NORESULTS |
+ SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) == 0;
+ while (mailbox_search_next_nonblock(ctx->search_ctx, ctx->mail,
+ &tryagain) > 0) {
+ id = cmd->uid ? ctx->mail->uid : ctx->mail->seq;
+ ctx->result_count++;
+
+ if (minmax) {
+ /* we only care about min/max */
+ if (id_min == 0 && (opts & SEARCH_RETURN_MIN) != 0)
+ id_min = id;
+ if ((opts & SEARCH_RETURN_MAX) != 0)
+ id_max = id;
+ if (id == id_min || id == id_max) {
+ /* return option updates are delayed until
+ we know the actual min/max values */
+ search_add_result_id(ctx, id);
+ }
+ continue;
+ }
+
+ search_update_mail(ctx);
+ if ((opts & ~(SEARCH_RETURN_NORESULTS |
+ SEARCH_RETURN_COUNT)) == 0) {
+ /* we only want to count (and get modseqs) */
+ continue;
+ }
+ search_add_result_id(ctx, id);
+ }
+ if (tryagain)
+ return FALSE;
+
+ if (minmax && array_count(&ctx->result) > 0 &&
+ (opts & (SEARCH_RETURN_MODSEQ | SEARCH_RETURN_SAVE)) != 0) {
+ /* handle MIN/MAX modseq/save updates */
+ if ((opts & SEARCH_RETURN_MIN) != 0) {
+ i_assert(id_min != 0);
+ if (cmd->uid) {
+ if (!mail_set_uid(ctx->mail, id_min))
+ i_unreached();
+ } else {
+ mail_set_seq(ctx->mail, id_min);
+ }
+ search_update_mail(ctx);
+ }
+ if ((opts & SEARCH_RETURN_MAX) != 0) {
+ i_assert(id_max != 0);
+ if (cmd->uid) {
+ if (!mail_set_uid(ctx->mail, id_max))
+ i_unreached();
+ } else {
+ mail_set_seq(ctx->mail, id_max);
+ }
+ search_update_mail(ctx);
+ }
+ }
+
+ lost_data = mailbox_search_seen_lost_data(ctx->search_ctx);
+ if (imap_search_deinit(ctx) < 0) {
+ client_send_storage_error(cmd,
+ mailbox_get_storage(cmd->client->mailbox));
+ return TRUE;
+ }
+
+ if (gettimeofday(&end_time, NULL) < 0)
+ memset(&end_time, 0, sizeof(end_time));
+ end_time.tv_sec -= ctx->start_time.tv_sec;
+ end_time.tv_usec -= ctx->start_time.tv_usec;
+ if (end_time.tv_usec < 0) {
+ end_time.tv_sec--;
+ end_time.tv_usec += 1000000;
+ }
+
+ sync_flags = MAILBOX_SYNC_FLAG_FAST;
+ if (!cmd->uid || ctx->have_seqsets)
+ sync_flags |= MAILBOX_SYNC_FLAG_NO_EXPUNGES;
+ ok_reply = t_strdup_printf("OK %s%s completed (%d.%03d secs).",
+ lost_data ? "["IMAP_RESP_CODE_EXPUNGEISSUED"] " : "",
+ !ctx->sorting ? "Search" : "Sort",
+ (int)end_time.tv_sec, (int)(end_time.tv_usec/1000));
+ return cmd_sync(cmd, sync_flags, 0, ok_reply);
+}
+
+static void cmd_search_more_callback(struct client_command_context *cmd)
+{
+ struct client *client = cmd->client;
+ bool finished;
+
+ o_stream_cork(client->output);
+ finished = cmd_search_more(cmd);
+ o_stream_uncork(client->output);
+
+ if (!finished)
+ (void)client_handle_unfinished_cmd(cmd);
+ else
+ client_command_free(&cmd);
+ (void)cmd_sync_delayed(client);
+ client_continue_pending_input(&client);
+}
+
+int cmd_search_parse_return_if_found(struct imap_search_context *ctx,
+ const struct imap_arg **_args)
+{
+ const struct imap_arg *args = *_args;
+ struct client_command_context *cmd = ctx->cmd;
+
+ if (!(args->type == IMAP_ARG_ATOM && args[1].type == IMAP_ARG_LIST &&
+ strcasecmp(IMAP_ARG_STR_NONULL(args), "RETURN") == 0)) {
+ ctx->return_options = SEARCH_RETURN_ALL;
+ return 1;
+ }
+
+ args++;
+ if (!search_parse_return_options(ctx, IMAP_ARG_LIST_ARGS(args)))
+ return -1;
+ args++;
+
+ if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0) {
+ /* wait if there is another SEARCH SAVE command running. */
+ cmd->search_save_result = TRUE;
+ if (client_handle_search_save_ambiguity(cmd))
+ return 0;
+
+ /* make sure the search result gets cleared if SEARCH fails */
+ if (array_is_created(&cmd->client->search_saved_uidset))
+ array_clear(&cmd->client->search_saved_uidset);
+ else
+ i_array_init(&cmd->client->search_saved_uidset, 128);
+ }
+
+ *_args = args;
+ return 1;
+}
+
+static void wanted_fields_get(struct mailbox *box,
+ const enum mail_sort_type *sort_program,
+ enum mail_fetch_field *wanted_fields_r,
+ struct mailbox_header_lookup_ctx **headers_ctx_r)
+{
+ const char *headers[2];
+
+ *wanted_fields_r = 0;
+ *headers_ctx_r = NULL;
+
+ if (sort_program == NULL)
+ return;
+
+ headers[0] = headers[1] = NULL;
+ switch (sort_program[0] & MAIL_SORT_MASK) {
+ case MAIL_SORT_ARRIVAL:
+ *wanted_fields_r = MAIL_FETCH_RECEIVED_DATE;
+ break;
+ case MAIL_SORT_CC:
+ headers[0] = "Cc";
+ break;
+ case MAIL_SORT_DATE:
+ *wanted_fields_r = MAIL_FETCH_DATE;
+ break;
+ case MAIL_SORT_FROM:
+ headers[0] = "From";
+ break;
+ case MAIL_SORT_SIZE:
+ *wanted_fields_r = MAIL_FETCH_VIRTUAL_SIZE;
+ break;
+ case MAIL_SORT_SUBJECT:
+ headers[0] = "Subject";
+ break;
+ case MAIL_SORT_TO:
+ headers[0] = "To";
+ break;
+ }
+
+ if (headers[0] != NULL)
+ *headers_ctx_r = mailbox_header_lookup_init(box, headers);
+}
+
+bool imap_search_start(struct imap_search_context *ctx,
+ struct mail_search_args *sargs,
+ const enum mail_sort_type *sort_program)
+{
+ struct client_command_context *cmd = ctx->cmd;
+ enum mail_fetch_field wanted_fields;
+ struct mailbox_header_lookup_ctx *wanted_headers;
+
+ imap_search_args_check(ctx, sargs->args);
+
+ if (ctx->have_modseqs) {
+ ctx->return_options |= SEARCH_RETURN_MODSEQ;
+ client_enable(cmd->client, MAILBOX_FEATURE_CONDSTORE);
+ }
+
+ wanted_fields_get(ctx->box, sort_program,
+ &wanted_fields, &wanted_headers);
+
+ ctx->box = cmd->client->mailbox;
+ ctx->trans = mailbox_transaction_begin(ctx->box, 0);
+ ctx->sargs = sargs;
+ ctx->search_ctx = mailbox_search_init(ctx->trans, sargs, sort_program);
+ ctx->mail = mail_alloc(ctx->trans, wanted_fields, wanted_headers);
+ ctx->sorting = sort_program != NULL;
+ (void)gettimeofday(&ctx->start_time, NULL);
+ i_array_init(&ctx->result, 128);
+ if ((ctx->return_options & SEARCH_RETURN_UPDATE) != 0)
+ imap_search_result_save(ctx);
+
+ cmd->func = cmd_search_more;
+ cmd->context = ctx;
+
+ if (cmd_search_more(cmd))
+ return TRUE;
+
+ /* we may have moved onto syncing by now */
+ if (cmd->func == cmd_search_more)
+ ctx->to = timeout_add(0, cmd_search_more_callback, cmd);
+ return FALSE;
+}
+
+static int imap_search_deinit(struct imap_search_context *ctx)
+{
+ int ret = 0;
+
+ mail_free(&ctx->mail);
+ if (mailbox_search_deinit(&ctx->search_ctx) < 0)
+ ret = -1;
+
+ if (ret == 0 && !ctx->cmd->cancel)
+ imap_search_send_result(ctx);
+ else {
+ /* search failed */
+ if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0)
+ array_clear(&ctx->cmd->client->search_saved_uidset);
+ }
+
+ (void)mailbox_transaction_commit(&ctx->trans);
+
+ if (ctx->to != NULL)
+ timeout_remove(&ctx->to);
+ array_free(&ctx->result);
+ mail_search_args_deinit(ctx->sargs);
+ mail_search_args_unref(&ctx->sargs);
+
+ ctx->cmd->context = NULL;
+ return ret;
+}
--- /dev/null
+#ifndef IMAP_SEARCH_H
+#define IMAP_SEARCH_H
+
+enum search_return_options {
+ SEARCH_RETURN_ESEARCH = 0x0001,
+ SEARCH_RETURN_MIN = 0x0002,
+ SEARCH_RETURN_MAX = 0x0004,
+ SEARCH_RETURN_ALL = 0x0008,
+ SEARCH_RETURN_COUNT = 0x0010,
+ SEARCH_RETURN_MODSEQ = 0x0020,
+ SEARCH_RETURN_SAVE = 0x0040,
+ SEARCH_RETURN_UPDATE = 0x0080,
+ SEARCH_RETURN_PARTIAL = 0x0100
+/* Options that don't return any seq/uid results */
+#define SEARCH_RETURN_NORESULTS \
+ (SEARCH_RETURN_ESEARCH | SEARCH_RETURN_MODSEQ | SEARCH_RETURN_SAVE | \
+ SEARCH_RETURN_UPDATE)
+};
+
+struct imap_search_context {
+ struct client_command_context *cmd;
+ struct mailbox *box;
+ struct mailbox_transaction_context *trans;
+ struct mail_search_context *search_ctx;
+ struct mail *mail;
+
+ struct mail_search_args *sargs;
+ enum search_return_options return_options;
+ uint32_t partial1, partial2;
+
+ struct timeout *to;
+ ARRAY_TYPE(seq_range) result;
+ unsigned int result_count;
+
+ uint64_t highest_seen_modseq;
+ struct timeval start_time;
+
+ unsigned int have_seqsets:1;
+ unsigned int have_modseqs:1;
+ unsigned int sorting:1;
+};
+
+int cmd_search_parse_return_if_found(struct imap_search_context *ctx,
+ const struct imap_arg **args);
+
+bool imap_search_start(struct imap_search_context *ctx,
+ struct mail_search_args *sargs,
+ const enum mail_sort_type *sort_program);
+
+#endif
+++ /dev/null
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-/* Implementation of draft-ietf-imapext-sort-10 sorting algorithm.
- Pretty messy code actually, adding any sort types requires care.
- This is pretty fast however and takes only as much memory as needed to be
- reasonably fast. */
-
-#include "common.h"
-#include "array.h"
-#include "hash.h"
-#include "ostream.h"
-#include "str.h"
-#include "imap-base-subject.h"
-#include "mail-storage.h"
-#include "message-address.h"
-#include "imap-sort.h"
-
-#include <stdlib.h>
-
-#define MAX_WANTED_HEADERS 10
-#define STRBUF_SIZE 1024
-
-#define IS_SORT_STRING(type) \
- ((type) == MAIL_SORT_CC || (type) == MAIL_SORT_FROM || \
- (type) == MAIL_SORT_SUBJECT || (type) == MAIL_SORT_TO)
-
-#define IS_SORT_TIME(type) \
- ((type) == MAIL_SORT_ARRIVAL || (type) == MAIL_SORT_DATE)
-
-struct sort_context {
- enum mail_sort_type sort_program[MAX_SORT_PROGRAM_SIZE];
-
- struct mailbox *box;
- struct ostream *output;
- string_t *str;
-
- bool written;
-};
-
-int imap_sort(struct client_command_context *cmd, struct mail_search_args *args,
- const enum mail_sort_type *sort_program)
-{
- struct client *client = cmd->client;
- const char *wanted_headers[2];
- enum mail_fetch_field wanted_fields;
- struct mail_search_context *search_ctx;
- struct mailbox_transaction_context *t;
- struct mailbox_header_lookup_ctx *headers_ctx;
- struct mail *mail;
- string_t *str;
- bool written = FALSE;
- int ret;
-
- wanted_fields = 0;
- wanted_headers[0] = wanted_headers[1] = NULL;
- switch (*sort_program & MAIL_SORT_MASK) {
- case MAIL_SORT_ARRIVAL:
- wanted_fields = MAIL_FETCH_RECEIVED_DATE;
- break;
- case MAIL_SORT_CC:
- wanted_headers[0] = "Cc";
- break;
- case MAIL_SORT_DATE:
- wanted_fields = MAIL_FETCH_DATE;
- break;
- case MAIL_SORT_FROM:
- wanted_headers[0] = "From";
- break;
- case MAIL_SORT_SIZE:
- wanted_fields = MAIL_FETCH_VIRTUAL_SIZE;
- break;
- case MAIL_SORT_SUBJECT:
- wanted_headers[0] = "Subject";
- break;
- case MAIL_SORT_TO:
- wanted_headers[0] = "To";
- break;
- }
-
- headers_ctx = wanted_headers[0] == NULL ? NULL :
- mailbox_header_lookup_init(client->mailbox, wanted_headers);
-
- t = mailbox_transaction_begin(client->mailbox, 0);
- search_ctx = mailbox_search_init(t, args, sort_program);
-
- str = t_str_new(STRBUF_SIZE);
- str_append(str, "* SORT");
-
- mail = mail_alloc(t, wanted_fields, headers_ctx);
- while (mailbox_search_next(search_ctx, mail) > 0) {
- if (str_len(str) >= STRBUF_SIZE-MAX_INT_STRLEN) {
- o_stream_send(client->output, str_data(str),
- str_len(str));
- str_truncate(str, 0);
- written = TRUE;
- }
- str_printfa(str, " %u", cmd->uid ? mail->uid : mail->seq);
- }
- ret = mailbox_search_deinit(&search_ctx);
- mail_free(&mail);
-
- if (mailbox_transaction_commit(&t) < 0)
- ret = -1;
-
- if (written || ret == 0) {
- str_append(str, "\r\n");
- o_stream_send(client->output, str_data(str), str_len(str));
- }
-
- if (headers_ctx != NULL)
- mailbox_header_lookup_unref(&headers_ctx);
- return ret;
-}
+++ /dev/null
-#ifndef IMAP_SORT_H
-#define IMAP_SORT_H
-
-int imap_sort(struct client_command_context *cmd, struct mail_search_args *args,
- const enum mail_sort_type *sort_program);
-
-#endif