index-search.c revision d67fde1a8ebc1d85704c5986d8f93aae97eccef3
/* Copyright (C) 2002 Timo Sirainen */
#include "lib.h"
#include "istream.h"
#include "str.h"
#include "message-address.h"
#include "message-date.h"
#include "message-body-search.h"
#include "message-header-search.h"
#include "message-parser.h"
#include "imap-date.h"
#include "index-storage.h"
#include "index-mail.h"
#include "mail-search.h"
#include <stdlib.h>
#include <ctype.h>
#define TXT_UNKNOWN_CHARSET "[BADCHARSET] Unknown charset"
#define TXT_INVALID_SEARCH_KEY "Invalid search key"
struct index_search_context {
struct mail_search_context mail_ctx;
struct mail_index_view *view;
struct index_mailbox *ibox;
char *charset;
struct mail_search_arg *args;
struct index_mail imail;
const char *error;
int failed;
};
struct search_header_context {
struct index_search_context *index_context;
struct mail_search_arg *args;
struct message_header_line *hdr;
unsigned int custom_header:1;
unsigned int threading:1;
};
struct search_body_context {
struct index_search_context *index_ctx;
const struct message_part *part;
};
{
return TRUE;
}
return FALSE;
}
{
num = 0;
while (*str != '\0') {
return 0;
str++;
}
return num;
}
const struct mail_index_record *rec,
const char *value)
{
const char **keywords;
int i;
for (i = 0; i < INDEX_KEYWORDS_BYTE_COUNT; i++) {
break;
}
if (i == INDEX_KEYWORDS_BYTE_COUNT)
return FALSE; /* no keywords set */
/*FIXME:keywords = mail_keywords_list_get(index->keywords);
for (i = 0; i < MAIL_KEYWORDS_COUNT; i++) {
if (keywords[i] != NULL &&
strcasecmp(keywords[i], value) == 0) {
return rec->msg_flags &
(1 << (MAIL_KEYWORD_1_BIT+i));
}
}*/
return FALSE;
}
/* Returns >0 = matched, 0 = not matched, -1 = unknown */
struct index_mail *imail,
enum mail_search_arg_type type,
const char *value)
{
const struct mail_full_flags *full_flags;
switch (type) {
case SEARCH_ALL:
return 1;
/* flags */
case SEARCH_ANSWERED:
case SEARCH_DELETED:
case SEARCH_DRAFT:
case SEARCH_FLAGGED:
case SEARCH_SEEN:
case SEARCH_RECENT:
case SEARCH_KEYWORD:
default:
return -1;
}
}
{
int found;
return;
}
/* expunged message */
ARG_SET_RESULT(arg, 0);
return;
}
case -1:
/* unknown */
break;
case 0:
ARG_SET_RESULT(arg, 0);
break;
default:
break;
}
}
/* Returns >0 = matched, 0 = not matched, -1 = unknown */
enum mail_search_arg_type type,
const char *value)
{
int timezone_offset;
switch (type) {
/* internal dates */
case SEARCH_BEFORE:
case SEARCH_ON:
case SEARCH_SINCE:
return -1;
return 0;
switch (type) {
case SEARCH_BEFORE:
return date < search_time;
case SEARCH_ON:
return date >= search_time &&
case SEARCH_SINCE:
return date >= search_time;
default:
/* unreachable */
break;
}
/* sent dates */
case SEARCH_SENTBEFORE:
case SEARCH_SENTON:
case SEARCH_SENTSINCE:
/* NOTE: RFC-3501 specifies that timezone is ignored
in searches. date is returned as UTC, so change it. */
return -1;
return 0;
switch (type) {
case SEARCH_SENTBEFORE:
return date < search_time;
case SEARCH_SENTON:
return date >= search_time &&
case SEARCH_SENTSINCE:
return date >= search_time;
default:
/* unreachable */
break;
}
/* sizes */
case SEARCH_SMALLER:
case SEARCH_LARGER:
return -1;
if (type == SEARCH_SMALLER)
return virtual_size < search_size;
else
return virtual_size > search_size;
default:
return -1;
}
}
{
case -1:
/* unknown */
break;
case 0:
ARG_SET_RESULT(arg, 0);
break;
default:
break;
}
}
{
int timezone_offset;
if (sent_value == NULL)
return 0;
return 0;
/* NOTE: RFC-3501 specifies that timezone is ignored
in searches. sent_time is returned as UTC, so change it. */
&sent_time, &timezone_offset))
return 0;
switch (type) {
case SEARCH_SENTBEFORE:
return sent_time < search_time;
case SEARCH_SENTON:
return sent_time >= search_time &&
case SEARCH_SENTSINCE:
return sent_time >= search_time;
default:
i_unreached();
}
}
static struct header_search_context *
struct mail_search_arg *arg)
{
int unknown_charset;
}
}
}
}
{
struct header_search_context *hdr_search_ctx;
int ret;
/* first check that the field name matches to argument. */
case SEARCH_SENTBEFORE:
case SEARCH_SENTON:
case SEARCH_SENTSINCE:
/* date is handled differently than others */
return;
}
}
return;
case SEARCH_HEADER:
case SEARCH_HEADER_ADDRESS:
return;
case SEARCH_TEXT:
/* TEXT goes through all headers */
break;
default:
return;
}
/* we're just testing existence of the field. always matches. */
ret = 1;
} else {
return;
}
t_push();
if (hdr_search_ctx == NULL)
ret = 0;
/* we have to match against normalized address */
struct message_address *addr;
0);
hdr_search_ctx) ? 1 : 0;
} else {
hdr_search_ctx) ? 1 : 0;
}
t_pop();
}
if (ret == 1 ||
/* set only when we definitely know if it's a match */
}
}
void *context __attr_unused__)
{
case SEARCH_SENTBEFORE:
case SEARCH_SENTON:
case SEARCH_SENTSINCE:
/* date header not found, so we match only for
NOT searches */
ARG_SET_RESULT(arg, 0);
}
break;
case SEARCH_HEADER:
case SEARCH_HEADER_ADDRESS:
ARG_SET_RESULT(arg, 0);
break;
default:
break;
}
}
{
/* end of headers, mark all unknown SEARCH_HEADERs unmatched */
return;
}
return;
}
}
{
int ret, unknown_charset;
return;
if (ret < 0) {
}
}
}
struct index_search_context *ctx)
{
struct mailbox_header_lookup_ctx *headers_ctx;
const char *const *headers;
int have_headers, have_body;
/* first check what we need to use */
if (!have_headers && !have_body)
return TRUE;
if (have_headers) {
struct search_header_context hdr_ctx;
if (have_body)
headers_ctx = NULL;
return FALSE;
} else {
/* FIXME: do this once in init */
headers);
return FALSE;
}
}
search_header, &hdr_ctx);
if (headers_ctx != NULL)
} else {
struct message_size hdr_size;
return FALSE;
}
if (have_body) {
struct search_body_context body_ctx;
}
return TRUE;
}
const struct mail_index_header *hdr,
struct mail_search_seqset *set,
{
"Invalid messageset");
return -1;
}
}
return 0;
}
const struct mail_index_header *hdr,
struct mail_search_arg *args,
{
return -1;
/* FIXME: in cases like "SEEN OR 5 7" we shouldn't
limit the range, but in cases like "1 OR 5 7" we
should expand the range. A bit tricky, we'll
just go through everything now to make it work
right. */
*seq1_r = 1;
/* We still have to fix potential seqsets though */
return -1;
return -1;
/* go through everything. don't stop, have to fix
seqsets. */
*seq1_r = 1;
}
}
return 0;
}
{
if (uid_lowwater == 0)
return 0;
return -1;
}
return 0;
}
const struct mail_index_header *hdr,
struct mail_search_arg *args,
{
/* SEEN with 0 seen? */
return 0;
/* UNSEEN with all seen? */
return 0;
/* SEEN with all seen */
/* UNSEEN with lowwater limiting */
if (search_limit_lowwater(ctx,
seq1) < 0)
return -1;
}
}
/* DELETED with 0 deleted? */
return 0;
if (hdr->deleted_messages_count ==
hdr->messages_count) {
/* UNDELETED with all deleted? */
return 0;
/* DELETED with all deleted */
/* DELETED with lowwater limiting */
if (search_limit_lowwater(ctx,
seq1) < 0)
return -1;
}
}
}
}
struct mail_search_arg *args)
{
const struct mail_index_header *hdr;
return -1;
}
if (hdr->messages_count == 0) {
return 0;
}
return -1;
}
/* UNSEEN and DELETED in root search level may limit the range */
return -1;
return 0;
}
enum mail_sort_type *sort_program)
{
/* currently we don't support sorting */
return 0;
}
struct mail_search_context *
const enum mail_sort_type *sort_program,
struct mailbox_header_lookup_ctx *wanted_headers)
{
struct index_transaction_context *t =
(struct index_transaction_context *)_t;
struct index_search_context *ctx;
i_fatal("BUG: index_storage_search_init(): "
"invalid sort_program");
}
}
}
{
int ret;
}
return ret;
}
{
struct mail_search_arg *arg;
int ret;
/* check the index matches first */
if (ret >= 0)
return ret > 0;
/* expunged message, no way to check if the rest would have
matched */
return FALSE;
}
/* next search only from cached arguments */
if (ret >= 0)
return ret > 0;
/* open the mail file and check the rest */
return FALSE;
return FALSE;
}
return TRUE;
}
{
int ret;
ret = 0;
return NULL;
}
t_push();
t_pop();
ret = -1;
if (ret != 0)
break;
}
if (ret <= 0) {
/* error or last record */
return NULL;
}
}