Coverage Report

Created: 2026-07-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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