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/ext-envelope.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
/* Extension envelope
4
 * ------------------
5
 *
6
 * Authors: Stephan Bosch
7
 * Specification: RFC 5228
8
 * Implementation: full
9
 * Status: testing
10
 *
11
 */
12
13
#include "lib.h"
14
#include "str-sanitize.h"
15
#include "array.h"
16
17
#include "sieve-common.h"
18
#include "sieve-extensions.h"
19
#include "sieve-commands.h"
20
#include "sieve-stringlist.h"
21
#include "sieve-code.h"
22
#include "sieve-address.h"
23
#include "sieve-comparators.h"
24
#include "sieve-match-types.h"
25
#include "sieve-address-parts.h"
26
#include "sieve-message.h"
27
28
#include "sieve-validator.h"
29
#include "sieve-generator.h"
30
#include "sieve-interpreter.h"
31
#include "sieve-dump.h"
32
#include "sieve-match.h"
33
34
/*
35
 * Forward declarations
36
 */
37
38
static const struct sieve_command_def envelope_test;
39
const struct sieve_operation_def envelope_operation;
40
const struct sieve_extension_def envelope_extension;
41
42
/*
43
 * Extension
44
 */
45
46
static bool
47
ext_envelope_validator_load(const struct sieve_extension *ext,
48
          struct sieve_validator *valdtr);
49
static bool
50
ext_envelope_interpreter_load(const struct sieve_extension *ext,
51
            const struct sieve_runtime_env *renv,
52
            sieve_size_t *address);
53
54
static bool
55
ext_envelope_validator_validate(const struct sieve_extension *ext,
56
        struct sieve_validator *valdtr, void *context,
57
        struct sieve_ast_argument *require_arg,
58
        bool required);
59
static int
60
ext_envelope_interpreter_run(const struct sieve_extension *this_ext,
61
           const struct sieve_runtime_env *renv,
62
           void *context, bool deferred);
63
64
const struct sieve_extension_def envelope_extension = {
65
  .name = "envelope",
66
  .interpreter_load = ext_envelope_interpreter_load,
67
  .validator_load = ext_envelope_validator_load,
68
  SIEVE_EXT_DEFINE_OPERATION(envelope_operation)
69
};
70
const struct sieve_validator_extension
71
envelope_validator_extension = {
72
  .ext = &envelope_extension,
73
  .validate = ext_envelope_validator_validate
74
};
75
const struct sieve_interpreter_extension
76
envelope_interpreter_extension = {
77
  .ext_def = &envelope_extension,
78
  .run = ext_envelope_interpreter_run
79
};
80
81
static bool
82
ext_envelope_validator_load(const struct sieve_extension *ext,
83
          struct sieve_validator *valdtr)
84
0
{
85
  /* Register new test */
86
0
  sieve_validator_register_command(valdtr, ext, &envelope_test);
87
88
0
  sieve_validator_extension_register(valdtr, ext,
89
0
             &envelope_validator_extension, NULL);
90
0
  return TRUE;
91
0
}
92
93
static bool
94
ext_envelope_interpreter_load(const struct sieve_extension *ext,
95
            const struct sieve_runtime_env *renv,
96
            sieve_size_t *address ATTR_UNUSED)
97
0
{
98
0
  sieve_interpreter_extension_register(renv->interp, ext,
99
0
               &envelope_interpreter_extension,
100
0
               NULL);
101
0
  return TRUE;
102
0
}
103
104
static bool
105
ext_envelope_validator_validate(const struct sieve_extension *ext,
106
        struct sieve_validator *valdtr,
107
        void *context ATTR_UNUSED,
108
        struct sieve_ast_argument *require_arg,
109
        bool required)
110
0
{
111
0
  if (required) {
112
0
    enum sieve_compile_flags flags =
113
0
      sieve_validator_compile_flags(valdtr);
114
115
0
    if ((flags & SIEVE_COMPILE_FLAG_NO_ENVELOPE) != 0) {
116
0
      sieve_argument_validate_error(
117
0
        valdtr, require_arg,
118
0
        "the %s extension cannot be used in this context "
119
0
        "(needs access to message envelope)",
120
0
        sieve_extension_name(ext));
121
0
      return FALSE;
122
0
    }
123
0
  }
124
0
  return TRUE;
125
0
}
126
127
static int
128
ext_envelope_interpreter_run(const struct sieve_extension *ext,
129
           const struct sieve_runtime_env *renv,
130
           void *context ATTR_UNUSED, bool deferred)
131
0
{
132
0
  const struct sieve_execute_env *eenv = renv->exec_env;
133
134
0
  if ((eenv->flags & SIEVE_EXECUTE_FLAG_NO_ENVELOPE) != 0) {
135
0
    if (!deferred) {
136
0
      sieve_runtime_error(
137
0
        renv, NULL,
138
0
        "the %s extension cannot be used in this context "
139
0
        "(needs access to message envelope)",
140
0
        sieve_extension_name(ext));
141
0
    }
142
0
    return SIEVE_EXEC_FAILURE;
143
0
  }
144
0
  return SIEVE_EXEC_OK;
145
0
}
146
147
/*
148
 * Envelope test
149
 *
150
 * Syntax
151
 *   envelope [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE]
152
 *     <envelope-part: string-list> <key-list: string-list>
153
 */
154
155
static bool
156
tst_envelope_registered(struct sieve_validator *valdtr,
157
      const struct sieve_extension *ext,
158
      struct sieve_command_registration *cmd_reg);
159
static bool
160
tst_envelope_validate(struct sieve_validator *valdtr,
161
          struct sieve_command *tst);
162
static bool
163
tst_envelope_generate(const struct sieve_codegen_env *cgenv,
164
          struct sieve_command *ctx);
165
166
static const struct sieve_command_def envelope_test = {
167
  .identifier = "envelope",
168
  .type = SCT_TEST,
169
  .positional_args= 2,
170
  .subtests = 0,
171
  .block_allowed = FALSE,
172
  .block_required = FALSE,
173
  .registered = tst_envelope_registered,
174
  .validate = tst_envelope_validate,
175
  .generate = tst_envelope_generate
176
};
177
178
/*
179
 * Envelope operation
180
 */
181
182
static bool
183
ext_envelope_operation_dump(const struct sieve_dumptime_env *denv,
184
          sieve_size_t *address);
185
static int
186
ext_envelope_operation_execute(const struct sieve_runtime_env *renv,
187
             sieve_size_t *address);
188
189
const struct sieve_operation_def envelope_operation = {
190
  .mnemonic = "ENVELOPE",
191
  .ext_def = &envelope_extension,
192
  .dump = ext_envelope_operation_dump,
193
  .execute = ext_envelope_operation_execute
194
};
195
196
/*
197
 * Envelope parts
198
 *
199
 * FIXME: not available to extensions
200
 */
201
202
struct sieve_envelope_part {
203
  const char *identifier;
204
205
  const struct smtp_address *const *(*get_addresses)
206
    (const struct sieve_runtime_env *renv);
207
  const char *const *(*get_values)
208
    (const struct sieve_runtime_env *renv);
209
};
210
211
static const struct smtp_address *const *
212
_from_part_get_addresses(const struct sieve_runtime_env *renv);
213
static const char *const *
214
_from_part_get_values(const struct sieve_runtime_env *renv);
215
static const struct smtp_address *const *
216
_to_part_get_addresses(const struct sieve_runtime_env *renv);
217
static const char *const *
218
_to_part_get_values(const struct sieve_runtime_env *renv);
219
static const char *const *
220
_auth_part_get_values(const struct sieve_runtime_env *renv);
221
222
static const struct sieve_envelope_part _from_part = {
223
  "from",
224
  _from_part_get_addresses,
225
  _from_part_get_values,
226
};
227
228
static const struct sieve_envelope_part _to_part = {
229
  "to",
230
  _to_part_get_addresses,
231
  _to_part_get_values,
232
};
233
234
static const struct sieve_envelope_part _auth_part = {
235
  "auth",
236
  NULL,
237
  _auth_part_get_values,
238
};
239
240
static const struct sieve_envelope_part *_envelope_parts[] = {
241
  /* Required */
242
  &_from_part, &_to_part,
243
244
  /* Non-standard */
245
  &_auth_part
246
};
247
248
static unsigned int _envelope_part_count = N_ELEMENTS(_envelope_parts);
249
250
static const struct sieve_envelope_part *
251
_envelope_part_find(const char *identifier)
252
0
{
253
0
  unsigned int i;
254
255
0
  for (i = 0; i < _envelope_part_count; i++) {
256
0
    if (strcasecmp(_envelope_parts[i]->identifier,
257
0
             identifier) == 0)
258
0
      return _envelope_parts[i];
259
0
  }
260
261
0
  return NULL;
262
0
}
263
264
/* Envelope parts implementation */
265
266
static const struct smtp_address *const *
267
_from_part_get_addresses(const struct sieve_runtime_env *renv)
268
0
{
269
0
  ARRAY(const struct smtp_address *) envelope_values;
270
0
  const struct smtp_address *address =
271
0
    sieve_message_get_sender(renv->msgctx);
272
273
0
  t_array_init(&envelope_values, 2);
274
275
0
  if (address == NULL)
276
0
    address = smtp_address_create_temp(NULL, NULL);
277
0
  array_append(&envelope_values, &address, 1);
278
279
0
  (void)array_append_space(&envelope_values);
280
0
  return array_idx(&envelope_values, 0);
281
0
}
282
283
static const char *const *
284
_from_part_get_values(const struct sieve_runtime_env *renv)
285
0
{
286
0
  ARRAY(const char *)envelope_values;
287
0
  const struct smtp_address *address =
288
0
    sieve_message_get_sender(renv->msgctx);
289
0
  const char *value;
290
291
0
  t_array_init(&envelope_values, 2);
292
293
0
  value = "";
294
0
  if (!smtp_address_isnull(address))
295
0
    value = smtp_address_encode(address);
296
0
  array_append(&envelope_values, &value, 1);
297
298
0
  (void)array_append_space(&envelope_values);
299
300
0
  return array_idx(&envelope_values, 0);
301
0
}
302
303
static const struct smtp_address *const *
304
_to_part_get_addresses(const struct sieve_runtime_env *renv)
305
0
{
306
0
  ARRAY(const struct smtp_address *) envelope_values;
307
0
  const struct smtp_address *address =
308
0
    sieve_message_get_orig_recipient(renv->msgctx);
309
310
0
  if (address != NULL && address->localpart != NULL) {
311
0
    t_array_init(&envelope_values, 2);
312
313
0
    array_append(&envelope_values, &address, 1);
314
315
0
    (void)array_append_space(&envelope_values);
316
0
    return array_idx(&envelope_values, 0);
317
0
  }
318
0
  return NULL;
319
0
}
320
321
static const char *const *
322
_to_part_get_values(const struct sieve_runtime_env *renv)
323
0
{
324
0
  ARRAY(const char *) envelope_values;
325
0
  const struct smtp_address *address =
326
0
    sieve_message_get_orig_recipient(renv->msgctx);
327
328
0
  t_array_init(&envelope_values, 2);
329
330
0
  if (address != NULL && address->localpart != NULL) {
331
0
    const char *value = smtp_address_encode(address);
332
0
    array_append(&envelope_values, &value, 1);
333
0
  }
334
335
0
  (void)array_append_space(&envelope_values);
336
337
0
  return array_idx(&envelope_values, 0);
338
0
}
339
340
static const char *const *
341
_auth_part_get_values(const struct sieve_runtime_env *renv)
342
0
{
343
0
  const struct sieve_execute_env *eenv = renv->exec_env;
344
0
  ARRAY(const char *) envelope_values;
345
346
0
  t_array_init(&envelope_values, 2);
347
348
0
  if (eenv->msgdata->auth_user != NULL)
349
0
    array_append(&envelope_values, &eenv->msgdata->auth_user, 1);
350
351
0
  (void)array_append_space(&envelope_values);
352
353
0
  return array_idx(&envelope_values, 0);
354
0
}
355
356
/*
357
 * Envelope address list
358
 */
359
360
/* Forward declarations */
361
362
static int
363
sieve_envelope_address_list_next_string_item(struct sieve_stringlist *_strlist,
364
               string_t **str_r);
365
static int
366
sieve_envelope_address_list_next_item(struct sieve_address_list *_addrlist,
367
              struct smtp_address *addr_r,
368
              string_t **unparsed_r);
369
static void
370
sieve_envelope_address_list_reset(struct sieve_stringlist *_strlist);
371
372
/* Stringlist object */
373
374
struct sieve_envelope_address_list {
375
  struct sieve_address_list addrlist;
376
377
  struct sieve_stringlist *env_parts;
378
379
  const struct smtp_address *const *cur_addresses;
380
  const char *const *cur_values;
381
382
  int value_index;
383
};
384
385
static struct sieve_address_list *
386
sieve_envelope_address_list_create(const struct sieve_runtime_env *renv,
387
           struct sieve_stringlist *env_parts)
388
0
{
389
0
  struct sieve_envelope_address_list *addrlist;
390
391
0
  addrlist = t_new(struct sieve_envelope_address_list, 1);
392
0
  addrlist->addrlist.strlist.runenv = renv;
393
0
  addrlist->addrlist.strlist.exec_status = SIEVE_EXEC_OK;
394
0
  addrlist->addrlist.strlist.next_item =
395
0
    sieve_envelope_address_list_next_string_item;
396
0
  addrlist->addrlist.strlist.reset = sieve_envelope_address_list_reset;
397
0
  addrlist->addrlist.next_item = sieve_envelope_address_list_next_item;
398
0
  addrlist->env_parts = env_parts;
399
400
0
  return &addrlist->addrlist;
401
0
}
402
403
static int
404
sieve_envelope_address_list_next_item(struct sieve_address_list *_addrlist,
405
              struct smtp_address *addr_r,
406
              string_t **unparsed_r)
407
0
{
408
0
  struct sieve_envelope_address_list *addrlist =
409
0
    (struct sieve_envelope_address_list *)_addrlist;
410
0
  const struct sieve_runtime_env *renv = _addrlist->strlist.runenv;
411
412
0
  if (addr_r != NULL)
413
0
    smtp_address_init(addr_r, NULL, NULL);
414
0
  if (unparsed_r != NULL) *unparsed_r = NULL;
415
416
0
  while (addrlist->cur_addresses == NULL &&
417
0
         addrlist->cur_values == NULL) {
418
0
    const struct sieve_envelope_part *epart;
419
0
    string_t *envp_item = NULL;
420
0
    int ret;
421
422
    /* Read next header value from source list */
423
0
    if ((ret = sieve_stringlist_next_item(addrlist->env_parts,
424
0
                  &envp_item)) <= 0)
425
0
      return ret;
426
427
0
    if (_addrlist->strlist.trace) {
428
0
      sieve_runtime_trace(
429
0
        _addrlist->strlist.runenv, 0,
430
0
        "getting '%s' part from message envelope",
431
0
        str_sanitize(str_c(envp_item), 80));
432
0
    }
433
434
0
    if ((epart=_envelope_part_find(str_c(envp_item))) != NULL) {
435
0
      addrlist->value_index = 0;
436
437
0
      if (epart->get_addresses != NULL) {
438
        /* Field contains addresses */
439
0
        addrlist->cur_addresses =
440
0
          epart->get_addresses(renv);
441
442
        /* Drop empty list */
443
0
        if (addrlist->cur_addresses != NULL &&
444
0
            addrlist->cur_addresses[0] == NULL)
445
0
          addrlist->cur_addresses = NULL;
446
0
      }
447
448
0
      if (addrlist->cur_addresses == NULL &&
449
0
          epart->get_values != NULL) {
450
        /* Field contains something else */
451
0
        addrlist->cur_values = epart->get_values(renv);
452
453
        /* Drop empty list */
454
0
        if (addrlist->cur_values != NULL &&
455
0
            addrlist->cur_values[0] == NULL)
456
0
          addrlist->cur_values = NULL;
457
0
      }
458
0
    }
459
0
  }
460
461
  /* Return next item */
462
0
  if (addrlist->cur_addresses != NULL) {
463
0
    const struct smtp_address *addr =
464
0
      addrlist->cur_addresses[addrlist->value_index];
465
466
0
    if (addr->localpart == NULL) {
467
      /* Null path <> */
468
0
      if (unparsed_r != NULL)
469
0
        *unparsed_r = t_str_new_const("", 0);
470
0
    } else {
471
0
      if (addr_r != NULL)
472
0
        *addr_r = *addr;
473
0
    }
474
475
    /* Advance to next value */
476
0
    addrlist->value_index++;
477
0
    if (addrlist->cur_addresses[addrlist->value_index] == NULL) {
478
0
      addrlist->cur_addresses = NULL;
479
0
      addrlist->value_index = 0;
480
0
    }
481
0
  } else {
482
0
    if (unparsed_r != NULL) {
483
0
      const char *value =
484
0
        addrlist->cur_values[addrlist->value_index];
485
486
0
      *unparsed_r = t_str_new_const(value, strlen(value));
487
0
    }
488
489
    /* Advance to next value */
490
0
    addrlist->value_index++;
491
0
    if (addrlist->cur_values[addrlist->value_index] == NULL) {
492
0
      addrlist->cur_values = NULL;
493
0
      addrlist->value_index = 0;
494
0
    }
495
0
  }
496
497
0
  return 1;
498
0
}
499
500
static int
501
sieve_envelope_address_list_next_string_item(struct sieve_stringlist *_strlist,
502
               string_t **str_r)
503
0
{
504
0
  struct sieve_address_list *addrlist =
505
0
    (struct sieve_address_list *)_strlist;
506
0
  struct smtp_address addr;
507
0
  int ret;
508
509
0
  if ((ret=sieve_envelope_address_list_next_item(addrlist, &addr,
510
0
                   str_r)) <= 0)
511
0
    return ret;
512
513
0
  if (addr.localpart != NULL) {
514
0
    const char *addr_str = smtp_address_encode(&addr);
515
0
    if (str_r != NULL)
516
0
      *str_r = t_str_new_const(addr_str, strlen(addr_str));
517
0
  }
518
0
  return 1;
519
0
}
520
521
static void
522
sieve_envelope_address_list_reset(struct sieve_stringlist *_strlist)
523
0
{
524
0
  struct sieve_envelope_address_list *addrlist =
525
0
    (struct sieve_envelope_address_list *)_strlist;
526
527
0
  sieve_stringlist_reset(addrlist->env_parts);
528
0
  addrlist->cur_addresses = NULL;
529
0
  addrlist->cur_values = NULL;
530
0
  addrlist->value_index = 0;
531
0
}
532
533
/*
534
 * Command Registration
535
 */
536
537
static bool
538
tst_envelope_registered(struct sieve_validator *valdtr,
539
      const struct sieve_extension *ext ATTR_UNUSED,
540
      struct sieve_command_registration *cmd_reg)
541
0
{
542
  /* The order of these is not significant */
543
0
  sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
544
0
  sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
545
0
  sieve_address_parts_link_tags(valdtr, cmd_reg, SIEVE_AM_OPT_ADDRESS_PART);
546
0
  return TRUE;
547
0
}
548
549
/*
550
 * Validation
551
 */
552
553
static int
554
_envelope_part_is_supported(void *context, struct sieve_ast_argument *arg)
555
0
{
556
0
  const struct sieve_envelope_part **not_address =
557
0
    (const struct sieve_envelope_part **) context;
558
559
0
  if (sieve_argument_is_string_literal(arg)) {
560
0
    const struct sieve_envelope_part *epart;
561
562
0
    if ((epart=_envelope_part_find(
563
0
      sieve_ast_strlist_strc(arg))) != NULL) {
564
0
      if (epart->get_addresses == NULL) {
565
0
        if (*not_address == NULL)
566
0
          *not_address = epart;
567
0
      }
568
0
      return 1;
569
0
    }
570
0
    return 0;
571
0
  }
572
0
  return 1; /* Can't check at compile time */
573
0
}
574
575
static bool
576
tst_envelope_validate(struct sieve_validator *valdtr, struct sieve_command *tst)
577
0
{
578
0
  struct sieve_ast_argument *arg = tst->first_positional;
579
0
  struct sieve_ast_argument *epart;
580
0
  struct sieve_comparator cmp_default =
581
0
    SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
582
0
  struct sieve_match_type mcht_default =
583
0
    SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
584
0
  const struct sieve_envelope_part *not_address = NULL;
585
586
0
  if (!sieve_validate_positional_argument(valdtr, tst, arg,
587
0
            "envelope part", 1,
588
0
            SAAT_STRING_LIST)) {
589
0
    return FALSE;
590
0
  }
591
592
0
  if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
593
0
    return FALSE;
594
595
  /* Check whether supplied envelope parts are supported
596
   *   FIXME: verify dynamic envelope parts at runtime
597
   */
598
0
  epart = arg;
599
0
  if (sieve_ast_stringlist_map(&epart, &not_address,
600
0
             _envelope_part_is_supported) <= 0) {
601
0
    i_assert(epart != NULL);
602
0
    sieve_argument_validate_error(
603
0
      valdtr, epart,
604
0
      "specified envelope part '%s' is not supported by the envelope test",
605
0
      str_sanitize(sieve_ast_strlist_strc(epart), 64));
606
0
    return FALSE;
607
0
  }
608
609
0
  if (not_address != NULL) {
610
0
    struct sieve_ast_argument *addrp_arg =
611
0
      sieve_command_find_argument(tst, &address_part_tag);
612
613
0
    if (addrp_arg != NULL) {
614
0
      sieve_argument_validate_error(
615
0
        valdtr, addrp_arg,
616
0
        "address part ':%s' specified while non-address envelope part '%s' "
617
0
        "is tested with the envelope test",
618
0
      sieve_ast_argument_tag(addrp_arg),
619
0
                 not_address->identifier);
620
0
      return FALSE;
621
0
    }
622
0
  }
623
624
0
  arg = sieve_ast_argument_next(arg);
625
626
0
  if (!sieve_validate_positional_argument(valdtr, tst, arg, "key list", 2,
627
0
            SAAT_STRING_LIST))
628
0
    return FALSE;
629
630
0
  if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
631
0
    return FALSE;
632
633
  /* Validate the key argument to a specified match type */
634
0
  return sieve_match_type_validate(valdtr, tst, arg,
635
0
           &mcht_default, &cmp_default);
636
0
}
637
638
/*
639
 * Code generation
640
 */
641
642
static bool
643
tst_envelope_generate(const struct sieve_codegen_env *cgenv,
644
          struct sieve_command *cmd)
645
0
{
646
0
  (void)sieve_operation_emit(cgenv->sblock, cmd->ext,
647
0
           &envelope_operation);
648
649
  /* Generate arguments */
650
0
  if (!sieve_generate_arguments(cgenv, cmd, NULL))
651
0
    return FALSE;
652
0
  return TRUE;
653
0
}
654
655
/*
656
 * Code dump
657
 */
658
659
static bool
660
ext_envelope_operation_dump(const struct sieve_dumptime_env *denv,
661
          sieve_size_t *address)
662
0
{
663
0
  sieve_code_dumpf(denv, "ENVELOPE");
664
0
  sieve_code_descend(denv);
665
666
  /* Handle any optional arguments */
667
0
  if (sieve_addrmatch_opr_optional_dump(denv, address, NULL) != 0)
668
0
    return FALSE;
669
670
0
  return (sieve_opr_stringlist_dump(denv, address, "envelope part") &&
671
0
    sieve_opr_stringlist_dump(denv, address, "key list"));
672
0
}
673
674
/*
675
 * Interpretation
676
 */
677
678
static int
679
ext_envelope_operation_execute(const struct sieve_runtime_env *renv,
680
             sieve_size_t *address)
681
0
{
682
0
  struct sieve_comparator cmp =
683
0
    SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator);
684
0
  struct sieve_match_type mcht =
685
0
    SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
686
0
  struct sieve_address_part addrp =
687
0
    SIEVE_ADDRESS_PART_DEFAULT(all_address_part);
688
0
  struct sieve_stringlist *env_part_list, *value_list, *key_list;
689
0
  struct sieve_address_list *addr_list;
690
0
  int match, ret;
691
692
  /*
693
   * Read operands
694
   */
695
696
  /* Read optional operands */
697
0
  if (sieve_addrmatch_opr_optional_read(renv, address, NULL, &ret,
698
0
                &addrp, &mcht, &cmp) < 0)
699
0
    return ret;
700
701
  /* Read envelope-part */
702
0
  if ((ret = sieve_opr_stringlist_read(renv, address, "envelope-part",
703
0
               &env_part_list)) <= 0)
704
0
    return ret;
705
706
  /* Read key-list */
707
0
  if ((ret = sieve_opr_stringlist_read(renv, address, "key-list",
708
0
               &key_list)) <= 0)
709
0
    return ret;
710
711
  /*
712
   * Perform test
713
   */
714
715
0
  sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "envelope test");
716
717
  /* Create value stringlist */
718
0
  addr_list = sieve_envelope_address_list_create(renv, env_part_list);
719
0
  value_list = sieve_address_part_stringlist_create(renv, &addrp,
720
0
                addr_list);
721
722
  /* Perform match */
723
0
  if ((match = sieve_match(renv, &mcht, &cmp,
724
0
         value_list, key_list, &ret)) < 0)
725
0
    return ret;
726
727
  /* Set test result for subsequent conditional jump */
728
0
  sieve_interpreter_set_test_result(renv->interp, match > 0);
729
0
  return SIEVE_EXEC_OK;
730
0
}
731