1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "str.h"
5 #include "charset-utf8.h"
6 #include "mail-storage-private.h"
7 #include "mail-search-register.h"
8 #include "mail-search-parser.h"
9 #include "mail-search-build.h"
10 
11 
12 static int mail_search_build_list(struct mail_search_build_context *ctx,
13 				  struct mail_search_arg **arg_r);
14 
15 struct mail_search_arg *
mail_search_build_new(struct mail_search_build_context * ctx,enum mail_search_arg_type type)16 mail_search_build_new(struct mail_search_build_context *ctx,
17 		      enum mail_search_arg_type type)
18 {
19 	struct mail_search_arg *arg;
20 
21 	arg = p_new(ctx->pool, struct mail_search_arg, 1);
22 	arg->type = type;
23 	return arg;
24 }
25 
26 struct mail_search_arg *
mail_search_build_str(struct mail_search_build_context * ctx,enum mail_search_arg_type type)27 mail_search_build_str(struct mail_search_build_context *ctx,
28 		      enum mail_search_arg_type type)
29 {
30 	struct mail_search_arg *sarg;
31 	const char *value;
32 
33 	sarg = mail_search_build_new(ctx, type);
34 	if (mail_search_parse_string(ctx->parser, &value) < 0)
35 		return NULL;
36 	sarg->value.str = p_strdup(ctx->pool, value);
37 	return sarg;
38 }
39 
40 static int
mail_search_build_key_int(struct mail_search_build_context * ctx,struct mail_search_arg * parent,struct mail_search_arg ** arg_r)41 mail_search_build_key_int(struct mail_search_build_context *ctx,
42 			  struct mail_search_arg *parent,
43 			  struct mail_search_arg **arg_r)
44 {
45 	struct mail_search_arg *sarg;
46 	struct mail_search_arg *old_parent = ctx->parent;
47 	const char *key;
48 	const struct mail_search_register_arg *reg_arg;
49 	mail_search_register_fallback_t *fallback;
50 	int ret;
51 
52 	ctx->parent = parent;
53 
54 	if ((ret = mail_search_parse_key(ctx->parser, &key)) <= 0)
55 		return ret;
56 
57 	if (strcmp(key, MAIL_SEARCH_PARSER_KEY_LIST) == 0) {
58 		if (mail_search_build_list(ctx, &sarg) < 0)
59 			return -1;
60 		if (sarg->value.subargs == NULL) {
61 			ctx->_error = "No search parameters inside list";
62 			return -1;
63 		}
64 
65 		ctx->parent = old_parent;
66 		*arg_r = sarg;
67 		return 1;
68 	}
69 	key = t_str_ucase(key);
70 
71 	reg_arg = mail_search_register_find(ctx->reg, key);
72 	if (reg_arg != NULL)
73 		sarg = reg_arg->build(ctx);
74 	else if (mail_search_register_get_fallback(ctx->reg, &fallback))
75 		sarg = fallback(ctx, key);
76 	else {
77 		sarg = NULL;
78 		ctx->_error = p_strconcat(ctx->pool, "Unknown argument ",
79 					  key, NULL);
80 	}
81 
82 	ctx->parent = old_parent;
83 	*arg_r = sarg;
84 	return sarg == NULL ? -1 : 1;
85 }
86 
mail_search_build_key(struct mail_search_build_context * ctx,struct mail_search_arg * parent,struct mail_search_arg ** arg_r)87 int mail_search_build_key(struct mail_search_build_context *ctx,
88 			  struct mail_search_arg *parent,
89 			  struct mail_search_arg **arg_r)
90 {
91 	int ret;
92 
93 	ret = mail_search_build_key_int(ctx, parent, arg_r);
94 	if (ret <= 0) {
95 		if (ret == 0)
96 			ctx->_error = "Missing argument";
97 		return -1;
98 	}
99 	return 0;
100 }
101 
mail_search_build_list(struct mail_search_build_context * ctx,struct mail_search_arg ** arg_r)102 static int mail_search_build_list(struct mail_search_build_context *ctx,
103 				  struct mail_search_arg **arg_r)
104 {
105 	struct mail_search_arg *sarg, **subargs;
106 	enum mail_search_arg_type cur_type = SEARCH_SUB;
107 	int ret;
108 
109 	sarg = p_new(ctx->pool, struct mail_search_arg, 1);
110 	sarg->type = cur_type;
111 
112 	subargs = &sarg->value.subargs;
113 	while ((ret = mail_search_build_key_int(ctx, sarg, subargs)) > 0) {
114 		if (cur_type == sarg->type) {
115 			/* expected type */
116 		} else if (cur_type == SEARCH_SUB) {
117 			/* type changed. everything in this list must now
118 			   belong to this type. */
119 			cur_type = sarg->type;
120 		} else {
121 			ctx->_error =
122 				"Use parenthesis when mixing ANDs and ORs";
123 			return -1;
124 		}
125 		subargs = &(*subargs)->next;
126 		sarg->type = SEARCH_SUB;
127 	}
128 	if (ret < 0)
129 		return -1;
130 	sarg->type = cur_type;
131 	*arg_r = sarg;
132 	return 0;
133 }
134 
mail_search_build(struct mail_search_register * reg,struct mail_search_parser * parser,const char ** charset,struct mail_search_args ** args_r,const char ** client_error_r)135 int mail_search_build(struct mail_search_register *reg,
136 		      struct mail_search_parser *parser, const char **charset,
137 		      struct mail_search_args **args_r,
138 		      const char **client_error_r)
139 {
140         struct mail_search_build_context ctx;
141 	struct mail_search_args *args;
142 	struct mail_search_arg *root;
143 	const char *str;
144 	int ret;
145 
146 	*args_r = NULL;
147 	*client_error_r = NULL;
148 
149 	i_zero(&ctx);
150 	ctx.args = args = mail_search_build_init();
151 	ctx.pool = args->pool;
152 	ctx.reg = reg;
153 	ctx.parser = parser;
154 	ctx.charset = p_strdup(ctx.pool, *charset);
155 
156 	ret = mail_search_build_list(&ctx, &root);
157 	if (!ctx.charset_checked && ret == 0) {
158 		/* make sure we give an error message if charset is invalid */
159 		ret = mail_search_build_get_utf8(&ctx, "", &str);
160 	}
161 	if (ret < 0) {
162 		*client_error_r = ctx._error != NULL ? t_strdup(ctx._error) :
163 			t_strdup(mail_search_parser_get_error(parser));
164 		if (ctx.unknown_charset)
165 			*charset = NULL;
166 		pool_unref(&args->pool);
167 		return -1;
168 	}
169 
170 	if (root->type == SEARCH_SUB && !root->match_not) {
171 		/* simple SUB root */
172 		args->args = root->value.subargs;
173 	} else {
174 		args->args = root;
175 	}
176 
177 	*args_r = args;
178 	return 0;
179 }
180 
mail_search_build_init(void)181 struct mail_search_args *mail_search_build_init(void)
182 {
183 	struct mail_search_args *args;
184 	pool_t pool;
185 
186 	pool = pool_alloconly_create("mail search args", 4096);
187 	args = p_new(pool, struct mail_search_args, 1);
188 	args->pool = pool;
189 	args->refcount = 1;
190 	return args;
191 }
192 
193 struct mail_search_arg *
mail_search_build_add(struct mail_search_args * args,enum mail_search_arg_type type)194 mail_search_build_add(struct mail_search_args *args,
195 		      enum mail_search_arg_type type)
196 {
197 	struct mail_search_arg *arg;
198 
199 	arg = p_new(args->pool, struct mail_search_arg, 1);
200 	arg->type = type;
201 
202 	arg->next = args->args;
203 	args->args = arg;
204 	return arg;
205 }
206 
mail_search_build_add_all(struct mail_search_args * args)207 void mail_search_build_add_all(struct mail_search_args *args)
208 {
209 	(void)mail_search_build_add(args, SEARCH_ALL);
210 }
211 
mail_search_build_add_seqset(struct mail_search_args * args,uint32_t seq1,uint32_t seq2)212 void mail_search_build_add_seqset(struct mail_search_args *args,
213 				  uint32_t seq1, uint32_t seq2)
214 {
215 	struct mail_search_arg *arg;
216 
217 	arg = mail_search_build_add(args, SEARCH_SEQSET);
218 
219 	p_array_init(&arg->value.seqset, args->pool, 1);
220 	seq_range_array_add_range(&arg->value.seqset, seq1, seq2);
221 }
222 
mail_search_build_get_utf8(struct mail_search_build_context * ctx,const char * input,const char ** output_r)223 int mail_search_build_get_utf8(struct mail_search_build_context *ctx,
224 			       const char *input, const char **output_r)
225 {
226 	int ret;
227 
228 	T_BEGIN {
229 		string_t *utf8 = t_str_new(128);
230 		enum charset_result result;
231 
232 		if (charset_to_utf8_str(ctx->charset, NULL,
233 					input, utf8, &result) < 0) {
234 			/* unknown charset */
235 			ctx->_error = "Unknown charset";
236 			ctx->unknown_charset = TRUE;
237 			ret = -1;
238 		} else if (result != CHARSET_RET_OK) {
239 			/* invalid key */
240 			ctx->_error = "Invalid search key";
241 			ret = -1;
242 		} else {
243 			*output_r = p_strdup(ctx->pool, str_c(utf8));
244 			ret = 0;
245 		}
246 	} T_END;
247 
248 	ctx->charset_checked = TRUE;
249 	return ret;
250 }
251