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