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: i_assert((arg->value.flags & MAIL_FLAGS_MASK) != 0); str_append_c(dest, '('); if ((arg->value.flags & MAIL_ANSWERED) != 0) str_append(dest, "ANSWERED "); if ((arg->value.flags & MAIL_FLAGGED) != 0) str_append(dest, "FLAGGED "); if ((arg->value.flags & MAIL_DELETED) != 0) str_append(dest, "DELETED "); if ((arg->value.flags & MAIL_SEEN) != 0) str_append(dest, "SEEN "); if ((arg->value.flags & MAIL_DRAFT) != 0) str_append(dest, "DRAFT "); if ((arg->value.flags & MAIL_RECENT) != 0) str_append(dest, "RECENT "); str_truncate(dest, str_len(dest)-1); str_append_c(dest, ')'); break; case SEARCH_KEYWORDS: { const struct mail_keywords *kw = arg->value.keywords; const ARRAY_TYPE(keywords) *names_arr; const char *const *namep; unsigned int i; if (kw == NULL) { /* uninitialized */ str_printfa(dest, "KEYWORD %s", arg->value.str); break; } names_arr = mail_index_get_keywords(kw->index); str_append_c(dest, '('); for (i = 0; i < kw->count; i++) { namep = array_idx(names_arr, kw->idx[i]); if (i > 0) str_append_c(dest, ' '); str_printfa(dest, "KEYWORD %s", *namep); } 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, "X-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, use_tz=%d)", (long)arg->value.time, arg->value.date_type, (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 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, "X-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, use_tz=%d)", (long)arg->value.time, arg->value.date_type, (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 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, "X-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, use_tz=%d)", (long)arg->value.time, arg->value.date_type, (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 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 %llu", (unsigned long long)arg->value.size); break; case SEARCH_LARGER: str_printfa(dest, "LARGER %llu", (unsigned long long)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, "%llu", (unsigned long long)arg->value.modseq->modseq); 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; } return TRUE; }
static void mail_search_arg_to_cmdline(string_t *dest, const struct mail_search_arg *arg) { struct mail_search_arg new_arg; const char *error; if (arg->match_not) str_append(dest, "NOT "); switch (arg->type) { case SEARCH_OR: mail_search_subargs_to_cmdline(dest, arg->value.subargs, " OR "); return; case SEARCH_SUB: mail_search_subargs_to_cmdline(dest, arg->value.subargs, " "); return; case SEARCH_FLAGS: case SEARCH_KEYWORDS: { size_t pos = str_len(dest); new_arg = *arg; new_arg.match_not = FALSE; if (!mail_search_arg_to_imap(dest, &new_arg, &error)) i_unreached(); if (str_c(dest)[pos] == '(') { str_insert(dest, pos+1, " "); str_insert(dest, str_len(dest)-1, " "); } return; } case SEARCH_INTHREAD: str_append(dest, "INTHREAD "); imap_append_astring(dest, mail_thread_type_to_str(arg->value.thread_type)); str_append_c(dest, ' '); mail_search_subargs_to_cmdline(dest, arg->value.subargs, " "); break; case SEARCH_MAILBOX: case SEARCH_MAILBOX_GLOB: str_append(dest, "MAILBOX "); imap_append_astring(dest, arg->value.str); return; case SEARCH_MAILBOX_GUID: str_append(dest, "MAILBOX-GUID "); imap_append_astring(dest, arg->value.str); return; case SEARCH_ALL: case SEARCH_SEQSET: case SEARCH_UIDSET: case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: case SEARCH_SMALLER: case SEARCH_LARGER: case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: case SEARCH_BODY: case SEARCH_TEXT: case SEARCH_MODSEQ: case SEARCH_GUID: case SEARCH_REAL_UID: case SEARCH_MIMEPART: break; } new_arg = *arg; new_arg.match_not = FALSE; if (!mail_search_arg_to_imap(dest, &new_arg, &error)) i_panic("mail_search_args_to_cmdline(): Missing handler: %s", error); }