/src/pigeonhole/src/lib-sieve/plugins/enotify/ext-enotify-common.c
Line | Count | Source |
1 | | /* Copyright (c) Pigeonhole authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "str.h" |
5 | | #include "str-sanitize.h" |
6 | | #include "array.h" |
7 | | |
8 | | #include "sieve-common.h" |
9 | | #include "sieve-ast.h" |
10 | | #include "sieve-stringlist.h" |
11 | | #include "sieve-code.h" |
12 | | #include "sieve-commands.h" |
13 | | #include "sieve-validator.h" |
14 | | #include "sieve-interpreter.h" |
15 | | #include "sieve-result.h" |
16 | | |
17 | | #include "ext-enotify-limits.h" |
18 | | #include "ext-enotify-common.h" |
19 | | |
20 | | #include <ctype.h> |
21 | | |
22 | | /* FIXME: (from draft RFC) |
23 | | |
24 | | Header/envelope tests [Sieve] together with Sieve variables can be used to |
25 | | extract the list of users to receive notifications from the incoming email |
26 | | message or its envelope. This is potentially quite dangerous, as this can be |
27 | | used for Deny Of Service attacks on recipients controlled by the message |
28 | | sender. For this reason implementations SHOULD NOT allow use of variables |
29 | | containing values extracted from the email message in the method parameter to |
30 | | the notify action. Note that violation of this SHOULD NOT may result in* the |
31 | | creation of an open relay, i.e. any sender would be able to create specially |
32 | | crafted email messages that would result in notifications delivered to |
33 | | recipients under the control of the sender. In worst case this might result |
34 | | in financial loss by user controlling the Sieve script and/or by recipients |
35 | | of notifications (e.g. if a notification is an SMS message). |
36 | | |
37 | | --> This is currently not possible to check. |
38 | | */ |
39 | | |
40 | | /* |
41 | | * Notify capability |
42 | | */ |
43 | | |
44 | | static const char * |
45 | | ext_notify_get_methods_string(const struct sieve_extension *ntfy_ext); |
46 | | |
47 | | const struct sieve_extension_capabilities notify_capabilities = { |
48 | | "notify", |
49 | | ext_notify_get_methods_string, |
50 | | }; |
51 | | |
52 | | /* |
53 | | * Core notification methods |
54 | | */ |
55 | | |
56 | | extern const struct sieve_enotify_method_def mailto_notify; |
57 | | |
58 | | |
59 | | /* |
60 | | * Enotify extension |
61 | | */ |
62 | | |
63 | | int sieve_ext_enotify_get_extension(struct sieve_instance *svinst, |
64 | | const struct sieve_extension **ext_r) |
65 | 0 | { |
66 | 0 | return sieve_extension_register(svinst, &enotify_extension, FALSE, |
67 | 0 | ext_r); |
68 | 0 | } |
69 | | |
70 | | int sieve_ext_enotify_require_extension(struct sieve_instance *svinst, |
71 | | const struct sieve_extension **ext_r) |
72 | 0 | { |
73 | 0 | return sieve_extension_require(svinst, &enotify_extension, TRUE, |
74 | 0 | ext_r); |
75 | 0 | } |
76 | | |
77 | | /* |
78 | | * Notify method registry |
79 | | */ |
80 | | |
81 | | static int |
82 | | ext_enotify_method_register(struct ext_enotify_context *extctx, |
83 | | const struct sieve_extension *ntfy_ext, |
84 | | const struct sieve_enotify_method_def *nmth_def, |
85 | | const struct sieve_enotify_method **nmth_r) |
86 | 0 | { |
87 | 0 | struct sieve_enotify_method *nmth; |
88 | 0 | int nmth_id = (int)array_count(&extctx->notify_methods); |
89 | |
|
90 | 0 | nmth = array_append_space(&extctx->notify_methods); |
91 | 0 | nmth->def = nmth_def; |
92 | 0 | nmth->id = nmth_id; |
93 | 0 | nmth->svinst = ntfy_ext->svinst; |
94 | 0 | nmth->ext = ntfy_ext; |
95 | |
|
96 | 0 | if (nmth_def->load != NULL && |
97 | 0 | nmth_def->load(nmth, &nmth->context) < 0) { |
98 | 0 | array_pop_back(&extctx->notify_methods); |
99 | 0 | return -1; |
100 | 0 | } |
101 | | |
102 | 0 | *nmth_r = nmth; |
103 | 0 | return 0; |
104 | 0 | } |
105 | | |
106 | | int ext_enotify_methods_init(struct ext_enotify_context *extctx, |
107 | | const struct sieve_extension *ntfy_ext) |
108 | 0 | { |
109 | 0 | const struct sieve_enotify_method *nmth; |
110 | |
|
111 | 0 | p_array_init(&extctx->notify_methods, default_pool, 4); |
112 | |
|
113 | 0 | if (ext_enotify_method_register(extctx, ntfy_ext, |
114 | 0 | &mailto_notify, &nmth) < 0) { |
115 | 0 | ext_enotify_methods_deinit(extctx); |
116 | 0 | return -1; |
117 | 0 | } |
118 | 0 | return 0; |
119 | 0 | } |
120 | | |
121 | | void ext_enotify_methods_deinit(struct ext_enotify_context *extctx) |
122 | 0 | { |
123 | 0 | const struct sieve_enotify_method *methods; |
124 | 0 | unsigned int meth_count, i; |
125 | |
|
126 | 0 | methods = array_get(&extctx->notify_methods, &meth_count); |
127 | 0 | for (i = 0; i < meth_count; i++) { |
128 | 0 | if (methods[i].def != NULL && methods[i].def->unload != NULL) |
129 | 0 | methods[i].def->unload(&methods[i]); |
130 | 0 | } |
131 | |
|
132 | 0 | array_free(&extctx->notify_methods); |
133 | 0 | } |
134 | | |
135 | | int sieve_enotify_method_register( |
136 | | const struct sieve_extension *ntfy_ext, |
137 | | const struct sieve_enotify_method_def *nmth_def, |
138 | | const struct sieve_enotify_method **nmth_r) |
139 | 0 | { |
140 | 0 | i_assert(ntfy_ext != NULL); |
141 | 0 | i_assert(ntfy_ext->def == &enotify_extension); |
142 | | |
143 | 0 | struct ext_enotify_context *extctx = ntfy_ext->context; |
144 | |
|
145 | 0 | return ext_enotify_method_register(extctx, ntfy_ext, nmth_def, nmth_r); |
146 | 0 | } |
147 | | |
148 | | void sieve_enotify_method_unregister(const struct sieve_enotify_method *nmth) |
149 | 0 | { |
150 | 0 | const struct sieve_extension *ntfy_ext = nmth->ext; |
151 | |
|
152 | 0 | i_assert(ntfy_ext != NULL); |
153 | 0 | i_assert(ntfy_ext->def == &enotify_extension); |
154 | | |
155 | 0 | struct ext_enotify_context *extctx = ntfy_ext->context; |
156 | 0 | int nmth_id = nmth->id; |
157 | |
|
158 | 0 | if (nmth_id >= 0 && |
159 | 0 | nmth_id < (int)array_count(&extctx->notify_methods)) { |
160 | 0 | struct sieve_enotify_method *nmth_mod = |
161 | 0 | array_idx_modifiable(&extctx->notify_methods, |
162 | 0 | nmth_id); |
163 | |
|
164 | 0 | nmth_mod->def = NULL; |
165 | 0 | } |
166 | 0 | } |
167 | | |
168 | | const struct sieve_enotify_method * |
169 | | ext_enotify_method_find(const struct sieve_extension *ntfy_ext, |
170 | | const char *identifier) |
171 | 0 | { |
172 | 0 | struct ext_enotify_context *extctx = ntfy_ext->context; |
173 | 0 | unsigned int meth_count, i; |
174 | 0 | const struct sieve_enotify_method *methods; |
175 | |
|
176 | 0 | methods = array_get(&extctx->notify_methods, &meth_count); |
177 | 0 | for (i = 0; i < meth_count; i++) { |
178 | 0 | if (methods[i].def == NULL) |
179 | 0 | continue; |
180 | | |
181 | 0 | if (strcasecmp(methods[i].def->identifier, identifier) == 0) |
182 | 0 | return &methods[i]; |
183 | 0 | } |
184 | 0 | return NULL; |
185 | 0 | } |
186 | | |
187 | | static const char * |
188 | | ext_notify_get_methods_string(const struct sieve_extension *ntfy_ext) |
189 | 0 | { |
190 | 0 | struct ext_enotify_context *extctx = ntfy_ext->context; |
191 | 0 | unsigned int meth_count, i; |
192 | 0 | const struct sieve_enotify_method *methods; |
193 | 0 | string_t *result = t_str_new(128); |
194 | |
|
195 | 0 | methods = array_get(&extctx->notify_methods, &meth_count); |
196 | 0 | if (meth_count > 0) { |
197 | 0 | for (i = 0; i < meth_count; i++) { |
198 | 0 | if (str_len(result) > 0) |
199 | 0 | str_append_c(result, ' '); |
200 | 0 | if (methods[i].def != NULL) |
201 | 0 | str_append(result, methods[i].def->identifier); |
202 | 0 | } |
203 | 0 | return str_c(result); |
204 | 0 | } |
205 | 0 | return NULL; |
206 | 0 | } |
207 | | |
208 | | /* |
209 | | * Compile-time argument validation |
210 | | */ |
211 | | |
212 | | static const char *ext_enotify_uri_scheme_parse(const char **uri_p) |
213 | 0 | { |
214 | 0 | string_t *scheme = t_str_new(EXT_ENOTIFY_MAX_SCHEME_LEN); |
215 | 0 | const char *p = *uri_p; |
216 | 0 | unsigned int len = 0; |
217 | | |
218 | | /* RFC 3968: |
219 | | |
220 | | scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) |
221 | | |
222 | | FIXME: we do not allow '%' in schemes. Is this correct? |
223 | | */ |
224 | |
|
225 | 0 | if (!i_isalpha(*p)) |
226 | 0 | return NULL; |
227 | | |
228 | 0 | str_append_c(scheme, *p); |
229 | 0 | p++; |
230 | |
|
231 | 0 | while (*p != '\0' && len < EXT_ENOTIFY_MAX_SCHEME_LEN) { |
232 | 0 | if (!i_isalnum(*p) && *p != '+' && *p != '-' && *p != '.') |
233 | 0 | break; |
234 | | |
235 | 0 | str_append_c(scheme, *p); |
236 | 0 | p++; |
237 | 0 | len++; |
238 | 0 | } |
239 | |
|
240 | 0 | if (*p != ':') |
241 | 0 | return NULL; |
242 | 0 | p++; |
243 | |
|
244 | 0 | *uri_p = p; |
245 | 0 | return str_c(scheme); |
246 | 0 | } |
247 | | |
248 | | /* Parse the option string into its name and value parts. The nenv parameter |
249 | | can be NULL, in which case no errors are reported. |
250 | | */ |
251 | | static bool |
252 | | ext_enotify_option_parse(struct sieve_enotify_env *nenv, |
253 | | const char *option, bool name_only, |
254 | | const char **opt_name_r, const char **opt_value_r) |
255 | 0 | { |
256 | 0 | const char *p = option; |
257 | | |
258 | | /* "<optionname>=<value>". |
259 | | |
260 | | l-d = ALPHA / DIGIT |
261 | | l-d-p = l-d / "." / "-" / "_" |
262 | | optionname = l-d *l-d-p |
263 | | value = *(%x01-09 / %x0B-0C / %x0E-FF) |
264 | | */ |
265 | | |
266 | | /* |
267 | | * Parse option name |
268 | | */ |
269 | | |
270 | | /* optionname = l-d *l-d-p |
271 | | */ |
272 | | |
273 | | /* Explicitly report empty option as such */ |
274 | 0 | if (*p == '\0') { |
275 | 0 | if (nenv != NULL) |
276 | 0 | sieve_enotify_error(nenv, "empty option specified"); |
277 | 0 | return FALSE; |
278 | 0 | } |
279 | | |
280 | | /* l-d = ALPHA / DIGIT */ |
281 | 0 | if (i_isalnum(*p)) { |
282 | 0 | p++; |
283 | | |
284 | | /* l-d-p = l-d / "." / "-" / "_" */ |
285 | 0 | while (i_isalnum(*p) || *p == '.' || *p == '-' || *p == '_') |
286 | 0 | p++; |
287 | 0 | } |
288 | | |
289 | | /* Parsing must end at '=' and we must parse at least one character */ |
290 | 0 | if (*p != '=' || p == option) { |
291 | 0 | if (nenv != NULL) { |
292 | 0 | sieve_enotify_error( |
293 | 0 | nenv, |
294 | 0 | "invalid option name specified in option '%s'", |
295 | 0 | str_sanitize(option, 80)); |
296 | 0 | } |
297 | 0 | return FALSE; |
298 | 0 | } |
299 | | |
300 | | /* Assign option name */ |
301 | 0 | if (opt_name_r != NULL) |
302 | 0 | *opt_name_r = t_strdup_until(option, p); |
303 | | |
304 | | /* Skip '=' */ |
305 | 0 | p++; |
306 | | |
307 | | /* Exit now if only the option name is of interest */ |
308 | 0 | if (name_only) |
309 | 0 | return TRUE; |
310 | | |
311 | | /* |
312 | | * Parse option value |
313 | | */ |
314 | | |
315 | | /* value = *(%x01-09 / %x0B-0C / %x0E-FF) */ |
316 | 0 | while (*p != '\0' && *p != 0x0A && *p != 0x0D) |
317 | 0 | p++; |
318 | | |
319 | | /* Parse must end at end of string */ |
320 | 0 | if (*p != '\0') { |
321 | 0 | if (nenv != NULL) { |
322 | 0 | sieve_enotify_error( |
323 | 0 | nenv, "notify command: " |
324 | 0 | "invalid option value specified in option '%s'", |
325 | 0 | str_sanitize(option, 80)); |
326 | 0 | } |
327 | 0 | return FALSE; |
328 | 0 | } |
329 | | |
330 | | /* Assign option value */ |
331 | 0 | if (opt_value_r != NULL) |
332 | 0 | *opt_value_r = p; |
333 | |
|
334 | 0 | return TRUE; |
335 | 0 | } |
336 | | |
337 | | struct _ext_enotify_option_check_context { |
338 | | struct sieve_instance *svinst; |
339 | | struct sieve_validator *valdtr; |
340 | | const struct sieve_enotify_method *method; |
341 | | }; |
342 | | |
343 | | static int |
344 | | _ext_enotify_option_check(void *context, struct sieve_ast_argument *arg) |
345 | 0 | { |
346 | 0 | struct _ext_enotify_option_check_context *optn_context = |
347 | 0 | (struct _ext_enotify_option_check_context *) context; |
348 | 0 | struct sieve_validator *valdtr = optn_context->valdtr; |
349 | 0 | const struct sieve_enotify_method *method = optn_context->method; |
350 | 0 | struct sieve_enotify_env nenv; |
351 | 0 | const char *option = sieve_ast_argument_strc(arg); |
352 | 0 | const char *opt_name = NULL, *opt_value = NULL; |
353 | 0 | bool check = TRUE; |
354 | 0 | int result = 1; |
355 | | |
356 | | /* Compose log structure */ |
357 | 0 | i_zero(&nenv); |
358 | 0 | nenv.svinst = optn_context->svinst; |
359 | 0 | nenv.method = method; |
360 | 0 | nenv.ehandler = sieve_validator_error_handler(valdtr); |
361 | 0 | nenv.location = sieve_error_script_location( |
362 | 0 | sieve_validator_script(valdtr), arg->source_line); |
363 | 0 | nenv.event = event_create(nenv.svinst->event); |
364 | 0 | event_set_append_log_prefix(nenv.event, "notify command: "); |
365 | | |
366 | | /* Parse option */ |
367 | 0 | if (!sieve_argument_is_string_literal(arg)) { |
368 | | /* Variable string: partial option parse |
369 | | |
370 | | If the string item is not a string literal, it cannot be |
371 | | validated fully at compile time. We can however check whether |
372 | | the '=' is in the string specification and whether the part |
373 | | before the '=' is a valid option name. In that case, the |
374 | | method option check function is called with the value |
375 | | parameter equal to NULL, meaning that it should only check |
376 | | the validity of the option itself and not the assigned value. |
377 | | */ |
378 | 0 | if (!ext_enotify_option_parse(NULL, option, TRUE, |
379 | 0 | &opt_name, &opt_value)) |
380 | 0 | check = FALSE; |
381 | 0 | } else { |
382 | | /* Literal string: full option parse */ |
383 | 0 | if (!ext_enotify_option_parse(&nenv, option, FALSE, |
384 | 0 | &opt_name, &opt_value)) |
385 | 0 | result = -1; |
386 | 0 | } |
387 | | |
388 | | /* Call method's option check function */ |
389 | 0 | if (result > 0 && check && method->def != NULL && |
390 | 0 | method->def->compile_check_option != NULL) { |
391 | 0 | result = (method->def->compile_check_option(&nenv, opt_name, |
392 | 0 | opt_value) ? |
393 | 0 | 1 : -1); |
394 | 0 | } |
395 | |
|
396 | 0 | event_unref(&nenv.event); |
397 | 0 | return result; |
398 | 0 | } |
399 | | |
400 | | bool ext_enotify_compile_check_arguments(struct sieve_validator *valdtr, |
401 | | struct sieve_command *cmd, |
402 | | struct sieve_ast_argument *uri_arg, |
403 | | struct sieve_ast_argument *msg_arg, |
404 | | struct sieve_ast_argument *from_arg, |
405 | | struct sieve_ast_argument *options_arg) |
406 | 0 | { |
407 | 0 | const struct sieve_extension *this_ext = cmd->ext; |
408 | 0 | struct sieve_instance *svinst = this_ext->svinst; |
409 | 0 | const char *uri = sieve_ast_argument_strc(uri_arg); |
410 | 0 | const char *scheme; |
411 | 0 | const struct sieve_enotify_method *method; |
412 | 0 | struct sieve_enotify_env nenv; |
413 | 0 | bool result = TRUE; |
414 | | |
415 | | /* If the uri string is not a constant literal, we cannot determine |
416 | | which method is used, so we bail out successfully and defer checking |
417 | | to runtime. |
418 | | */ |
419 | 0 | if (!sieve_argument_is_string_literal(uri_arg)) |
420 | 0 | return TRUE; |
421 | | |
422 | | /* Parse scheme part of URI */ |
423 | 0 | if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL) { |
424 | 0 | sieve_argument_validate_error( |
425 | 0 | valdtr, uri_arg, "notify command: " |
426 | 0 | "invalid scheme part for method URI '%s'", |
427 | 0 | str_sanitize(sieve_ast_argument_strc(uri_arg), 80)); |
428 | 0 | return FALSE; |
429 | 0 | } |
430 | | |
431 | | /* Find used method with the parsed scheme identifier */ |
432 | 0 | if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL) { |
433 | 0 | sieve_argument_validate_error( |
434 | 0 | valdtr, uri_arg, "notify command: " |
435 | 0 | "invalid method '%s'", scheme); |
436 | 0 | return FALSE; |
437 | 0 | } |
438 | | |
439 | 0 | if (method->def == NULL) |
440 | 0 | return TRUE; |
441 | | |
442 | | /* Compose log structure */ |
443 | 0 | i_zero(&nenv); |
444 | 0 | nenv.svinst = svinst; |
445 | 0 | nenv.method = method; |
446 | | |
447 | | /* Check URI itself */ |
448 | 0 | if (result && method->def->compile_check_uri != NULL) { |
449 | | /* Set log location to location of URI argument */ |
450 | 0 | nenv.ehandler = sieve_validator_error_handler(valdtr); |
451 | 0 | nenv.location = sieve_error_script_location( |
452 | 0 | sieve_validator_script(valdtr), uri_arg->source_line); |
453 | 0 | nenv.event = event_create(nenv.svinst->event); |
454 | 0 | event_set_append_log_prefix(nenv.event, "notify command: "); |
455 | | |
456 | | /* Execute method check function */ |
457 | 0 | result = method->def->compile_check_uri( |
458 | 0 | &nenv, sieve_ast_argument_strc(uri_arg), uri); |
459 | 0 | } |
460 | | |
461 | | /* Check :message argument */ |
462 | 0 | if (result && msg_arg != NULL && |
463 | 0 | sieve_argument_is_string_literal(msg_arg) && |
464 | 0 | method->def->compile_check_message != NULL) { |
465 | | /* Set log location to location of :message argument */ |
466 | 0 | event_unref(&nenv.event); |
467 | 0 | nenv.ehandler = sieve_validator_error_handler(valdtr); |
468 | 0 | nenv.location = sieve_error_script_location( |
469 | 0 | sieve_validator_script(valdtr), msg_arg->source_line); |
470 | 0 | nenv.event = event_create(nenv.svinst->event); |
471 | 0 | event_set_append_log_prefix(nenv.event, "notify command: "); |
472 | | |
473 | | /* Execute method check function */ |
474 | 0 | result = method->def->compile_check_message( |
475 | 0 | &nenv, sieve_ast_argument_str(msg_arg)); |
476 | 0 | } |
477 | | |
478 | | /* Check :from argument */ |
479 | 0 | if (result && from_arg != NULL && |
480 | 0 | sieve_argument_is_string_literal(from_arg) && |
481 | 0 | method->def->compile_check_from != NULL) { |
482 | | /* Set log location to location of :from argument */ |
483 | 0 | event_unref(&nenv.event); |
484 | 0 | nenv.ehandler = sieve_validator_error_handler(valdtr); |
485 | 0 | nenv.location = sieve_error_script_location( |
486 | 0 | sieve_validator_script(valdtr), from_arg->source_line); |
487 | 0 | nenv.event = event_create(nenv.svinst->event); |
488 | 0 | event_set_append_log_prefix(nenv.event, "notify command: "); |
489 | | |
490 | | /* Execute method check function */ |
491 | 0 | result = method->def->compile_check_from( |
492 | 0 | &nenv, sieve_ast_argument_str(from_arg)); |
493 | 0 | } |
494 | |
|
495 | 0 | event_unref(&nenv.event); |
496 | | |
497 | | /* Check :options argument */ |
498 | 0 | if (result && options_arg != NULL) { |
499 | 0 | struct sieve_ast_argument *option = options_arg; |
500 | 0 | struct _ext_enotify_option_check_context optn_context = { |
501 | 0 | svinst, valdtr, method }; |
502 | | |
503 | | /* Parse and check options */ |
504 | 0 | result = (sieve_ast_stringlist_map( |
505 | 0 | &option, &optn_context, |
506 | 0 | _ext_enotify_option_check) > 0); |
507 | | |
508 | | /* Discard argument if options are not accepted by method */ |
509 | 0 | if (result && method->def->compile_check_option == NULL) { |
510 | 0 | sieve_argument_validate_warning( |
511 | 0 | valdtr, options_arg, "notify command: " |
512 | 0 | "method '%s' accepts no options", scheme); |
513 | 0 | (void)sieve_ast_arguments_detach(options_arg, 1); |
514 | 0 | } |
515 | 0 | } |
516 | 0 | return result; |
517 | 0 | } |
518 | | |
519 | | /* |
520 | | * Runtime operand checking |
521 | | */ |
522 | | |
523 | | bool ext_enotify_runtime_method_validate(const struct sieve_runtime_env *renv, |
524 | | string_t *method_uri) |
525 | 0 | { |
526 | 0 | const struct sieve_execute_env *eenv = renv->exec_env; |
527 | 0 | const struct sieve_extension *this_ext = renv->oprtn->ext; |
528 | 0 | const struct sieve_enotify_method *method; |
529 | 0 | const char *uri = str_c(method_uri); |
530 | 0 | const char *scheme; |
531 | 0 | bool result = TRUE; |
532 | | |
533 | | /* Get the method */ |
534 | |
|
535 | 0 | if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL) |
536 | 0 | return FALSE; |
537 | 0 | if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL) |
538 | 0 | return FALSE; |
539 | | |
540 | | /* Validate the provided URI */ |
541 | | |
542 | 0 | if (method->def != NULL && method->def->runtime_check_uri != NULL) { |
543 | 0 | struct sieve_enotify_env nenv; |
544 | |
|
545 | 0 | i_zero(&nenv); |
546 | 0 | nenv.svinst = eenv->svinst; |
547 | 0 | nenv.method = method; |
548 | 0 | nenv.ehandler = renv->ehandler; |
549 | 0 | nenv.location = sieve_runtime_get_full_command_location(renv), |
550 | 0 | nenv.event = event_create(nenv.svinst->event); |
551 | 0 | event_set_append_log_prefix(nenv.event, |
552 | 0 | "valid_notify_method test: "); |
553 | | |
554 | | /* Use the method check function to validate the URI */ |
555 | 0 | result = method->def->runtime_check_uri( |
556 | 0 | &nenv, str_c(method_uri), uri); |
557 | |
|
558 | 0 | event_unref(&nenv.event); |
559 | 0 | } |
560 | |
|
561 | 0 | return result; |
562 | 0 | } |
563 | | |
564 | | static const struct |
565 | | sieve_enotify_method *ext_enotify_get_method( |
566 | | const struct sieve_runtime_env *renv, string_t *method_uri, |
567 | | const char **uri_body_r) |
568 | 0 | { |
569 | 0 | const struct sieve_extension *this_ext = renv->oprtn->ext; |
570 | 0 | const struct sieve_enotify_method *method; |
571 | 0 | const char *uri = str_c(method_uri); |
572 | 0 | const char *scheme; |
573 | | |
574 | | /* Parse part before ':' of the uri (the scheme) and use it to identify |
575 | | notify method. |
576 | | */ |
577 | 0 | if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL) { |
578 | 0 | sieve_runtime_error( |
579 | 0 | renv, NULL, "invalid scheme part for method URI '%s'", |
580 | 0 | str_sanitize(str_c(method_uri), 80)); |
581 | 0 | return NULL; |
582 | 0 | } |
583 | | |
584 | | /* Find the notify method */ |
585 | 0 | if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL) { |
586 | 0 | sieve_runtime_error(renv, NULL, "invalid notify method '%s'", |
587 | 0 | scheme); |
588 | 0 | return NULL; |
589 | 0 | } |
590 | | |
591 | | /* Return the parse pointer and the found method */ |
592 | 0 | *uri_body_r = uri; |
593 | 0 | return method; |
594 | 0 | } |
595 | | |
596 | | const char * |
597 | | ext_enotify_runtime_get_method_capability(const struct sieve_runtime_env *renv, |
598 | | string_t *method_uri, |
599 | | const char *capability) |
600 | 0 | { |
601 | 0 | const struct sieve_execute_env *eenv = renv->exec_env; |
602 | 0 | const struct sieve_enotify_method *method; |
603 | 0 | const char *uri_body; |
604 | 0 | const char *result = NULL; |
605 | | |
606 | | /* Get method */ |
607 | 0 | method = ext_enotify_get_method(renv, method_uri, &uri_body); |
608 | 0 | if (method == NULL) |
609 | 0 | return NULL; |
610 | | |
611 | | /* Get requested capability */ |
612 | 0 | if (method->def != NULL && |
613 | 0 | method->def->runtime_get_method_capability != NULL) { |
614 | 0 | struct sieve_enotify_env nenv; |
615 | |
|
616 | 0 | i_zero(&nenv); |
617 | 0 | nenv.svinst = eenv->svinst; |
618 | 0 | nenv.method = method; |
619 | 0 | nenv.ehandler = renv->ehandler; |
620 | 0 | nenv.location = sieve_runtime_get_full_command_location(renv), |
621 | 0 | nenv.event = event_create(nenv.svinst->event); |
622 | 0 | event_set_append_log_prefix(nenv.event, |
623 | 0 | "notify_method_capability test: "); |
624 | | |
625 | | /* Execute method function to acquire capability value */ |
626 | 0 | result = method->def->runtime_get_method_capability( |
627 | 0 | &nenv, str_c(method_uri), uri_body, capability); |
628 | |
|
629 | 0 | event_unref(&nenv.event); |
630 | 0 | } |
631 | |
|
632 | 0 | return result; |
633 | 0 | } |
634 | | |
635 | | int ext_enotify_runtime_check_operands( |
636 | | const struct sieve_runtime_env *renv, string_t *method_uri, |
637 | | string_t *message, string_t *from, struct sieve_stringlist *options, |
638 | | const struct sieve_enotify_method **method_r, void **method_context) |
639 | 0 | { |
640 | 0 | const struct sieve_execute_env *eenv = renv->exec_env; |
641 | 0 | const struct sieve_enotify_method *method; |
642 | 0 | const char *uri_body; |
643 | | |
644 | | /* Get method */ |
645 | 0 | method = ext_enotify_get_method(renv, method_uri, &uri_body); |
646 | 0 | if (method == NULL) |
647 | 0 | return SIEVE_EXEC_FAILURE; |
648 | | |
649 | | /* Check provided operands */ |
650 | 0 | if (method->def != NULL && |
651 | 0 | method->def->runtime_check_operands != NULL) { |
652 | 0 | struct sieve_enotify_env nenv; |
653 | 0 | int result = SIEVE_EXEC_OK; |
654 | |
|
655 | 0 | i_zero(&nenv); |
656 | 0 | nenv.svinst = eenv->svinst; |
657 | 0 | nenv.method = method; |
658 | 0 | nenv.ehandler = renv->ehandler; |
659 | 0 | nenv.location = sieve_runtime_get_full_command_location(renv), |
660 | 0 | nenv.event = event_create(nenv.svinst->event); |
661 | 0 | event_set_append_log_prefix(nenv.event, "notify_action: "); |
662 | | |
663 | | /* Execute check function */ |
664 | 0 | if (method->def->runtime_check_operands( |
665 | 0 | &nenv, str_c(method_uri), uri_body, message, from, |
666 | 0 | sieve_result_pool(renv->result), method_context)) { |
667 | | |
668 | | /* Check any provided options */ |
669 | 0 | if (options != NULL) { |
670 | 0 | string_t *option = NULL; |
671 | 0 | int ret; |
672 | | |
673 | | /* Iterate through all provided options */ |
674 | 0 | while ((ret = sieve_stringlist_next_item( |
675 | 0 | options, &option)) > 0) { |
676 | 0 | const char *opt_name = NULL; |
677 | 0 | const char *opt_value = NULL; |
678 | | |
679 | | /* Parse option into <optionname> and |
680 | | <value> */ |
681 | 0 | if (ext_enotify_option_parse( |
682 | 0 | &nenv, str_c(option), FALSE, |
683 | 0 | &opt_name, &opt_value)) { |
684 | | |
685 | | /* Set option */ |
686 | 0 | if (method->def->runtime_set_option != NULL) { |
687 | 0 | (void)method->def->runtime_set_option( |
688 | 0 | &nenv, *method_context, |
689 | 0 | opt_name, opt_value); |
690 | 0 | } |
691 | 0 | } |
692 | 0 | } |
693 | | |
694 | | /* Check for binary corruptions encountered |
695 | | during string list iteration */ |
696 | 0 | if (ret >= 0) { |
697 | 0 | *method_r = method; |
698 | 0 | } else { |
699 | | /* Binary corrupt */ |
700 | 0 | sieve_runtime_trace_error( |
701 | 0 | renv, "invalid item in options string list"); |
702 | 0 | result = SIEVE_EXEC_BIN_CORRUPT; |
703 | 0 | } |
704 | |
|
705 | 0 | } else { |
706 | | /* No options */ |
707 | 0 | *method_r = method; |
708 | 0 | } |
709 | |
|
710 | 0 | } else { |
711 | | /* Operand check failed */ |
712 | 0 | result = SIEVE_EXEC_FAILURE; |
713 | 0 | } |
714 | |
|
715 | 0 | event_unref(&nenv.event); |
716 | 0 | return result; |
717 | 0 | } |
718 | | |
719 | | /* No check function defined: a most unlikely situation */ |
720 | 0 | *method_context = NULL; |
721 | 0 | *method_r = method; |
722 | 0 | return SIEVE_EXEC_OK; |
723 | 0 | } |
724 | | |
725 | | /* |
726 | | * Notify method printing |
727 | | */ |
728 | | |
729 | | void sieve_enotify_method_printf(const struct sieve_enotify_print_env *penv, |
730 | | const char *fmt, ...) |
731 | 0 | { |
732 | 0 | va_list args; |
733 | |
|
734 | 0 | va_start(args, fmt); |
735 | 0 | sieve_result_vprintf(penv->result_penv, fmt, args); |
736 | 0 | va_end(args); |
737 | 0 | } |
738 | | |
739 | | /* |
740 | | * Action execution |
741 | | */ |
742 | | |
743 | | struct event_passthrough * |
744 | | sieve_enotify_create_finish_event(const struct sieve_enotify_exec_env *nenv) |
745 | 0 | { |
746 | 0 | struct event_passthrough *e = |
747 | 0 | event_create_passthrough(nenv->event)-> |
748 | 0 | set_name("sieve_action_finished"); |
749 | |
|
750 | 0 | return e; |
751 | 0 | } |
752 | | |