/src/pigeonhole/src/lib-sieve/sieve-match.c
Line | Count | Source |
1 | | /* Copyright (c) Pigeonhole authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "mempool.h" |
5 | | #include "hash.h" |
6 | | #include "array.h" |
7 | | #include "str-sanitize.h" |
8 | | |
9 | | #include "sieve-extensions.h" |
10 | | #include "sieve-commands.h" |
11 | | #include "sieve-stringlist.h" |
12 | | #include "sieve-code.h" |
13 | | #include "sieve-binary.h" |
14 | | #include "sieve-validator.h" |
15 | | #include "sieve-generator.h" |
16 | | #include "sieve-interpreter.h" |
17 | | #include "sieve-dump.h" |
18 | | #include "sieve-comparators.h" |
19 | | #include "sieve-match-types.h" |
20 | | #include "sieve-runtime-trace.h" |
21 | | |
22 | | #include "sieve-match.h" |
23 | | |
24 | | /* |
25 | | * Matching implementation |
26 | | */ |
27 | | |
28 | | struct sieve_match_context * |
29 | | sieve_match_begin(const struct sieve_runtime_env *renv, |
30 | | const struct sieve_match_type *mcht, |
31 | | const struct sieve_comparator *cmp) |
32 | 0 | { |
33 | 0 | struct sieve_match_context *mctx; |
34 | 0 | pool_t pool; |
35 | | |
36 | | /* Reject unimplemented match-type */ |
37 | 0 | if (mcht->def == NULL || (mcht->def->match == NULL && |
38 | 0 | mcht->def->match_keys == NULL && mcht->def->match_key == NULL)) |
39 | 0 | return NULL; |
40 | | |
41 | | /* Create match context */ |
42 | 0 | pool = pool_alloconly_create("sieve_match_context", 1024); |
43 | 0 | mctx = p_new(pool, struct sieve_match_context, 1); |
44 | 0 | mctx->pool = pool; |
45 | 0 | mctx->runenv = renv; |
46 | 0 | mctx->match_type = mcht; |
47 | 0 | mctx->comparator = cmp; |
48 | 0 | mctx->exec_status = SIEVE_EXEC_OK; |
49 | 0 | mctx->trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING); |
50 | | |
51 | | /* Trace */ |
52 | 0 | if (mctx->trace) { |
53 | 0 | sieve_runtime_trace_descend(renv); |
54 | 0 | sieve_runtime_trace( |
55 | 0 | renv, 0, "starting ':%s' match with '%s' comparator:", |
56 | 0 | sieve_match_type_name(mcht), |
57 | 0 | sieve_comparator_name(cmp)); |
58 | 0 | } |
59 | | |
60 | | /* Initialize match type */ |
61 | 0 | if (mcht->def != NULL && mcht->def->match_init != NULL) |
62 | 0 | mcht->def->match_init(mctx); |
63 | |
|
64 | 0 | return mctx; |
65 | 0 | } |
66 | | |
67 | | int sieve_match_value(struct sieve_match_context *mctx, |
68 | | const char *value, size_t value_size, |
69 | | struct sieve_stringlist *key_list) |
70 | 0 | { |
71 | 0 | const struct sieve_match_type *mcht = mctx->match_type; |
72 | 0 | const struct sieve_runtime_env *renv = mctx->runenv; |
73 | 0 | int match, ret; |
74 | |
|
75 | 0 | if (mctx->trace) { |
76 | 0 | sieve_runtime_trace(renv, 0, "matching value '%s'", |
77 | 0 | str_sanitize(value, 80)); |
78 | 0 | } |
79 | | |
80 | | /* Match to key values */ |
81 | |
|
82 | 0 | sieve_stringlist_reset(key_list); |
83 | |
|
84 | 0 | if (mctx->trace) |
85 | 0 | sieve_stringlist_set_trace(key_list, TRUE); |
86 | |
|
87 | 0 | sieve_runtime_trace_descend(renv); |
88 | |
|
89 | 0 | if (mcht->def->match_keys != NULL) { |
90 | | /* Call match-type's own key match handler */ |
91 | 0 | match = mcht->def->match_keys(mctx, value, value_size, |
92 | 0 | key_list); |
93 | 0 | } else { |
94 | 0 | string_t *key_item = NULL; |
95 | | |
96 | | /* Default key match loop */ |
97 | 0 | match = 0; |
98 | 0 | while (match == 0 && |
99 | 0 | (ret = sieve_stringlist_next_item( |
100 | 0 | key_list, &key_item)) > 0) T_BEGIN { |
101 | 0 | match = mcht->def->match_key( |
102 | 0 | mctx, value, value_size, |
103 | 0 | str_c(key_item), str_len(key_item)); |
104 | 0 | if (mctx->trace) { |
105 | 0 | sieve_runtime_trace( |
106 | 0 | renv, 0, "with key '%s' => %d", |
107 | 0 | str_sanitize(str_c(key_item), 80), |
108 | 0 | match); |
109 | 0 | } |
110 | 0 | } T_END; |
111 | | |
112 | 0 | if (ret < 0) { |
113 | 0 | mctx->exec_status = key_list->exec_status; |
114 | 0 | match = -1; |
115 | 0 | } |
116 | 0 | } |
117 | | |
118 | 0 | sieve_runtime_trace_ascend(renv); |
119 | |
|
120 | 0 | if (mctx->match_status < 0 || match < 0) |
121 | 0 | mctx->match_status = -1; |
122 | 0 | else { |
123 | 0 | mctx->match_status = (mctx->match_status > match ? |
124 | 0 | mctx->match_status : match); |
125 | 0 | } |
126 | 0 | return match; |
127 | 0 | } |
128 | | |
129 | | int sieve_match_end(struct sieve_match_context **mctx, int *exec_status) |
130 | 0 | { |
131 | 0 | const struct sieve_match_type *mcht = (*mctx)->match_type; |
132 | 0 | const struct sieve_runtime_env *renv = (*mctx)->runenv; |
133 | 0 | int match = (*mctx)->match_status; |
134 | |
|
135 | 0 | if (mcht->def != NULL && mcht->def->match_deinit != NULL) |
136 | 0 | mcht->def->match_deinit(*mctx); |
137 | |
|
138 | 0 | if (exec_status != NULL) |
139 | 0 | *exec_status = (*mctx)->exec_status; |
140 | |
|
141 | 0 | pool_unref(&(*mctx)->pool); |
142 | |
|
143 | 0 | sieve_runtime_trace( |
144 | 0 | renv, SIEVE_TRLVL_MATCHING, "finishing match with result: %s", |
145 | 0 | (match > 0 ? "matched" : |
146 | 0 | (match < 0 ? "error" : "not matched"))); |
147 | 0 | sieve_runtime_trace_ascend(renv); |
148 | |
|
149 | 0 | return match; |
150 | 0 | } |
151 | | |
152 | | int sieve_match(const struct sieve_runtime_env *renv, |
153 | | const struct sieve_match_type *mcht, |
154 | | const struct sieve_comparator *cmp, |
155 | | struct sieve_stringlist *value_list, |
156 | | struct sieve_stringlist *key_list, int *exec_status) |
157 | 0 | { |
158 | 0 | struct sieve_match_context *mctx; |
159 | 0 | string_t *value_item = NULL; |
160 | 0 | int match, ret; |
161 | |
|
162 | 0 | if ((mctx = sieve_match_begin(renv, mcht, cmp)) == NULL) |
163 | 0 | return 0; |
164 | | |
165 | | /* Match value to keys */ |
166 | | |
167 | 0 | sieve_stringlist_reset(value_list); |
168 | 0 | if (mctx->trace) |
169 | 0 | sieve_stringlist_set_trace(value_list, TRUE); |
170 | |
|
171 | 0 | if (mcht->def->match != NULL) { |
172 | | /* Call match-type's match handler */ |
173 | 0 | match = mctx->match_status = |
174 | 0 | mcht->def->match(mctx, value_list, key_list); |
175 | 0 | } else { |
176 | | /* Default value match loop */ |
177 | 0 | match = 0; |
178 | 0 | while (match == 0 && |
179 | 0 | (ret = sieve_stringlist_next_item( |
180 | 0 | value_list, &value_item)) > 0) { |
181 | |
|
182 | 0 | match = sieve_match_value( |
183 | 0 | mctx, str_c(value_item), str_len(value_item), |
184 | 0 | key_list); |
185 | 0 | } |
186 | |
|
187 | 0 | if (ret < 0) { |
188 | 0 | mctx->exec_status = value_list->exec_status; |
189 | 0 | match = -1; |
190 | 0 | } |
191 | 0 | } |
192 | |
|
193 | 0 | (void)sieve_match_end(&mctx, exec_status); |
194 | 0 | return match; |
195 | 0 | } |
196 | | |
197 | | /* |
198 | | * Reading match operands |
199 | | */ |
200 | | |
201 | | int sieve_match_opr_optional_dump(const struct sieve_dumptime_env *denv, |
202 | | sieve_size_t *address, int *opt_code) |
203 | 0 | { |
204 | 0 | int _opt_code = 0; |
205 | 0 | bool final = FALSE, opok = TRUE; |
206 | |
|
207 | 0 | if (opt_code == NULL) { |
208 | 0 | opt_code = &_opt_code; |
209 | 0 | final = TRUE; |
210 | 0 | } |
211 | |
|
212 | 0 | while (opok) { |
213 | 0 | int opt; |
214 | |
|
215 | 0 | if ((opt = sieve_opr_optional_dump(denv, address, |
216 | 0 | opt_code)) <= 0) |
217 | 0 | return opt; |
218 | | |
219 | 0 | switch (*opt_code) { |
220 | 0 | case SIEVE_MATCH_OPT_COMPARATOR: |
221 | 0 | opok = sieve_opr_comparator_dump(denv, address); |
222 | 0 | break; |
223 | 0 | case SIEVE_MATCH_OPT_MATCH_TYPE: |
224 | 0 | opok = sieve_opr_match_type_dump(denv, address); |
225 | 0 | break; |
226 | 0 | default: |
227 | 0 | return (final ? -1 : 1); |
228 | 0 | } |
229 | 0 | } |
230 | 0 | return -1; |
231 | 0 | } |
232 | | |
233 | | int sieve_match_opr_optional_read(const struct sieve_runtime_env *renv, |
234 | | sieve_size_t *address, int *opt_code, |
235 | | int *exec_status, |
236 | | struct sieve_comparator *cmp, |
237 | | struct sieve_match_type *mcht) |
238 | 0 | { |
239 | 0 | int _opt_code = 0; |
240 | 0 | bool final = FALSE; |
241 | 0 | int status = SIEVE_EXEC_OK; |
242 | |
|
243 | 0 | if (opt_code == NULL) { |
244 | 0 | opt_code = &_opt_code; |
245 | 0 | final = TRUE; |
246 | 0 | } |
247 | |
|
248 | 0 | if (exec_status != NULL) |
249 | 0 | *exec_status = SIEVE_EXEC_OK; |
250 | |
|
251 | 0 | while (status == SIEVE_EXEC_OK) { |
252 | 0 | int opt; |
253 | |
|
254 | 0 | if ((opt = sieve_opr_optional_read( |
255 | 0 | renv, address, opt_code)) <= 0) { |
256 | 0 | if (opt < 0 && exec_status != NULL) |
257 | 0 | *exec_status = SIEVE_EXEC_BIN_CORRUPT; |
258 | 0 | return opt; |
259 | 0 | } |
260 | | |
261 | 0 | switch (*opt_code) { |
262 | 0 | case SIEVE_MATCH_OPT_COMPARATOR: |
263 | 0 | if (cmp == NULL) { |
264 | 0 | sieve_runtime_trace_error( |
265 | 0 | renv, "unexpected comparator operand"); |
266 | 0 | if (exec_status != NULL) |
267 | 0 | *exec_status = SIEVE_EXEC_BIN_CORRUPT; |
268 | 0 | return -1; |
269 | 0 | } |
270 | 0 | status = sieve_opr_comparator_read(renv, address, cmp); |
271 | 0 | break; |
272 | 0 | case SIEVE_MATCH_OPT_MATCH_TYPE: |
273 | 0 | if (mcht == NULL) { |
274 | 0 | sieve_runtime_trace_error( |
275 | 0 | renv, "unexpected match-type operand"); |
276 | 0 | if (exec_status != NULL) |
277 | 0 | *exec_status = SIEVE_EXEC_BIN_CORRUPT; |
278 | 0 | return -1; |
279 | 0 | } |
280 | 0 | status = sieve_opr_match_type_read(renv, address, mcht); |
281 | 0 | break; |
282 | 0 | default: |
283 | 0 | if (final) { |
284 | 0 | sieve_runtime_trace_error( |
285 | 0 | renv, "invalid optional operand"); |
286 | 0 | if (exec_status != NULL) |
287 | 0 | *exec_status = SIEVE_EXEC_BIN_CORRUPT; |
288 | 0 | return -1; |
289 | 0 | } |
290 | 0 | return 1; |
291 | 0 | } |
292 | 0 | } |
293 | | |
294 | 0 | if (exec_status != NULL) |
295 | 0 | *exec_status = status; |
296 | 0 | return -1; |
297 | 0 | } |