Mercurial > dovecot > original-hg > dovecot-1.2
view src/imap/imap-search.c @ 6429:65c69a53a7be HEAD
Replaced my Copyright notices. The year range always ends with 2007 now.
My name was replaced with "Dovecot authors". In many cases I didn't really
even own the copyright, so this is more correct.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 16 Sep 2007 14:34:22 +0300 |
parents | 913b188f4dd4 |
children | a5a7d38b6df7 |
line wrap: on
line source
/* Copyright (c) 2002-2007 Dovecot authors, see the included COPYING file */ #include "common.h" #include "mail-storage.h" #include "mail-search.h" #include "imap-date.h" #include "imap-search.h" #include "imap-parser.h" #include "imap-messageset.h" struct search_build_data { pool_t pool; struct mailbox *box; const char *error; }; static int imap_uidset_parse(pool_t pool, struct mailbox *box, const char *uidset, struct mail_search_seqset **seqset_r, const char **error_r) { struct mail_search_seqset *seqset, **p; bool last; *seqset_r = imap_messageset_parse(pool, uidset); if (*seqset_r == NULL) { *error_r = "Invalid UID messageset"; return -1; } p = seqset_r; for (seqset = *seqset_r; seqset != NULL; seqset = seqset->next) { if (seqset->seq1 == (uint32_t)-1) { /* last message, stays same */ continue; } last = seqset->seq2 == (uint32_t)-1; mailbox_get_uids(box, seqset->seq1, seqset->seq2, &seqset->seq1, &seqset->seq2); if (seqset->seq1 == 0 && last) { /* we need special case for too_high_uid:* case */ seqset->seq1 = seqset->seq2 = (uint32_t)-1; } if (seqset->seq1 != 0) p = &seqset->next; else *p = seqset->next; } *error_r = NULL; return 0; } static struct mail_search_arg * search_arg_new(pool_t pool, enum mail_search_arg_type type) { struct mail_search_arg *arg; arg = p_new(pool, struct mail_search_arg, 1); arg->type = type; return arg; } #define ARG_NEW(type) \ arg_new(data, args, next_sarg, type, TRUE, NULL) #define ARG_NEW_FLAG(type) \ arg_new(data, args, next_sarg, type, FALSE, NULL) #define ARG_NEW_HEADER(type, hdr_name) \ arg_new(data, args, next_sarg, type, TRUE, hdr_name) static bool arg_new(struct search_build_data *data, const struct imap_arg **args, struct mail_search_arg **next_sarg, enum mail_search_arg_type type, bool have_value, const char *hdr_name) { struct mail_search_arg *sarg; *next_sarg = sarg = search_arg_new(data->pool, type); if (!have_value) return TRUE; if ((*args)->type == IMAP_ARG_EOL) { data->error = "Missing parameter for argument"; return FALSE; } if ((*args)->type != IMAP_ARG_ATOM && (*args)->type != IMAP_ARG_STRING) { data->error = "Invalid parameter for argument"; return FALSE; } switch (type) { case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: case SEARCH_SENTBEFORE: case SEARCH_SENTON: case SEARCH_SENTSINCE: if (!imap_parse_date(IMAP_ARG_STR(*args), &sarg->value.time)) { data->error = "Invalid search date parameter"; return FALSE; } default: sarg->value.str = p_strdup(data->pool, IMAP_ARG_STR(*args)); break; } *args += 1; if (hdr_name != NULL) sarg->hdr_field_name = p_strdup(data->pool, hdr_name); return TRUE; } static bool search_arg_build(struct search_build_data *data, const struct imap_arg **args, struct mail_search_arg **next_sarg) { struct mail_search_seqset *seqset; struct mail_search_arg **subargs; const struct imap_arg *arg; const char *str; if ((*args)->type == IMAP_ARG_EOL) { data->error = "Missing argument"; return FALSE; } arg = *args; if (arg->type == IMAP_ARG_NIL) { /* NIL not allowed */ data->error = "NIL not allowed"; return FALSE; } if (arg->type == IMAP_ARG_LIST) { const struct imap_arg *listargs = IMAP_ARG_LIST_ARGS(arg); if (listargs->type == IMAP_ARG_EOL) { data->error = "Empty list not allowed"; return FALSE; } *next_sarg = search_arg_new(data->pool, SEARCH_SUB); subargs = &(*next_sarg)->value.subargs; while (listargs->type != IMAP_ARG_EOL) { if (!search_arg_build(data, &listargs, subargs)) return FALSE; subargs = &(*subargs)->next; } *args += 1; return TRUE; } i_assert(arg->type == IMAP_ARG_ATOM || arg->type == IMAP_ARG_STRING); /* string argument - get the name and jump to next */ str = IMAP_ARG_STR(arg); *args += 1; str = t_str_ucase(str); switch (*str) { case 'A': if (strcmp(str, "ANSWERED") == 0) return ARG_NEW_FLAG(SEARCH_ANSWERED); else if (strcmp(str, "ALL") == 0) return ARG_NEW_FLAG(SEARCH_ALL); break; case 'B': if (strcmp(str, "BODY") == 0) { /* <string> */ return ARG_NEW(SEARCH_BODY); } else if (strcmp(str, "BEFORE") == 0) { /* <date> */ return ARG_NEW(SEARCH_BEFORE); } else if (strcmp(str, "BCC") == 0) { /* <string> */ return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); } break; case 'C': if (strcmp(str, "CC") == 0) { /* <string> */ return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); } break; case 'D': if (strcmp(str, "DELETED") == 0) return ARG_NEW_FLAG(SEARCH_DELETED); else if (strcmp(str, "DRAFT") == 0) return ARG_NEW_FLAG(SEARCH_DRAFT); break; case 'F': if (strcmp(str, "FLAGGED") == 0) return ARG_NEW_FLAG(SEARCH_FLAGGED); else if (strcmp(str, "FROM") == 0) { /* <string> */ return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); } break; case 'H': if (strcmp(str, "HEADER") == 0) { /* <field-name> <string> */ const char *key; if ((*args)->type == IMAP_ARG_EOL) { data->error = "Missing parameter for HEADER"; return FALSE; } if ((*args)->type != IMAP_ARG_ATOM && (*args)->type != IMAP_ARG_STRING) { data->error = "Invalid parameter for HEADER"; return FALSE; } key = t_str_ucase(IMAP_ARG_STR(*args)); *args += 1; return ARG_NEW_HEADER(SEARCH_HEADER, key); } break; case 'K': if (strcmp(str, "KEYWORD") == 0) { /* <flag> */ return ARG_NEW(SEARCH_KEYWORD); } break; case 'L': if (strcmp(str, "LARGER") == 0) { /* <n> */ return ARG_NEW(SEARCH_LARGER); } break; case 'N': if (strcmp(str, "NOT") == 0) { if (!search_arg_build(data, args, next_sarg)) return FALSE; (*next_sarg)->not = !(*next_sarg)->not; return TRUE; } else if (strcmp(str, "NEW") == 0) { /* NEW == (RECENT UNSEEN) */ *next_sarg = search_arg_new(data->pool, SEARCH_SUB); subargs = &(*next_sarg)->value.subargs; *subargs = search_arg_new(data->pool, SEARCH_RECENT); (*subargs)->next = search_arg_new(data->pool, SEARCH_SEEN); (*subargs)->next->not = TRUE; return TRUE; } break; case 'O': if (strcmp(str, "OR") == 0) { /* <search-key1> <search-key2> */ *next_sarg = search_arg_new(data->pool, SEARCH_OR); subargs = &(*next_sarg)->value.subargs; for (;;) { if (!search_arg_build(data, args, subargs)) return FALSE; subargs = &(*subargs)->next; /* <key> OR <key> OR ... <key> - put them all under one SEARCH_OR list. */ if ((*args)->type == IMAP_ARG_EOL) break; if ((*args)->type != IMAP_ARG_ATOM || strcasecmp(IMAP_ARG_STR_NONULL(*args), "OR") != 0) break; *args += 1; } if (!search_arg_build(data, args, subargs)) return FALSE; return TRUE; } if (strcmp(str, "ON") == 0) { /* <date> */ return ARG_NEW(SEARCH_ON); } if (strcmp(str, "OLD") == 0) { /* OLD == NOT RECENT */ if (!ARG_NEW_FLAG(SEARCH_RECENT)) return FALSE; (*next_sarg)->not = TRUE; return TRUE; } break; case 'R': if (strcmp(str, "RECENT") == 0) return ARG_NEW_FLAG(SEARCH_RECENT); break; case 'S': if (strcmp(str, "SEEN") == 0) return ARG_NEW_FLAG(SEARCH_SEEN); else if (strcmp(str, "SUBJECT") == 0) { /* <string> */ return ARG_NEW_HEADER(SEARCH_HEADER, str); } else if (strcmp(str, "SENTBEFORE") == 0) { /* <date> */ return ARG_NEW(SEARCH_SENTBEFORE); } else if (strcmp(str, "SENTON") == 0) { /* <date> */ return ARG_NEW(SEARCH_SENTON); } else if (strcmp(str, "SENTSINCE") == 0) { /* <date> */ return ARG_NEW(SEARCH_SENTSINCE); } else if (strcmp(str, "SINCE") == 0) { /* <date> */ return ARG_NEW(SEARCH_SINCE); } else if (strcmp(str, "SMALLER") == 0) { /* <n> */ return ARG_NEW(SEARCH_SMALLER); } break; case 'T': if (strcmp(str, "TEXT") == 0) { /* <string> */ return ARG_NEW(SEARCH_TEXT); } else if (strcmp(str, "TO") == 0) { /* <string> */ return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); } break; case 'U': if (strcmp(str, "UID") == 0) { /* <message set> */ if (!ARG_NEW(SEARCH_SEQSET)) return FALSE; return imap_uidset_parse(data->pool, data->box, (*next_sarg)->value.str, &(*next_sarg)->value.seqset, &data->error) == 0; } else if (strcmp(str, "UNANSWERED") == 0) { if (!ARG_NEW_FLAG(SEARCH_ANSWERED)) return FALSE; (*next_sarg)->not = TRUE; return TRUE; } else if (strcmp(str, "UNDELETED") == 0) { if (!ARG_NEW_FLAG(SEARCH_DELETED)) return FALSE; (*next_sarg)->not = TRUE; return TRUE; } else if (strcmp(str, "UNDRAFT") == 0) { if (!ARG_NEW_FLAG(SEARCH_DRAFT)) return FALSE; (*next_sarg)->not = TRUE; return TRUE; } else if (strcmp(str, "UNFLAGGED") == 0) { if (!ARG_NEW_FLAG(SEARCH_FLAGGED)) return FALSE; (*next_sarg)->not = TRUE; return TRUE; } else if (strcmp(str, "UNKEYWORD") == 0) { /* <flag> */ if (!ARG_NEW(SEARCH_KEYWORD)) return FALSE; (*next_sarg)->not = TRUE; return TRUE; } else if (strcmp(str, "UNSEEN") == 0) { if (!ARG_NEW_FLAG(SEARCH_SEEN)) return FALSE; (*next_sarg)->not = TRUE; return TRUE; } break; case 'X': if (strcmp(str, "X-BODY-FAST") == 0) { /* <string> */ return ARG_NEW(SEARCH_BODY_FAST); } else if (strcmp(str, "X-TEXT-FAST") == 0) { /* <string> */ return ARG_NEW(SEARCH_TEXT_FAST); } break; default: if (*str == '*' || (*str >= '0' && *str <= '9')) { /* <message-set> */ seqset = imap_messageset_parse(data->pool, str); if (seqset == NULL) { data->error = "Invalid messageset"; return FALSE; } if (!ARG_NEW_FLAG(SEARCH_SEQSET)) return FALSE; (*next_sarg)->value.seqset = seqset; return TRUE; } break; } data->error = t_strconcat("Unknown argument ", str, NULL); return FALSE; } struct mail_search_arg * imap_search_args_build(pool_t pool, struct mailbox *box, const struct imap_arg *args, const char **error_r) { struct search_build_data data; struct mail_search_arg *first_sarg, **sargs; *error_r = NULL; data.box = box; data.pool = pool; data.error = NULL; /* get the first arg */ first_sarg = NULL; sargs = &first_sarg; while (args->type != IMAP_ARG_EOL) { if (!search_arg_build(&data, &args, sargs)) { *error_r = data.error; return NULL; } sargs = &(*sargs)->next; } return first_sarg; } static int imap_search_get_msgset_arg(struct client_command_context *cmd, const char *messageset, struct mail_search_arg **arg_r, const char **error_r) { struct mail_search_arg *arg; arg = p_new(cmd->pool, struct mail_search_arg, 1); arg->type = SEARCH_SEQSET; arg->value.seqset = imap_messageset_parse(cmd->pool, messageset); /* when there are no messages, all messagesets are invalid. if there's at least one message: - * gives seq1 = seq2 = (uint32_t)-1 - n:* should work if n <= messages_count - n:m or m should work if m <= messages_count */ if (arg->value.seqset == NULL || cmd->client->messages_count == 0 || (arg->value.seqset->seq1 > cmd->client->messages_count && arg->value.seqset->seq1 != (uint32_t)-1) || (arg->value.seqset->seq2 > cmd->client->messages_count && arg->value.seqset->seq2 != (uint32_t)-1)) { *error_r = "Invalid messageset"; return -1; } *arg_r = arg; return 0; } static int imap_search_get_uidset_arg(pool_t pool, struct mailbox *box, const char *uidset, struct mail_search_arg **arg_r, const char **error_r) { struct mail_search_arg *arg; arg = p_new(pool, struct mail_search_arg, 1); arg->type = SEARCH_SEQSET; *arg_r = arg; return imap_uidset_parse(pool, box, uidset, &arg->value.seqset, error_r); } struct mail_search_arg * imap_search_get_arg(struct client_command_context *cmd, const char *set, bool uid) { struct mail_search_arg *search_arg = NULL; const char *error; int ret; if (!uid) { ret = imap_search_get_msgset_arg(cmd, set, &search_arg, &error); } else { ret = imap_search_get_uidset_arg(cmd->pool, cmd->client->mailbox, set, &search_arg, &error); } if (ret < 0) { client_send_command_error(cmd, error); return NULL; } return search_arg; }