bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "imap-common.h"
def516ea503a60f20d510c14d5070b7ff5bbddf4Timo Sirainen#include "buffer.h"
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "imap-commands.h"
e5b8b3a57993ab957d389e1c7253698bcd1e6cb1Timo Sirainen#include "imap-search-args.h"
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen#include "imap-search.h"
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstruct sort_name {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen enum mail_sort_type type;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen const char *name;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen};
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstatic struct sort_name sort_names[] = {
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen { MAIL_SORT_ARRIVAL, "arrival" },
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen { MAIL_SORT_CC, "cc" },
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen { MAIL_SORT_DATE, "date" },
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen { MAIL_SORT_FROM, "from" },
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen { MAIL_SORT_SIZE, "size" },
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen { MAIL_SORT_SUBJECT, "subject" },
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen { MAIL_SORT_TO, "to" },
fd14806f879f6cd4f023750e0c4cac27a7f94fbbTimo Sirainen { MAIL_SORT_RELEVANCY, "x-score" }, /* FIXME: obsolete */
fd14806f879f6cd4f023750e0c4cac27a7f94fbbTimo Sirainen { MAIL_SORT_RELEVANCY, "relevancy" },
00d43d5087b555c5c9c5667942ddf41d1b4ecb0fTimo Sirainen { MAIL_SORT_DISPLAYFROM, "displayfrom" },
00d43d5087b555c5c9c5667942ddf41d1b4ecb0fTimo Sirainen { MAIL_SORT_DISPLAYTO, "displayto" },
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen { MAIL_SORT_END, NULL }
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen};
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
a04cd96888653891272a512f7735121193af7b35Timo Sirainenstatic int
f1e9611e93dcb3b745c1904029084fa81644e1b3Timo Sirainenget_sort_program(struct client_command_context *cmd,
f1e9611e93dcb3b745c1904029084fa81644e1b3Timo Sirainen const struct imap_arg *args,
a04cd96888653891272a512f7735121193af7b35Timo Sirainen enum mail_sort_type program[MAX_SORT_PROGRAM_SIZE])
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen{
a04cd96888653891272a512f7735121193af7b35Timo Sirainen enum mail_sort_type mask = 0;
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen const char *arg;
a04cd96888653891272a512f7735121193af7b35Timo Sirainen unsigned int i, pos;
828edf966ee46f65ec5d907f310cab270e7e1088Timo Sirainen bool reverse, last_reverse;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen if (IMAP_ARG_IS_EOL(args)) {
19557f192d37cd54a1a090a8a26d9d47265e4413Aki Tuomi /* empty list */
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen client_send_command_error(cmd, "Empty sort program.");
a04cd96888653891272a512f7735121193af7b35Timo Sirainen return -1;
63946971b08cfb1eec698c28569e1c4aa237852dTimo Sirainen }
63946971b08cfb1eec698c28569e1c4aa237852dTimo Sirainen
828edf966ee46f65ec5d907f310cab270e7e1088Timo Sirainen pos = 0; reverse = last_reverse = FALSE;
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen for (; imap_arg_get_astring(args, &arg); args++) {
828edf966ee46f65ec5d907f310cab270e7e1088Timo Sirainen last_reverse = strcasecmp(arg, "reverse") == 0;
828edf966ee46f65ec5d907f310cab270e7e1088Timo Sirainen if (last_reverse) {
a04cd96888653891272a512f7735121193af7b35Timo Sirainen reverse = !reverse;
a04cd96888653891272a512f7735121193af7b35Timo Sirainen continue;
a04cd96888653891272a512f7735121193af7b35Timo Sirainen }
a04cd96888653891272a512f7735121193af7b35Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen for (i = 0; sort_names[i].type != MAIL_SORT_END; i++) {
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen if (strcasecmp(arg, sort_names[i].name) == 0)
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen break;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen }
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen if (sort_names[i].type == MAIL_SORT_END) {
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen client_send_command_error(cmd, t_strconcat(
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen "Unknown sort argument: ", arg, NULL));
a04cd96888653891272a512f7735121193af7b35Timo Sirainen return -1;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen }
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
a04cd96888653891272a512f7735121193af7b35Timo Sirainen if ((mask & sort_names[i].type) != 0)
a04cd96888653891272a512f7735121193af7b35Timo Sirainen continue;
a04cd96888653891272a512f7735121193af7b35Timo Sirainen mask |= sort_names[i].type;
a04cd96888653891272a512f7735121193af7b35Timo Sirainen
a04cd96888653891272a512f7735121193af7b35Timo Sirainen /* @UNSAFE: mask check should prevent us from ever
a04cd96888653891272a512f7735121193af7b35Timo Sirainen overflowing */
a04cd96888653891272a512f7735121193af7b35Timo Sirainen i_assert(pos < MAX_SORT_PROGRAM_SIZE-1);
a04cd96888653891272a512f7735121193af7b35Timo Sirainen program[pos++] = sort_names[i].type |
a04cd96888653891272a512f7735121193af7b35Timo Sirainen (reverse ? MAIL_SORT_FLAG_REVERSE : 0);
a04cd96888653891272a512f7735121193af7b35Timo Sirainen reverse = FALSE;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen }
828edf966ee46f65ec5d907f310cab270e7e1088Timo Sirainen if (last_reverse) {
828edf966ee46f65ec5d907f310cab270e7e1088Timo Sirainen client_send_command_error(cmd, "Sort list ends with REVERSE.");
828edf966ee46f65ec5d907f310cab270e7e1088Timo Sirainen return -1;
828edf966ee46f65ec5d907f310cab270e7e1088Timo Sirainen }
8bb360f9e5de1c25e4f875205bb06e8bf15dae14Timo Sirainen program[pos] = MAIL_SORT_END;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen if (!IMAP_ARG_IS_EOL(args)) {
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen client_send_command_error(cmd,
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen "Invalid sort list argument.");
a04cd96888653891272a512f7735121193af7b35Timo Sirainen return -1;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen }
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
a04cd96888653891272a512f7735121193af7b35Timo Sirainen return 0;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen}
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenbool cmd_sort(struct client_command_context *cmd)
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen{
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct imap_search_context *ctx;
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen struct mail_search_args *sargs;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen enum mail_sort_type sort_program[MAX_SORT_PROGRAM_SIZE];
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen const struct imap_arg *args, *list_args;
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen const char *charset;
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen int ret;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
2f25f180578a4c280c9f5fda1cb9f22410084a1eTimo Sirainen if (!client_read_args(cmd, 0, 0, &args))
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen return FALSE;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen if (!client_verify_open_mailbox(cmd))
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen return TRUE;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
9995d93294b03312d3fcd99cad602f97af9bff40Timo Sirainen ctx = p_new(cmd->pool, struct imap_search_context, 1);
9995d93294b03312d3fcd99cad602f97af9bff40Timo Sirainen ctx->cmd = cmd;
9995d93294b03312d3fcd99cad602f97af9bff40Timo Sirainen
9995d93294b03312d3fcd99cad602f97af9bff40Timo Sirainen if ((ret = cmd_search_parse_return_if_found(ctx, &args)) <= 0) {
9995d93294b03312d3fcd99cad602f97af9bff40Timo Sirainen /* error / waiting for unambiguity */
9995d93294b03312d3fcd99cad602f97af9bff40Timo Sirainen return ret < 0;
9995d93294b03312d3fcd99cad602f97af9bff40Timo Sirainen }
9995d93294b03312d3fcd99cad602f97af9bff40Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen /* sort program */
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen if (!imap_arg_get_list(args, &list_args)) {
e60a349c641bb2f4723e4a395a25f55531682d2bTimo Sirainen client_send_command_error(cmd, "Invalid sort argument.");
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen imap_search_context_free(ctx);
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen return TRUE;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen }
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (get_sort_program(cmd, list_args, sort_program) < 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen imap_search_context_free(ctx);
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen return TRUE;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen args++;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen /* charset */
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen if (!imap_arg_get_astring(args, &charset)) {
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen client_send_command_error(cmd, "Invalid charset argument.");
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen imap_search_context_free(ctx);
fd4632d0060b2e9eef513b544ccff1e26d1fc222Timo Sirainen return TRUE;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen }
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen args++;
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen ret = imap_search_args_build(cmd, args, charset, &sargs);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (ret <= 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen imap_search_context_free(ctx);
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen return ret < 0;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
97cfe59cd16ce624e58e8d9b6003d1e29d75b3d2Timo Sirainen
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen return imap_search_start(ctx, sargs, sort_program);
55a7410569737197afb302b07b488973324b0cc5Timo Sirainen}