diff src/lib-storage/mail-search.c @ 0:3b1985cbc908 HEAD

Initial revision
author Timo Sirainen <tss@iki.fi>
date Fri, 09 Aug 2002 12:15:38 +0300
parents
children 82b7de533f98
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/mail-search.c	Fri Aug 09 12:15:38 2002 +0300
@@ -0,0 +1,496 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "lib.h"
+#include "mail-search.h"
+
+typedef struct {
+	Pool pool;
+	const char *error;
+} SearchBuildData;
+
+static MailSearchArg *search_arg_new(Pool pool, MailSearchArgType type)
+{
+	MailSearchArg *arg;
+
+	arg = p_new(pool, MailSearchArg, 1);
+	arg->type = type;
+
+	return arg;
+}
+
+#define ARG_NEW(type, value) \
+	arg_new(data, args, next_sarg, type, value)
+
+static int arg_new(SearchBuildData *data, ImapArgList **args,
+		   MailSearchArg **next_sarg, MailSearchArgType type, int value)
+{
+	MailSearchArg *sarg;
+
+	*next_sarg = sarg = search_arg_new(data->pool, type);
+	if (value == 0)
+		return TRUE;
+
+	/* first arg */
+	if (*args == NULL) {
+		data->error = "Missing parameter for argument";
+		return FALSE;
+	}
+
+	sarg->value.str = str_ucase((*args)->arg.data.str);
+	*args = (*args)->next;
+
+	/* second arg */
+	if (value == 2) {
+		if (*args == NULL) {
+			data->error = "Missing parameter for argument";
+			return FALSE;
+		}
+
+		sarg->hdr_value = str_ucase((*args)->arg.data.str);
+		*args = (*args)->next;
+	}
+
+	return TRUE;
+}
+
+static int search_arg_build(SearchBuildData *data, ImapArgList **args,
+			    MailSearchArg **next_sarg)
+{
+	MailSearchArg **subargs;
+	ImapArg *arg;
+	char *str;
+
+	if (*args == NULL) {
+		data->error = "Missing argument";
+		return FALSE;
+	}
+
+	arg = &(*args)->arg;
+
+	if (arg->type == IMAP_ARG_NIL) {
+		/* NIL not allowed */
+		data->error = "NIL not allowed";
+		return FALSE;
+	}
+
+	if (arg->type == IMAP_ARG_LIST) {
+		ImapArgList *list = arg->data.list;
+
+		*next_sarg = search_arg_new(data->pool, SEARCH_SUB);
+		subargs = &(*next_sarg)->value.subargs;
+		while (list != NULL) {
+			if (!search_arg_build(data, &list, subargs))
+				return FALSE;
+			subargs = &(*subargs)->next;
+		}
+
+		*args = (*args)->next;
+		return TRUE;
+	}
+
+	i_assert(arg->type == IMAP_ARG_ATOM ||
+		 arg->type == IMAP_ARG_STRING);
+
+	/* string argument - get the name and jump to next */
+	str = arg->data.str;
+	*args = (*args)->next;
+	str_ucase(str);
+
+	switch (*str) {
+	case 'A':
+		if (strcmp(str, "ANSWERED") == 0)
+			return ARG_NEW(SEARCH_ANSWERED, 0);
+		else if (strcmp(str, "ALL") == 0)
+			return ARG_NEW(SEARCH_ALL, 0);
+		break;
+	case 'B':
+		if (strcmp(str, "BODY") == 0) {
+			/* <string> */
+			return ARG_NEW(SEARCH_BODY, 1);
+		} else if (strcmp(str, "BEFORE") == 0) {
+			/* <date> */
+			return ARG_NEW(SEARCH_BEFORE, 1);
+		} else if (strcmp(str, "BCC") == 0) {
+			/* <string> */
+			return ARG_NEW(SEARCH_BCC, 1);
+		}
+		break;
+	case 'C':
+		if (strcmp(str, "CC") == 0) {
+			/* <string> */
+			return ARG_NEW(SEARCH_CC, 1);
+		}
+		break;
+	case 'D':
+		if (strcmp(str, "DELETED") == 0)
+			return ARG_NEW(SEARCH_DELETED, 0);
+		else if (strcmp(str, "DRAFT") == 0)
+			return ARG_NEW(SEARCH_DRAFT, 0);
+		break;
+	case 'F':
+		if (strcmp(str, "FLAGGED") == 0)
+			return ARG_NEW(SEARCH_FLAGGED, 0);
+		else if (strcmp(str, "FROM") == 0) {
+			/* <string> */
+			return ARG_NEW(SEARCH_FROM, 1);
+		}
+		break;
+	case 'H':
+		if (strcmp(str, "HEADER") == 0) {
+			/* <field-name> <string> */
+			const char *key;
+
+			if (*args == NULL) {
+				data->error = "Missing parameter for HEADER";
+				return FALSE;
+			}
+			key = str_ucase((*args)->arg.data.str);
+
+			if (strcmp(key, "FROM") == 0) {
+				*args = (*args)->next;
+				return ARG_NEW(SEARCH_FROM, 1);
+			} else if (strcmp(key, "TO") == 0) {
+				*args = (*args)->next;
+				return ARG_NEW(SEARCH_TO, 1);
+			} else if (strcmp(key, "CC") == 0) {
+				*args = (*args)->next;
+				return ARG_NEW(SEARCH_CC, 1);
+			} else if (strcmp(key, "BCC") == 0) {
+				*args = (*args)->next;
+				return ARG_NEW(SEARCH_BCC, 1);
+			} else if (strcmp(key, "SUBJECT") == 0) {
+				*args = (*args)->next;
+				return ARG_NEW(SEARCH_SUBJECT, 1);
+			} else {
+				return ARG_NEW(SEARCH_HEADER, 2);
+			}
+		}
+		break;
+	case 'K':
+		if (strcmp(str, "KEYWORD") == 0) {
+			/* <flag> */
+			return ARG_NEW(SEARCH_KEYWORD, 1);
+		}
+		break;
+	case 'L':
+		if (strcmp(str, "LARGER") == 0) {
+			/* <n> */
+			return ARG_NEW(SEARCH_LARGER, 1);
+		}
+		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 == NULL)
+					break;
+
+				arg = &(*args)->arg;
+				if (arg->type != IMAP_ARG_ATOM ||
+				    strcasecmp(arg->data.str, "OR") != 0)
+					break;
+
+				*args = (*args)->next;
+			}
+
+			if (!search_arg_build(data, args, subargs))
+				return FALSE;
+			return TRUE;
+		} if (strcmp(str, "ON") == 0) {
+			/* <date> */
+			return ARG_NEW(SEARCH_ON, 1);
+		} if (strcmp(str, "OLD") == 0) {
+			/* OLD == NOT RECENT */
+			if (!ARG_NEW(SEARCH_RECENT, 0))
+				return FALSE;
+
+			(*next_sarg)->not = TRUE;
+			return TRUE;
+		}
+		break;
+	case 'R':
+		if (strcmp(str, "RECENT") == 0)
+			return ARG_NEW(SEARCH_RECENT, 0);
+		break;
+	case 'S':
+		if (strcmp(str, "SEEN") == 0)
+			return ARG_NEW(SEARCH_SEEN, 0);
+		else if (strcmp(str, "SUBJECT") == 0) {
+			/* <string> */
+			return ARG_NEW(SEARCH_SUBJECT, 1);
+		} else if (strcmp(str, "SENTBEFORE") == 0) {
+			/* <date> */
+			return ARG_NEW(SEARCH_SENTBEFORE, 1);
+		} else if (strcmp(str, "SENTON") == 0) {
+			/* <date> */
+			return ARG_NEW(SEARCH_SENTON, 1);
+		} else if (strcmp(str, "SENTSINCE") == 0) {
+			/* <date> */
+			return ARG_NEW(SEARCH_SENTSINCE, 1);
+		} else if (strcmp(str, "SINCE") == 0) {
+			/* <date> */
+			return ARG_NEW(SEARCH_SINCE, 1);
+		} else if (strcmp(str, "SMALLER") == 0) {
+			/* <n> */
+			return ARG_NEW(SEARCH_SMALLER, 1);
+		}
+		break;
+	case 'T':
+		if (strcmp(str, "TEXT") == 0) {
+			/* <string> */
+			return ARG_NEW(SEARCH_TEXT, 1);
+		} else if (strcmp(str, "TO") == 0) {
+			/* <string> */
+			return ARG_NEW(SEARCH_TO, 1);
+		}
+		break;
+	case 'U':
+		if (strcmp(str, "UID") == 0) {
+			/* <message set> */
+			return ARG_NEW(SEARCH_UID, 1);
+		} else if (strcmp(str, "UNANSWERED") == 0) {
+			if (!ARG_NEW(SEARCH_ANSWERED, 0))
+				return FALSE;
+			(*next_sarg)->not = TRUE;
+			return TRUE;
+		} else if (strcmp(str, "UNDELETED") == 0) {
+			if (!ARG_NEW(SEARCH_DELETED, 0))
+				return FALSE;
+			(*next_sarg)->not = TRUE;
+			return TRUE;
+		} else if (strcmp(str, "UNDRAFT") == 0) {
+			if (!ARG_NEW(SEARCH_DRAFT, 0))
+				return FALSE;
+			(*next_sarg)->not = TRUE;
+			return TRUE;
+		} else if (strcmp(str, "UNFLAGGED") == 0) {
+			if (!ARG_NEW(SEARCH_FLAGGED, 0))
+				return FALSE;
+			(*next_sarg)->not = TRUE;
+			return TRUE;
+		} else if (strcmp(str, "UNKEYWORD") == 0) {
+			if (!ARG_NEW(SEARCH_KEYWORD, 0))
+				return FALSE;
+			(*next_sarg)->not = TRUE;
+			return TRUE;
+		} else if (strcmp(str, "UNSEEN") == 0) {
+			if (!ARG_NEW(SEARCH_SEEN, 0))
+				return FALSE;
+			(*next_sarg)->not = TRUE;
+			return TRUE;
+		}
+		break;
+	default:
+		if (*str == '*' || (*str >= '0' && *str <= '9')) {
+			/* <message-set> */
+			if (!ARG_NEW(SEARCH_SET, 0))
+				return FALSE;
+
+			(*next_sarg)->value.str = str;
+			return TRUE;
+		}
+		break;
+	}
+
+	data->error = t_strconcat("Unknown argument ", str, NULL);
+	return FALSE;
+}
+
+MailSearchArg *mail_search_args_build(Pool pool, ImapArg *args, int args_count,
+				      const char **error)
+{
+        SearchBuildData data;
+	MailSearchArg *first_sarg, **sargs;
+	ImapArgList *list, **listp;
+	int i;
+
+	/* first we need to conver the imap arguments into ImapArgList */
+	list = NULL; listp = &list;
+	for (i = 0; i < args_count; i++) {
+		*listp = t_new(ImapArgList, 1);
+		memcpy(&(*listp)->arg, &args[i], sizeof(ImapArg));
+		listp = &(*listp)->next;
+	}
+
+	data.pool = pool;
+	data.error = NULL;
+
+	/* get the first arg */
+	first_sarg = NULL; sargs = &first_sarg;
+	while (list != NULL) {
+		if (!search_arg_build(&data, &list, sargs)) {
+			*error = data.error;
+			return NULL;
+		}
+		sargs = &(*sargs)->next;
+	}
+
+	*error = NULL;
+	return first_sarg;
+}
+
+void mail_search_args_reset(MailSearchArg *args)
+{
+	while (args != NULL) {
+		if (args->type == SEARCH_OR || args->type == SEARCH_SUB)
+			mail_search_args_reset(args->value.subargs);
+		args->result = 0;
+
+		args = args->next;
+	}
+}
+
+static void search_arg_foreach(MailSearchArg *arg, MailSearchForeachFunc func,
+			       void *user_data)
+{
+	MailSearchArg *subarg;
+
+	if (arg->result != 0)
+		return;
+
+	if (arg->type == SEARCH_SUB) {
+		/* sublist of conditions */
+		i_assert(arg->value.subargs != NULL);
+
+		arg->result = 1;
+		subarg = arg->value.subargs;
+		while (subarg != NULL) {
+			if (subarg->result == 0)
+				search_arg_foreach(subarg, func, user_data);
+
+			if (subarg->result == -1) {
+				/* failed */
+				arg->result = -1;
+				break;
+			}
+
+			if (subarg->result == 0)
+				arg->result = 0;
+
+			subarg = subarg->next;
+		}
+	} else if (arg->type == SEARCH_OR) {
+		/* OR-list of conditions */
+		i_assert(arg->value.subargs != NULL);
+
+		subarg = arg->value.subargs;
+		arg->result = -1;
+		while (subarg != NULL) {
+			if (subarg->result == 0)
+				search_arg_foreach(subarg, func, user_data);
+
+			if (subarg->result == 1) {
+				/* matched */
+				arg->result = 1;
+				break;
+			}
+
+			if (subarg->result == 0)
+				arg->result = 0;
+
+			subarg = subarg->next;
+		}
+	} else {
+		/* just a single condition */
+		func(arg, user_data);
+	}
+}
+
+int mail_search_args_foreach(MailSearchArg *args, MailSearchForeachFunc func,
+			     void *user_data)
+{
+	int result;
+
+	result = 1;
+	for (; args != NULL; args = args->next) {
+		search_arg_foreach(args, func, user_data);
+
+		if (args->result == -1) {
+			/* failed, abort */
+			return -1;
+		}
+
+		if (args->result == 0)
+			result = 0;
+	}
+
+	return result;
+}
+
+static void search_arg_analyze(MailSearchArg *arg, int *have_headers,
+			       int *have_body, int *have_text)
+{
+	MailSearchArg *subarg;
+
+	if (arg->result != 0)
+		return;
+
+	switch (arg->type) {
+	case SEARCH_OR:
+	case SEARCH_SUB:
+		subarg = arg->value.subargs;
+		while (subarg != NULL) {
+			if (subarg->result == 0) {
+				search_arg_analyze(subarg, have_headers,
+						   have_body, have_text);
+			}
+
+			subarg = subarg->next;
+		}
+		break;
+	case SEARCH_FROM:
+	case SEARCH_TO:
+	case SEARCH_CC:
+	case SEARCH_BCC:
+	case SEARCH_SUBJECT:
+	case SEARCH_HEADER:
+		*have_headers = TRUE;
+		break;
+	case SEARCH_BODY:
+		*have_body = TRUE;
+		break;
+	case SEARCH_TEXT:
+		*have_text = TRUE;
+		break;
+	default:
+		break;
+	}
+}
+
+void mail_search_args_analyze(MailSearchArg *args, int *have_headers,
+			      int *have_body, int *have_text)
+{
+	*have_headers = *have_body = *have_text = FALSE;
+
+	for (; args != NULL; args = args->next)
+		search_arg_analyze(args, have_headers, have_body, have_text);
+}
+