summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/mail-search-args-imap.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
commitf7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch)
treea3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-storage/mail-search-args-imap.c
parentInitial commit. (diff)
downloaddovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz
dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-storage/mail-search-args-imap.c')
-rw-r--r--src/lib-storage/mail-search-args-imap.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/src/lib-storage/mail-search-args-imap.c b/src/lib-storage/mail-search-args-imap.c
new file mode 100644
index 0000000..e89e119
--- /dev/null
+++ b/src/lib-storage/mail-search-args-imap.c
@@ -0,0 +1,326 @@
+/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "array.h"
+#include "str.h"
+#include "utc-offset.h"
+#include "mail-index.h"
+#include "imap-date.h"
+#include "imap-util.h"
+#include "imap-quote.h"
+#include "mail-search.h"
+#include "mail-search-mime.h"
+
+#include <time.h>
+
+static bool
+mail_search_subargs_to_imap(string_t *dest, const struct mail_search_arg *args,
+ const char *prefix, const char **error_r)
+{
+ const struct mail_search_arg *arg;
+
+ if (prefix[0] == '\0')
+ str_append_c(dest, '(');
+ for (arg = args; arg != NULL; arg = arg->next) {
+ if (arg->next != NULL)
+ str_append(dest, prefix);
+ if (!mail_search_arg_to_imap(dest, arg, error_r))
+ return FALSE;
+ if (arg->next != NULL)
+ str_append_c(dest, ' ');
+ }
+ if (prefix[0] == '\0')
+ str_append_c(dest, ')');
+ return TRUE;
+}
+
+static bool
+mail_search_arg_to_imap_date(string_t *dest, const struct mail_search_arg *arg)
+{
+ time_t timestamp = arg->value.time;
+ const char *str;
+
+ if ((arg->value.search_flags &
+ MAIL_SEARCH_ARG_FLAG_UTC_TIMES) == 0) {
+ struct tm *tm = localtime(&timestamp);
+ int tz_offset = utc_offset(tm, timestamp);
+ timestamp -= tz_offset * 60;
+ }
+ if (!imap_to_date(timestamp, &str))
+ return FALSE;
+ str_printfa(dest, " \"%s\"", str);
+ return TRUE;
+}
+
+static void
+mail_search_arg_to_imap_flags(string_t *dest, enum mail_flags flags)
+{
+ static const char *flag_names[] = {
+ "ANSWERED", "FLAGGED", "DELETED", "SEEN", "DRAFT", "RECENT"
+ };
+
+ i_assert(flags != 0);
+
+ if (!bits_is_power_of_two(flags))
+ str_append_c(dest, '(');
+ for (unsigned int i = 0; i < N_ELEMENTS(flag_names); i++) {
+ if ((flags & (1 << i)) != 0) {
+ str_append(dest, flag_names[i]);
+ str_append_c(dest, ' ');
+ }
+ }
+
+ str_truncate(dest, str_len(dest)-1);
+ if (!bits_is_power_of_two(flags))
+ str_append_c(dest, ')');
+}
+
+bool mail_search_arg_to_imap(string_t *dest, const struct mail_search_arg *arg,
+ const char **error_r)
+{
+ unsigned int start_pos;
+
+ if (arg->match_not)
+ str_append(dest, "NOT ");
+ start_pos = str_len(dest);
+ switch (arg->type) {
+ case SEARCH_OR:
+ if (!mail_search_subargs_to_imap(dest, arg->value.subargs,
+ "OR ", error_r))
+ return FALSE;
+ break;
+ case SEARCH_SUB:
+ if (!mail_search_subargs_to_imap(dest, arg->value.subargs,
+ "", error_r))
+ return FALSE;
+ break;
+ case SEARCH_ALL:
+ str_append(dest, "ALL");
+ break;
+ case SEARCH_SEQSET:
+ imap_write_seq_range(dest, &arg->value.seqset);
+ break;
+ case SEARCH_UIDSET:
+ str_append(dest, "UID ");
+ imap_write_seq_range(dest, &arg->value.seqset);
+ break;
+ case SEARCH_FLAGS:
+ mail_search_arg_to_imap_flags(dest, arg->value.flags);
+ break;
+ case SEARCH_KEYWORDS: {
+ const struct mail_keywords *kw = arg->initialized.keywords;
+ const ARRAY_TYPE(keywords) *names_arr;
+ const char *name;
+ unsigned int i;
+
+ if (kw == NULL || kw->count == 0) {
+ /* uninitialized / invalid keyword */
+ str_printfa(dest, "KEYWORD %s", arg->value.str);
+ break;
+ }
+
+ names_arr = mail_index_get_keywords(kw->index);
+
+ if (kw->count > 1)
+ str_append_c(dest, '(');
+ for (i = 0; i < kw->count; i++) {
+ name = array_idx_elem(names_arr, kw->idx[i]);
+ if (i > 0)
+ str_append_c(dest, ' ');
+ str_printfa(dest, "KEYWORD %s", name);
+ }
+ if (kw->count > 1)
+ str_append_c(dest, ')');
+ break;
+ }
+
+ case SEARCH_BEFORE:
+ switch (arg->value.date_type) {
+ case MAIL_SEARCH_DATE_TYPE_SENT:
+ str_append(dest, "SENTBEFORE");
+ break;
+ case MAIL_SEARCH_DATE_TYPE_RECEIVED:
+ str_append(dest, "BEFORE");
+ break;
+ case MAIL_SEARCH_DATE_TYPE_SAVED:
+ str_append(dest, "SAVEDBEFORE");
+ break;
+ }
+ if (mail_search_arg_to_imap_date(dest, arg))
+ ;
+ else if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_RECEIVED ||
+ arg->value.time > ioloop_time) {
+ *error_r = t_strdup_printf(
+ "SEARCH_BEFORE can't be written as IMAP for timestamp %ld (type=%d, utc_times=%d)",
+ (long)arg->value.time, arg->value.date_type,
+ (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_UTC_TIMES) != 0);
+ return FALSE;
+ } else {
+ str_truncate(dest, start_pos);
+ str_printfa(dest, "OLDER %u",
+ (unsigned int)(ioloop_time - arg->value.time + 1));
+ }
+ break;
+ case SEARCH_ON:
+ switch (arg->value.date_type) {
+ case MAIL_SEARCH_DATE_TYPE_SENT:
+ str_append(dest, "SENTON");
+ break;
+ case MAIL_SEARCH_DATE_TYPE_RECEIVED:
+ str_append(dest, "ON");
+ break;
+ case MAIL_SEARCH_DATE_TYPE_SAVED:
+ str_append(dest, "SAVEDON");
+ break;
+ }
+ if (!mail_search_arg_to_imap_date(dest, arg)) {
+ *error_r = t_strdup_printf(
+ "SEARCH_ON can't be written as IMAP for timestamp %ld (type=%d, utc_times=%d)",
+ (long)arg->value.time, arg->value.date_type,
+ (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_UTC_TIMES) != 0);
+ return FALSE;
+ }
+ break;
+ case SEARCH_SINCE:
+ switch (arg->value.date_type) {
+ case MAIL_SEARCH_DATE_TYPE_SENT:
+ str_append(dest, "SENTSINCE");
+ break;
+ case MAIL_SEARCH_DATE_TYPE_RECEIVED:
+ str_append(dest, "SINCE");
+ break;
+ case MAIL_SEARCH_DATE_TYPE_SAVED:
+ str_append(dest, "SAVEDSINCE");
+ break;
+ }
+ if (mail_search_arg_to_imap_date(dest, arg))
+ ;
+ else if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_RECEIVED ||
+ arg->value.time >= ioloop_time) {
+ *error_r = t_strdup_printf(
+ "SEARCH_SINCE can't be written as IMAP for timestamp %ld (type=%d, utc_times=%d)",
+ (long)arg->value.time, arg->value.date_type,
+ (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_UTC_TIMES) != 0);
+ return FALSE;
+ } else {
+ str_truncate(dest, start_pos);
+ str_printfa(dest, "YOUNGER %u",
+ (unsigned int)(ioloop_time - arg->value.time));
+ }
+ break;
+ case SEARCH_SMALLER:
+ str_printfa(dest, "SMALLER %"PRIuUOFF_T, arg->value.size);
+ break;
+ case SEARCH_LARGER:
+ str_printfa(dest, "LARGER %"PRIuUOFF_T, arg->value.size);
+ break;
+ case SEARCH_HEADER:
+ case SEARCH_HEADER_ADDRESS:
+ case SEARCH_HEADER_COMPRESS_LWSP:
+ if (strcasecmp(arg->hdr_field_name, "From") == 0 ||
+ strcasecmp(arg->hdr_field_name, "To") == 0 ||
+ strcasecmp(arg->hdr_field_name, "Cc") == 0 ||
+ strcasecmp(arg->hdr_field_name, "Bcc") == 0 ||
+ strcasecmp(arg->hdr_field_name, "Subject") == 0)
+ str_append(dest, t_str_ucase(arg->hdr_field_name));
+ else {
+ str_append(dest, "HEADER ");
+ imap_append_astring(dest, arg->hdr_field_name);
+ }
+ str_append_c(dest, ' ');
+ imap_append_astring(dest, arg->value.str);
+ break;
+
+ case SEARCH_BODY:
+ str_append(dest, "BODY ");
+ imap_append_astring(dest, arg->value.str);
+ break;
+ case SEARCH_TEXT:
+ str_append(dest, "TEXT ");
+ imap_append_astring(dest, arg->value.str);
+ break;
+
+ /* extensions */
+ case SEARCH_MODSEQ: {
+ bool extended_output = FALSE;
+
+ str_append(dest, "MODSEQ ");
+ if (arg->value.str != NULL) {
+ str_printfa(dest, "/flags/%s", arg->value.str);
+ extended_output = TRUE;
+ } else if (arg->value.flags != 0) {
+ str_append(dest, "/flags/");
+ imap_write_flags(dest, arg->value.flags, NULL);
+ extended_output = TRUE;
+ }
+ if (extended_output) {
+ str_append_c(dest, ' ');
+ switch (arg->value.modseq->type) {
+ case MAIL_SEARCH_MODSEQ_TYPE_ANY:
+ str_append(dest, "all");
+ break;
+ case MAIL_SEARCH_MODSEQ_TYPE_PRIVATE:
+ str_append(dest, "priv");
+ break;
+ case MAIL_SEARCH_MODSEQ_TYPE_SHARED:
+ str_append(dest, "shared");
+ break;
+ }
+ str_append_c(dest, ' ');
+ }
+ str_printfa(dest, "%"PRIu64, arg->value.modseq->modseq);
+ break;
+ }
+ case SEARCH_SAVEDATESUPPORTED:
+ str_append(dest, "SAVEDATESUPPORTED");
+ break;
+ case SEARCH_INTHREAD:
+ str_append(dest, "INTHREAD ");
+ imap_append_astring(dest, mail_thread_type_to_str(arg->value.thread_type));
+ str_append_c(dest, ' ');
+ if (!mail_search_subargs_to_imap(dest, arg->value.subargs,
+ "", error_r))
+ return FALSE;
+ break;
+ case SEARCH_GUID:
+ str_append(dest, "X-GUID ");
+ imap_append_astring(dest, arg->value.str);
+ break;
+ case SEARCH_MAILBOX:
+ *error_r = "SEARCH_MAILBOX can't be written as IMAP";
+ return FALSE;
+ case SEARCH_MAILBOX_GUID:
+ *error_r = "SEARCH_MAILBOX_GUID can't be written as IMAP";
+ return FALSE;
+ case SEARCH_MAILBOX_GLOB:
+ str_append(dest, "X-MAILBOX ");
+ imap_append_astring(dest, arg->value.str);
+ break;
+ case SEARCH_REAL_UID:
+ str_append(dest, "X-REAL-UID ");
+ imap_write_seq_range(dest, &arg->value.seqset);
+ break;
+ case SEARCH_MIMEPART:
+ str_append(dest, "MIMEPART ");
+ if (!mail_search_mime_part_to_imap(dest,
+ arg->value.mime_part, error_r))
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+bool mail_search_args_to_imap(string_t *dest, const struct mail_search_arg *args,
+ const char **error_r)
+{
+ const struct mail_search_arg *arg;
+
+ for (arg = args; arg != NULL; arg = arg->next) {
+ if (!mail_search_arg_to_imap(dest, arg, error_r))
+ return FALSE;
+ if (arg->next != NULL)
+ str_append_c(dest, ' ');
+ }
+ return TRUE;
+}