1 /* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "array.h"
6 #include "str.h"
7 #include "unichar.h"
8 #include "settings-parser.h"
9 #include "mail-storage.h"
10 #include "mail-search-register.h"
11 #include "mail-search-parser.h"
12 #include "mail-search-build.h"
13 
14 #include <time.h>
15 #include <ctype.h>
16 
17 struct mail_search_register *mail_search_register_human;
18 
19 static struct mail_search_arg *
human_search_or(struct mail_search_build_context * ctx)20 human_search_or(struct mail_search_build_context *ctx)
21 {
22 	struct mail_search_arg *sarg;
23 
24 	/* this changes the parent arg to be an OR block instead of AND block */
25 	ctx->parent->type = SEARCH_OR;
26 	if (mail_search_build_key(ctx, ctx->parent, &sarg) < 0)
27 		return NULL;
28 	return sarg;
29 }
30 
31 static struct mail_search_arg *
arg_new_human_date(struct mail_search_build_context * ctx,enum mail_search_arg_type type,enum mail_search_date_type date_type)32 arg_new_human_date(struct mail_search_build_context *ctx,
33 		   enum mail_search_arg_type type,
34 		   enum mail_search_date_type date_type)
35 {
36 	struct mail_search_arg *sarg;
37 	const char *value;
38 	bool utc;
39 
40 	sarg = mail_search_build_new(ctx, type);
41 	if (mail_search_parse_string(ctx->parser, &value) < 0)
42 		return NULL;
43 
44 	if (mail_parse_human_timestamp(value, &sarg->value.time, &utc) < 0)
45 		sarg->value.time = (time_t)-1;
46 	if (utc)
47 		sarg->value.search_flags = MAIL_SEARCH_ARG_FLAG_UTC_TIMES;
48 
49 	if (sarg->value.time == (time_t)-1) {
50 		ctx->_error = p_strconcat(ctx->pool,
51 			"Invalid search date parameter: ", value, NULL);
52 		return NULL;
53 	}
54 	sarg->value.date_type = date_type;
55 	return sarg;
56 }
57 
58 #define CALLBACK_DATE(_func, _type, _date_type) \
59 static struct mail_search_arg *\
60 human_search_##_func(struct mail_search_build_context *ctx) \
61 { \
62 	return arg_new_human_date(ctx, _type, _date_type); \
63 }
CALLBACK_DATE(before,SEARCH_BEFORE,MAIL_SEARCH_DATE_TYPE_RECEIVED)64 CALLBACK_DATE(before, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_RECEIVED)
65 CALLBACK_DATE(on, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_RECEIVED)
66 CALLBACK_DATE(since, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_RECEIVED)
67 
68 CALLBACK_DATE(sentbefore, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_SENT)
69 CALLBACK_DATE(senton, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_SENT)
70 CALLBACK_DATE(sentsince, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_SENT)
71 
72 CALLBACK_DATE(savedbefore, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_SAVED)
73 CALLBACK_DATE(savedon, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_SAVED)
74 CALLBACK_DATE(savedsince, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_SAVED)
75 
76 static struct mail_search_arg *
77 human_search_savedatesupported(struct mail_search_build_context *ctx)
78 {
79 	return mail_search_build_new(ctx, SEARCH_SAVEDATESUPPORTED);
80 }
81 
82 static struct mail_search_arg *
arg_new_human_size(struct mail_search_build_context * ctx,enum mail_search_arg_type type)83 arg_new_human_size(struct mail_search_build_context *ctx,
84 		   enum mail_search_arg_type type)
85 {
86 	struct mail_search_arg *sarg;
87 	const char *value, *error;
88 
89 	sarg = mail_search_build_new(ctx, type);
90 	if (mail_search_parse_string(ctx->parser, &value) < 0)
91 		return NULL;
92 
93 	if (settings_get_size(value, &sarg->value.size, &error) < 0) {
94 		ctx->_error = p_strdup(ctx->pool, error);
95 		return NULL;
96 	}
97 	return sarg;
98 }
99 
100 static struct mail_search_arg *
human_search_larger(struct mail_search_build_context * ctx)101 human_search_larger(struct mail_search_build_context *ctx)
102 {
103 	return arg_new_human_size(ctx, SEARCH_LARGER);
104 }
105 
106 static struct mail_search_arg *
human_search_smaller(struct mail_search_build_context * ctx)107 human_search_smaller(struct mail_search_build_context *ctx)
108 {
109 	return arg_new_human_size(ctx, SEARCH_SMALLER);
110 }
111 
112 static struct mail_search_arg *
human_search_guid(struct mail_search_build_context * ctx)113 human_search_guid(struct mail_search_build_context *ctx)
114 {
115 	return mail_search_build_str(ctx, SEARCH_GUID);
116 }
117 
118 static struct mail_search_arg *
human_search_mailbox(struct mail_search_build_context * ctx)119 human_search_mailbox(struct mail_search_build_context *ctx)
120 {
121 	struct mail_search_arg *sarg;
122 
123 	sarg = mail_search_build_str(ctx, SEARCH_MAILBOX);
124 	if (sarg == NULL)
125 		return NULL;
126 
127 	if (strchr(sarg->value.str, '*') != NULL ||
128 	    strchr(sarg->value.str, '%') != NULL)
129 		sarg->type = SEARCH_MAILBOX_GLOB;
130 
131 	if (!uni_utf8_str_is_valid(sarg->value.str)) {
132 		ctx->_error = p_strconcat(ctx->pool,
133 			"Mailbox name not valid UTF-8: ",
134 			sarg->value.str, NULL);
135 		return NULL;
136 	}
137 	return sarg;
138 }
139 
140 static struct mail_search_arg *
human_search_mailbox_guid(struct mail_search_build_context * ctx)141 human_search_mailbox_guid(struct mail_search_build_context *ctx)
142 {
143 	return mail_search_build_str(ctx, SEARCH_MAILBOX_GUID);
144 }
145 
146 static struct mail_search_arg *
human_search_oldestonly(struct mail_search_build_context * ctx)147 human_search_oldestonly(struct mail_search_build_context *ctx)
148 {
149 	ctx->args->stop_on_nonmatch = TRUE;
150 	return mail_search_build_new(ctx, SEARCH_ALL);
151 }
152 
153 static const struct mail_search_register_arg human_register_args[] = {
154 	{ "OR", human_search_or },
155 
156 	/* dates */
157 	{ "BEFORE", human_search_before },
158 	{ "ON", human_search_on },
159 	{ "SINCE", human_search_since },
160 	{ "SENTBEFORE", human_search_sentbefore },
161 	{ "SENTON", human_search_senton },
162 	{ "SENTSINCE", human_search_sentsince },
163 	{ "SAVEDBEFORE", human_search_savedbefore },
164 	{ "SAVEDON", human_search_savedon },
165 	{ "SAVEDSINCE", human_search_savedsince },
166 	{ "SAVEDATESUPPORTED", human_search_savedatesupported },
167 	{ "X-SAVEDBEFORE", human_search_savedbefore },
168 	{ "X-SAVEDON", human_search_savedon },
169 	{ "X-SAVEDSINCE", human_search_savedsince },
170 
171 	/* sizes */
172 	{ "LARGER", human_search_larger },
173 	{ "SMALLER", human_search_smaller },
174 
175 	/* Other Dovecot extensions: */
176 	{ "GUID", human_search_guid },
177 	{ "MAILBOX", human_search_mailbox },
178 	{ "MAILBOX-GUID", human_search_mailbox_guid },
179 	{ "OLDESTONLY", human_search_oldestonly }
180 };
181 
182 static struct mail_search_register *
mail_search_register_init_human(struct mail_search_register * imap_register)183 mail_search_register_init_human(struct mail_search_register *imap_register)
184 {
185 	struct mail_search_register *reg;
186 	mail_search_register_fallback_t *fallback;
187 	ARRAY(struct mail_search_register_arg) copy_args;
188 	const struct mail_search_register_arg *human_args, *imap_args;
189 	unsigned int i, j, human_count, imap_count;
190 	int ret;
191 
192 	reg = mail_search_register_init();
193 	mail_search_register_add(reg, human_register_args,
194 				 N_ELEMENTS(human_register_args));
195 
196 	/* find and register args in imap that don't exist in human */
197 	imap_args = mail_search_register_get(imap_register, &imap_count);
198 	human_args = mail_search_register_get(reg, &human_count);
199 	t_array_init(&copy_args, imap_count);
200 	for (i = j = 0; i < imap_count && j < human_count; ) {
201 		ret = strcmp(imap_args[i].key, human_args[j].key);
202 		if (ret < 0) {
203 			array_push_back(&copy_args, &imap_args[i]);
204 			i++;
205 		} else if (ret > 0) {
206 			j++;
207 		} else {
208 			i++; j++;
209 		}
210 	}
211 	for (; i < imap_count; i++)
212 		array_push_back(&copy_args, &imap_args[i]);
213 
214 	imap_args = array_get(&copy_args, &imap_count);
215 	mail_search_register_add(reg, imap_args, imap_count);
216 
217 	if (mail_search_register_get_fallback(imap_register, &fallback))
218 		mail_search_register_fallback(reg, fallback);
219 	return reg;
220 }
221 
mail_search_register_get_human(void)222 struct mail_search_register *mail_search_register_get_human(void)
223 {
224 	if (mail_search_register_human == NULL) {
225 		struct mail_search_register *imap_reg =
226 			mail_search_register_get_imap();
227 
228 		mail_search_register_human =
229 			mail_search_register_init_human(imap_reg);
230 	}
231 	return mail_search_register_human;
232 }
233