Coverage Report

Created: 2026-09-03 07:00

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
/* 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