/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 | | static bool |
249 | | ext_enotify_option_parse(struct sieve_enotify_env *nenv, |
250 | | const char *option, bool name_only, |
251 | | const char **opt_name_r, const char **opt_value_r) |
252 | 0 | { |
253 | 0 | const char *p = option; |
254 | | |
255 | | /* "<optionname>=<value>". |
256 | | |
257 | | l-d = ALPHA / DIGIT |
258 | | l-d-p = l-d / "." / "-" / "_" |
259 | | optionname = l-d *l-d-p |
260 | | value = *(%x01-09 / %x0B-0C / %x0E-FF) |
261 | | */ |
262 | | |
263 | | /* |
264 | | * Parse option name |
265 | | */ |
266 | | |
267 | | /* optionname = l-d *l-d-p |
268 | | */ |
269 | | |
270 | | /* Explicitly report empty option as such */ |
271 | 0 | if (*p == '\0') { |
272 | 0 | sieve_enotify_error(nenv, "empty option specified"); |
273 | 0 | return FALSE; |
274 | 0 | } |
275 | | |
276 | | /* l-d = ALPHA / DIGIT */ |
277 | 0 | if (i_isalnum(*p)) { |
278 | 0 | p++; |
279 | | |
280 | | /* l-d-p = l-d / "." / "-" / "_" */ |
281 | 0 | while (i_isalnum(*p) || *p == '.' || *p == '-' || *p == '_') |
282 | 0 | p++; |
283 | 0 | } |
284 | | |
285 | | /* Parsing must end at '=' and we must parse at least one character */ |
286 | 0 | if (*p != '=' || p == option) { |
287 | 0 | sieve_enotify_error( |
288 | 0 | nenv, "invalid option name specified in option '%s'", |
289 | 0 | str_sanitize(option, 80)); |
290 | 0 | return FALSE; |
291 | 0 | } |
292 | | |
293 | | /* Assign option name */ |
294 | 0 | if (opt_name_r != NULL) |
295 | 0 | *opt_name_r = t_strdup_until(option, p); |
296 | | |
297 | | /* Skip '=' */ |
298 | 0 | p++; |
299 | | |
300 | | /* Exit now if only the option name is of interest */ |
301 | 0 | if (name_only) |
302 | 0 | return TRUE; |
303 | | |
304 | | /* |
305 | | * Parse option value |
306 | | */ |
307 | | |
308 | | /* value = *(%x01-09 / %x0B-0C / %x0E-FF) */ |
309 | 0 | while (*p != '\0' && *p != 0x0A && *p != 0x0D) |
310 | 0 | p++; |
311 | | |
312 | | /* Parse must end at end of string */ |
313 | 0 | if (*p != '\0') { |
314 | 0 | sieve_enotify_error( |
315 | 0 | nenv, "notify command: " |
316 | 0 | "invalid option value specified in option '%s'", |
317 | 0 | str_sanitize(option, 80)); |
318 | 0 | return FALSE; |
319 | 0 | } |
320 | | |
321 | | /* Assign option value */ |
322 | 0 | if (opt_value_r != NULL) |
323 | 0 | *opt_value_r = p; |
324 | |
|
325 | 0 | return TRUE; |
326 | 0 | } |
327 | | |
328 | | struct _ext_enotify_option_check_context { |
329 | | struct sieve_instance *svinst; |
330 | | struct sieve_validator *valdtr; |
331 | | const struct sieve_enotify_method *method; |
332 | | }; |
333 | | |
334 | | static int |
335 | | _ext_enotify_option_check(void *context, struct sieve_ast_argument *arg) |
336 | 0 | { |
337 | 0 | struct _ext_enotify_option_check_context *optn_context = |
338 | 0 | (struct _ext_enotify_option_check_context *) context; |
339 | 0 | struct sieve_validator *valdtr = optn_context->valdtr; |
340 | 0 | const struct sieve_enotify_method *method = optn_context->method; |
341 | 0 | struct sieve_enotify_env nenv; |
342 | 0 | const char *option = sieve_ast_argument_strc(arg); |
343 | 0 | const char *opt_name = NULL, *opt_value = NULL; |
344 | 0 | bool check = TRUE; |
345 | 0 | int result = 1; |
346 | | |
347 | | /* Compose log structure */ |
348 | 0 | i_zero(&nenv); |
349 | 0 | nenv.svinst = optn_context->svinst; |
350 | 0 | nenv.method = method; |
351 | 0 | nenv.ehandler = sieve_validator_error_handler(valdtr); |
352 | 0 | nenv.location = sieve_error_script_location( |
353 | 0 | sieve_validator_script(valdtr), arg->source_line); |
354 | 0 | nenv.event = event_create(nenv.svinst->event); |
355 | 0 | event_set_append_log_prefix(nenv.event, "notify command: "); |
356 | | |
357 | | /* Parse option */ |
358 | 0 | if (!sieve_argument_is_string_literal(arg)) { |
359 | | /* Variable string: partial option parse |
360 | | |
361 | | If the string item is not a string literal, it cannot be |
362 | | validated fully at compile time. We can however check whether |
363 | | the '=' is in the string specification and whether the part |
364 | | before the '=' is a valid option name. In that case, the |
365 | | method option check function is called with the value |
366 | | parameter equal to NULL, meaning that it should only check |
367 | | the validity of the option itself and not the assigned value. |
368 | | */ |
369 | 0 | if (!ext_enotify_option_parse(NULL, option, TRUE, |
370 | 0 | &opt_name, &opt_value)) |
371 | 0 | check = FALSE; |
372 | 0 | } else { |
373 | | /* Literal string: full option parse */ |
374 | 0 | if (!ext_enotify_option_parse(&nenv, option, FALSE, |
375 | 0 | &opt_name, &opt_value)) |
376 | 0 | result = -1; |
377 | 0 | } |
378 | | |
379 | | /* Call method's option check function */ |
380 | 0 | if (result > 0 && check && method->def != NULL && |
381 | 0 | method->def->compile_check_option != NULL) { |
382 | 0 | result = (method->def->compile_check_option(&nenv, opt_name, |
383 | 0 | opt_value) ? |
384 | 0 | 1 : -1); |
385 | 0 | } |
386 | |
|
387 | 0 | event_unref(&nenv.event); |
388 | 0 | return result; |
389 | 0 | } |
390 | | |
391 | | bool ext_enotify_compile_check_arguments(struct sieve_validator *valdtr, |
392 | | struct sieve_command *cmd, |
393 | | struct sieve_ast_argument *uri_arg, |
394 | | struct sieve_ast_argument *msg_arg, |
395 | | struct sieve_ast_argument *from_arg, |
396 | | struct sieve_ast_argument *options_arg) |
397 | 0 | { |
398 | 0 | const struct sieve_extension *this_ext = cmd->ext; |
399 | 0 | struct sieve_instance *svinst = this_ext->svinst; |
400 | 0 | const char *uri = sieve_ast_argument_strc(uri_arg); |
401 | 0 | const char *scheme; |
402 | 0 | const struct sieve_enotify_method *method; |
403 | 0 | struct sieve_enotify_env nenv; |
404 | 0 | bool result = TRUE; |
405 | | |
406 | | /* If the uri string is not a constant literal, we cannot determine |
407 | | which method is used, so we bail out successfully and defer checking |
408 | | to runtime. |
409 | | */ |
410 | 0 | if (!sieve_argument_is_string_literal(uri_arg)) |
411 | 0 | return TRUE; |
412 | | |
413 | | /* Parse scheme part of URI */ |
414 | 0 | if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL) { |
415 | 0 | sieve_argument_validate_error( |
416 | 0 | valdtr, uri_arg, "notify command: " |
417 | 0 | "invalid scheme part for method URI '%s'", |
418 | 0 | str_sanitize(sieve_ast_argument_strc(uri_arg), 80)); |
419 | 0 | return FALSE; |
420 | 0 | } |
421 | | |
422 | | /* Find used method with the parsed scheme identifier */ |
423 | 0 | if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL) { |
424 | 0 | sieve_argument_validate_error( |
425 | 0 | valdtr, uri_arg, "notify command: " |
426 | 0 | "invalid method '%s'", scheme); |
427 | 0 | return FALSE; |
428 | 0 | } |
429 | | |
430 | 0 | if (method->def == NULL) |
431 | 0 | return TRUE; |
432 | | |
433 | | /* Compose log structure */ |
434 | 0 | i_zero(&nenv); |
435 | 0 | nenv.svinst = svinst; |
436 | 0 | nenv.method = method; |
437 | | |
438 | | /* Check URI itself */ |
439 | 0 | if (result && method->def->compile_check_uri != NULL) { |
440 | | /* Set log location to location of URI argument */ |
441 | 0 | nenv.ehandler = sieve_validator_error_handler(valdtr); |
442 | 0 | nenv.location = sieve_error_script_location( |
443 | 0 | sieve_validator_script(valdtr), uri_arg->source_line); |
444 | 0 | nenv.event = event_create(nenv.svinst->event); |
445 | 0 | event_set_append_log_prefix(nenv.event, "notify command: "); |
446 | | |
447 | | /* Execute method check function */ |
448 | 0 | result = method->def->compile_check_uri( |
449 | 0 | &nenv, sieve_ast_argument_strc(uri_arg), uri); |
450 | 0 | } |
451 | | |
452 | | /* Check :message argument */ |
453 | 0 | if (result && msg_arg != NULL && |
454 | 0 | sieve_argument_is_string_literal(msg_arg) && |
455 | 0 | method->def->compile_check_message != NULL) { |
456 | | /* Set log location to location of :message argument */ |
457 | 0 | event_unref(&nenv.event); |
458 | 0 | nenv.ehandler = sieve_validator_error_handler(valdtr); |
459 | 0 | nenv.location = sieve_error_script_location( |
460 | 0 | sieve_validator_script(valdtr), msg_arg->source_line); |
461 | 0 | nenv.event = event_create(nenv.svinst->event); |
462 | 0 | event_set_append_log_prefix(nenv.event, "notify command: "); |
463 | | |
464 | | /* Execute method check function */ |
465 | 0 | result = method->def->compile_check_message( |
466 | 0 | &nenv, sieve_ast_argument_str(msg_arg)); |
467 | 0 | } |
468 | | |
469 | | /* Check :from argument */ |
470 | 0 | if (result && from_arg != NULL && |
471 | 0 | sieve_argument_is_string_literal(from_arg) && |
472 | 0 | method->def->compile_check_from != NULL) { |
473 | | /* Set log location to location of :from argument */ |
474 | 0 | event_unref(&nenv.event); |
475 | 0 | nenv.ehandler = sieve_validator_error_handler(valdtr); |
476 | 0 | nenv.location = sieve_error_script_location( |
477 | 0 | sieve_validator_script(valdtr), from_arg->source_line); |
478 | 0 | nenv.event = event_create(nenv.svinst->event); |
479 | 0 | event_set_append_log_prefix(nenv.event, "notify command: "); |
480 | | |
481 | | /* Execute method check function */ |
482 | 0 | result = method->def->compile_check_from( |
483 | 0 | &nenv, sieve_ast_argument_str(from_arg)); |
484 | 0 | } |
485 | |
|
486 | 0 | event_unref(&nenv.event); |
487 | | |
488 | | /* Check :options argument */ |
489 | 0 | if (result && options_arg != NULL) { |
490 | 0 | struct sieve_ast_argument *option = options_arg; |
491 | 0 | struct _ext_enotify_option_check_context optn_context = { |
492 | 0 | svinst, valdtr, method }; |
493 | | |
494 | | /* Parse and check options */ |
495 | 0 | result = (sieve_ast_stringlist_map( |
496 | 0 | &option, &optn_context, |
497 | 0 | _ext_enotify_option_check) > 0); |
498 | | |
499 | | /* Discard argument if options are not accepted by method */ |
500 | 0 | if (result && method->def->compile_check_option == NULL) { |
501 | 0 | sieve_argument_validate_warning( |
502 | 0 | valdtr, options_arg, "notify command: " |
503 | 0 | "method '%s' accepts no options", scheme); |
504 | 0 | (void)sieve_ast_arguments_detach(options_arg, 1); |
505 | 0 | } |
506 | 0 | } |
507 | 0 | return result; |
508 | 0 | } |
509 | | |
510 | | /* |
511 | | * Runtime operand checking |
512 | | */ |
513 | | |
514 | | bool ext_enotify_runtime_method_validate(const struct sieve_runtime_env *renv, |
515 | | string_t *method_uri) |
516 | 0 | { |
517 | 0 | const struct sieve_execute_env *eenv = renv->exec_env; |
518 | 0 | const struct sieve_extension *this_ext = renv->oprtn->ext; |
519 | 0 | const struct sieve_enotify_method *method; |
520 | 0 | const char *uri = str_c(method_uri); |
521 | 0 | const char *scheme; |
522 | 0 | bool result = TRUE; |
523 | | |
524 | | /* Get the method */ |
525 | |
|
526 | 0 | if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL) |
527 | 0 | return FALSE; |
528 | 0 | if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL) |
529 | 0 | return FALSE; |
530 | | |
531 | | /* Validate the provided URI */ |
532 | | |
533 | 0 | if (method->def != NULL && method->def->runtime_check_uri != NULL) { |
534 | 0 | struct sieve_enotify_env nenv; |
535 | |
|
536 | 0 | i_zero(&nenv); |
537 | 0 | nenv.svinst = eenv->svinst; |
538 | 0 | nenv.method = method; |
539 | 0 | nenv.ehandler = renv->ehandler; |
540 | 0 | nenv.location = sieve_runtime_get_full_command_location(renv), |
541 | 0 | nenv.event = event_create(nenv.svinst->event); |
542 | 0 | event_set_append_log_prefix(nenv.event, |
543 | 0 | "valid_notify_method test: "); |
544 | | |
545 | | /* Use the method check function to validate the URI */ |
546 | 0 | result = method->def->runtime_check_uri( |
547 | 0 | &nenv, str_c(method_uri), uri); |
548 | |
|
549 | 0 | event_unref(&nenv.event); |
550 | 0 | } |
551 | |
|
552 | 0 | return result; |
553 | 0 | } |
554 | | |
555 | | static const struct |
556 | | sieve_enotify_method *ext_enotify_get_method( |
557 | | const struct sieve_runtime_env *renv, string_t *method_uri, |
558 | | const char **uri_body_r) |
559 | 0 | { |
560 | 0 | const struct sieve_extension *this_ext = renv->oprtn->ext; |
561 | 0 | const struct sieve_enotify_method *method; |
562 | 0 | const char *uri = str_c(method_uri); |
563 | 0 | const char *scheme; |
564 | | |
565 | | /* Parse part before ':' of the uri (the scheme) and use it to identify |
566 | | notify method. |
567 | | */ |
568 | 0 | if ((scheme = ext_enotify_uri_scheme_parse(&uri)) == NULL) { |
569 | 0 | sieve_runtime_error( |
570 | 0 | renv, NULL, "invalid scheme part for method URI '%s'", |
571 | 0 | str_sanitize(str_c(method_uri), 80)); |
572 | 0 | return NULL; |
573 | 0 | } |
574 | | |
575 | | /* Find the notify method */ |
576 | 0 | if ((method = ext_enotify_method_find(this_ext, scheme)) == NULL) { |
577 | 0 | sieve_runtime_error(renv, NULL, "invalid notify method '%s'", |
578 | 0 | scheme); |
579 | 0 | return NULL; |
580 | 0 | } |
581 | | |
582 | | /* Return the parse pointer and the found method */ |
583 | 0 | *uri_body_r = uri; |
584 | 0 | return method; |
585 | 0 | } |
586 | | |
587 | | const char * |
588 | | ext_enotify_runtime_get_method_capability(const struct sieve_runtime_env *renv, |
589 | | string_t *method_uri, |
590 | | const char *capability) |
591 | 0 | { |
592 | 0 | const struct sieve_execute_env *eenv = renv->exec_env; |
593 | 0 | const struct sieve_enotify_method *method; |
594 | 0 | const char *uri_body; |
595 | 0 | const char *result = NULL; |
596 | | |
597 | | /* Get method */ |
598 | 0 | method = ext_enotify_get_method(renv, method_uri, &uri_body); |
599 | 0 | if (method == NULL) |
600 | 0 | return NULL; |
601 | | |
602 | | /* Get requested capability */ |
603 | 0 | if (method->def != NULL && |
604 | 0 | method->def->runtime_get_method_capability != NULL) { |
605 | 0 | struct sieve_enotify_env nenv; |
606 | |
|
607 | 0 | i_zero(&nenv); |
608 | 0 | nenv.svinst = eenv->svinst; |
609 | 0 | nenv.method = method; |
610 | 0 | nenv.ehandler = renv->ehandler; |
611 | 0 | nenv.location = sieve_runtime_get_full_command_location(renv), |
612 | 0 | nenv.event = event_create(nenv.svinst->event); |
613 | 0 | event_set_append_log_prefix(nenv.event, |
614 | 0 | "notify_method_capability test: "); |
615 | | |
616 | | /* Execute method function to acquire capability value */ |
617 | 0 | result = method->def->runtime_get_method_capability( |
618 | 0 | &nenv, str_c(method_uri), uri_body, capability); |
619 | |
|
620 | 0 | event_unref(&nenv.event); |
621 | 0 | } |
622 | |
|
623 | 0 | return result; |
624 | 0 | } |
625 | | |
626 | | int ext_enotify_runtime_check_operands( |
627 | | const struct sieve_runtime_env *renv, string_t *method_uri, |
628 | | string_t *message, string_t *from, struct sieve_stringlist *options, |
629 | | const struct sieve_enotify_method **method_r, void **method_context) |
630 | 0 | { |
631 | 0 | const struct sieve_execute_env *eenv = renv->exec_env; |
632 | 0 | const struct sieve_enotify_method *method; |
633 | 0 | const char *uri_body; |
634 | | |
635 | | /* Get method */ |
636 | 0 | method = ext_enotify_get_method(renv, method_uri, &uri_body); |
637 | 0 | if (method == NULL) |
638 | 0 | return SIEVE_EXEC_FAILURE; |
639 | | |
640 | | /* Check provided operands */ |
641 | 0 | if (method->def != NULL && |
642 | 0 | method->def->runtime_check_operands != NULL) { |
643 | 0 | struct sieve_enotify_env nenv; |
644 | 0 | int result = SIEVE_EXEC_OK; |
645 | |
|
646 | 0 | i_zero(&nenv); |
647 | 0 | nenv.svinst = eenv->svinst; |
648 | 0 | nenv.method = method; |
649 | 0 | nenv.ehandler = renv->ehandler; |
650 | 0 | nenv.location = sieve_runtime_get_full_command_location(renv), |
651 | 0 | nenv.event = event_create(nenv.svinst->event); |
652 | 0 | event_set_append_log_prefix(nenv.event, "notify_action: "); |
653 | | |
654 | | /* Execute check function */ |
655 | 0 | if (method->def->runtime_check_operands( |
656 | 0 | &nenv, str_c(method_uri), uri_body, message, from, |
657 | 0 | sieve_result_pool(renv->result), method_context)) { |
658 | | |
659 | | /* Check any provided options */ |
660 | 0 | if (options != NULL) { |
661 | 0 | string_t *option = NULL; |
662 | 0 | int ret; |
663 | | |
664 | | /* Iterate through all provided options */ |
665 | 0 | while ((ret = sieve_stringlist_next_item( |
666 | 0 | options, &option)) > 0) { |
667 | 0 | const char *opt_name = NULL; |
668 | 0 | const char *opt_value = NULL; |
669 | | |
670 | | /* Parse option into <optionname> and |
671 | | <value> */ |
672 | 0 | if (ext_enotify_option_parse( |
673 | 0 | &nenv, str_c(option), FALSE, |
674 | 0 | &opt_name, &opt_value)) { |
675 | | |
676 | | /* Set option */ |
677 | 0 | if (method->def->runtime_set_option != NULL) { |
678 | 0 | (void)method->def->runtime_set_option( |
679 | 0 | &nenv, *method_context, |
680 | 0 | opt_name, opt_value); |
681 | 0 | } |
682 | 0 | } |
683 | 0 | } |
684 | | |
685 | | /* Check for binary corruptions encountered |
686 | | during string list iteration */ |
687 | 0 | if (ret >= 0) { |
688 | 0 | *method_r = method; |
689 | 0 | } else { |
690 | | /* Binary corrupt */ |
691 | 0 | sieve_runtime_trace_error( |
692 | 0 | renv, "invalid item in options string list"); |
693 | 0 | result = SIEVE_EXEC_BIN_CORRUPT; |
694 | 0 | } |
695 | |
|
696 | 0 | } else { |
697 | | /* No options */ |
698 | 0 | *method_r = method; |
699 | 0 | } |
700 | |
|
701 | 0 | } else { |
702 | | /* Operand check failed */ |
703 | 0 | result = SIEVE_EXEC_FAILURE; |
704 | 0 | } |
705 | |
|
706 | 0 | event_unref(&nenv.event); |
707 | 0 | return result; |
708 | 0 | } |
709 | | |
710 | | /* No check function defined: a most unlikely situation */ |
711 | 0 | *method_context = NULL; |
712 | 0 | *method_r = method; |
713 | 0 | return SIEVE_EXEC_OK; |
714 | 0 | } |
715 | | |
716 | | /* |
717 | | * Notify method printing |
718 | | */ |
719 | | |
720 | | void sieve_enotify_method_printf(const struct sieve_enotify_print_env *penv, |
721 | | const char *fmt, ...) |
722 | 0 | { |
723 | 0 | va_list args; |
724 | |
|
725 | 0 | va_start(args, fmt); |
726 | 0 | sieve_result_vprintf(penv->result_penv, fmt, args); |
727 | 0 | va_end(args); |
728 | 0 | } |
729 | | |
730 | | /* |
731 | | * Action execution |
732 | | */ |
733 | | |
734 | | struct event_passthrough * |
735 | | sieve_enotify_create_finish_event(const struct sieve_enotify_exec_env *nenv) |
736 | 0 | { |
737 | 0 | struct event_passthrough *e = |
738 | 0 | event_create_passthrough(nenv->event)-> |
739 | 0 | set_name("sieve_action_finished"); |
740 | |
|
741 | 0 | return e; |
742 | 0 | } |
743 | | |