Coverage Report

Created: 2026-10-17 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/sieve-address-parts.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "compat.h"
5
#include "mempool.h"
6
#include "hash.h"
7
#include "array.h"
8
#include "str-sanitize.h"
9
10
#include "sieve-extensions.h"
11
#include "sieve-code.h"
12
#include "sieve-address.h"
13
#include "sieve-commands.h"
14
#include "sieve-binary.h"
15
#include "sieve-comparators.h"
16
#include "sieve-match-types.h"
17
#include "sieve-validator.h"
18
#include "sieve-generator.h"
19
#include "sieve-interpreter.h"
20
#include "sieve-dump.h"
21
#include "sieve-match.h"
22
23
#include "sieve-address-parts.h"
24
25
#include <string.h>
26
27
/*
28
 * Default address parts
29
 */
30
31
const struct sieve_address_part_def *sieve_core_address_parts[] = {
32
  &all_address_part, &local_address_part, &domain_address_part
33
};
34
35
const unsigned int sieve_core_address_parts_count =
36
  N_ELEMENTS(sieve_core_address_parts);
37
38
/*
39
 * Address-part 'extension'
40
 */
41
42
static bool
43
addrp_validator_load(const struct sieve_extension *ext,
44
         struct sieve_validator *valdtr);
45
46
const struct sieve_extension_def address_part_extension = {
47
  .name = "@address-parts",
48
  .validator_load = addrp_validator_load
49
};
50
51
/*
52
 * Validator context:
53
 *   name-based address-part registry.
54
 */
55
56
static struct sieve_validator_object_registry *
57
_get_object_registry(struct sieve_validator *valdtr)
58
0
{
59
0
  struct sieve_instance *svinst;
60
0
  const struct sieve_extension *adrp_ext;
61
62
0
  svinst = sieve_validator_svinst(valdtr);
63
0
  adrp_ext = sieve_get_address_part_extension(svinst);
64
0
  return sieve_validator_object_registry_get(valdtr, adrp_ext);
65
0
}
66
67
void sieve_address_part_register(
68
  struct sieve_validator *valdtr, const struct sieve_extension *ext,
69
  const struct sieve_address_part_def *addrp_def)
70
0
{
71
0
  struct sieve_validator_object_registry *regs =
72
0
    _get_object_registry(valdtr);
73
74
0
  sieve_validator_object_registry_add(regs, ext, &addrp_def->obj_def);
75
0
}
76
77
static bool
78
sieve_address_part_exists(struct sieve_validator *valdtr,
79
        const char *identifier)
80
0
{
81
0
  struct sieve_validator_object_registry *regs =
82
0
    _get_object_registry(valdtr);
83
84
0
  return sieve_validator_object_registry_find(regs, identifier, NULL);
85
0
}
86
87
static const struct sieve_address_part *
88
sieve_address_part_create_instance(struct sieve_validator *valdtr,
89
           struct sieve_command *cmd,
90
           const char *identifier)
91
0
{
92
0
  struct sieve_validator_object_registry *regs =
93
0
    _get_object_registry(valdtr);
94
0
  struct sieve_object object;
95
0
  struct sieve_address_part *addrp;
96
97
0
  if (!sieve_validator_object_registry_find(regs, identifier, &object))
98
0
    return NULL;
99
100
0
  addrp = p_new(sieve_command_pool(cmd), struct sieve_address_part, 1);
101
0
  addrp->object = object;
102
0
  addrp->def = (const struct sieve_address_part_def *) object.def;
103
104
0
  return addrp;
105
0
}
106
107
static bool
108
addrp_validator_load(const struct sieve_extension *ext,
109
         struct sieve_validator *valdtr)
110
0
{
111
0
  struct sieve_validator_object_registry *regs =
112
0
    sieve_validator_object_registry_init(valdtr, ext);
113
0
  unsigned int i;
114
115
  /* Register core address-parts */
116
0
  for (i = 0; i < sieve_core_address_parts_count; i++) {
117
0
    sieve_validator_object_registry_add(
118
0
      regs, NULL, &(sieve_core_address_parts[i]->obj_def));
119
0
  }
120
121
0
  return TRUE;
122
0
}
123
124
void sieve_address_parts_link_tags(struct sieve_validator *valdtr,
125
           struct sieve_command_registration *cmd_reg,
126
           int id_code)
127
0
{
128
0
  struct sieve_instance *svinst;
129
0
  const struct sieve_extension *adrp_ext;
130
131
0
  svinst = sieve_validator_svinst(valdtr);
132
0
  adrp_ext = sieve_get_address_part_extension(svinst);
133
134
0
  sieve_validator_register_tag(valdtr, cmd_reg, adrp_ext,
135
0
             &address_part_tag, id_code);
136
0
}
137
138
/*
139
 * Address-part tagged argument
140
 */
141
142
/* Forward declarations */
143
144
static bool
145
tag_address_part_is_instance_of(struct sieve_validator *valdtr,
146
        struct sieve_command *cmd,
147
        const struct sieve_extension *ext,
148
        const char *identifier, void **data);
149
static bool
150
tag_address_part_validate(struct sieve_validator *valdtr,
151
        struct sieve_ast_argument **arg,
152
        struct sieve_command *cmd);
153
static bool
154
tag_address_part_generate(const struct sieve_codegen_env *cgenv,
155
        struct sieve_ast_argument *arg,
156
        struct sieve_command *cmd);
157
158
/* Argument object */
159
160
const struct sieve_argument_def address_part_tag = {
161
  .identifier = "ADDRESS-PART",
162
  .is_instance_of = tag_address_part_is_instance_of,
163
  .validate = tag_address_part_validate,
164
  .generate = tag_address_part_generate
165
};
166
167
/* Argument implementation */
168
169
static bool
170
tag_address_part_is_instance_of(struct sieve_validator *valdtr,
171
        struct sieve_command *cmd,
172
        const struct sieve_extension *ext ATTR_UNUSED,
173
        const char *identifier, void **data)
174
0
{
175
0
  const struct sieve_address_part *addrp;
176
177
0
  if (data == NULL)
178
0
    return sieve_address_part_exists(valdtr, identifier);
179
180
0
  if ((addrp = sieve_address_part_create_instance(
181
0
    valdtr, cmd, identifier)) == NULL)
182
0
    return FALSE;
183
184
0
  *data = (void *)addrp;
185
0
  return TRUE;
186
0
}
187
188
static bool
189
tag_address_part_validate(struct sieve_validator *valdtr ATTR_UNUSED,
190
        struct sieve_ast_argument **arg,
191
        struct sieve_command *cmd ATTR_UNUSED)
192
0
{
193
  /* NOTE: Currenly trivial, but might need to allow for further
194
     validation for future extensions.
195
   */
196
197
  /* Syntax:
198
       ":localpart" / ":domain" / ":all" (subject to extension)
199
     */
200
201
  /* Skip tag */
202
0
  *arg = sieve_ast_argument_next(*arg);
203
204
0
  return TRUE;
205
0
}
206
207
static bool
208
tag_address_part_generate(const struct sieve_codegen_env *cgenv,
209
        struct sieve_ast_argument *arg,
210
        struct sieve_command *cmd ATTR_UNUSED)
211
0
{
212
0
  struct sieve_address_part *addrp =
213
0
    (struct sieve_address_part *)arg->argument->data;
214
215
0
  sieve_opr_address_part_emit(cgenv->sblock, addrp);
216
0
  return TRUE;
217
0
}
218
219
/*
220
 * Address-part operand
221
 */
222
223
const struct sieve_operand_class sieve_address_part_operand_class =
224
  { "address part" };
225
226
static const struct sieve_extension_objects core_address_parts =
227
  SIEVE_EXT_DEFINE_MATCH_TYPES(sieve_core_address_parts);
228
229
const struct sieve_operand_def address_part_operand = {
230
  .name = "address-part",
231
  .code = SIEVE_OPERAND_ADDRESS_PART,
232
  .class = &sieve_address_part_operand_class,
233
  .interface = &core_address_parts
234
};
235
236
/*
237
 * Address-part string list
238
 */
239
240
static int
241
sieve_address_part_stringlist_next_item(struct sieve_stringlist *_strlist,
242
          string_t **str_r);
243
static void
244
sieve_address_part_stringlist_reset(struct sieve_stringlist *_strlist);
245
static int
246
sieve_address_part_stringlist_get_length(struct sieve_stringlist *_strlist);
247
static void
248
sieve_address_part_stringlist_set_trace(struct sieve_stringlist *_strlist,
249
          bool trace);
250
251
struct sieve_address_part_stringlist {
252
  struct sieve_stringlist strlist;
253
254
  const struct sieve_address_part *addrp;
255
  struct sieve_address_list *addresses;
256
};
257
258
struct sieve_stringlist *
259
sieve_address_part_stringlist_create(const struct sieve_runtime_env *renv,
260
             const struct sieve_address_part *addrp,
261
             struct sieve_address_list *addresses)
262
0
{
263
0
  struct sieve_address_part_stringlist *strlist;
264
265
0
  strlist = t_new(struct sieve_address_part_stringlist, 1);
266
0
  strlist->strlist.runenv = renv;
267
0
  strlist->strlist.next_item = sieve_address_part_stringlist_next_item;
268
0
  strlist->strlist.reset = sieve_address_part_stringlist_reset;
269
0
  strlist->strlist.get_length = sieve_address_part_stringlist_get_length;
270
0
  strlist->strlist.set_trace = sieve_address_part_stringlist_set_trace;
271
272
0
  strlist->addrp = addrp;
273
0
  strlist->addresses = addresses;
274
275
0
  return &strlist->strlist;
276
0
}
277
278
static int
279
sieve_address_part_stringlist_next_item(struct sieve_stringlist *_strlist,
280
          string_t **str_r)
281
0
{
282
0
  struct sieve_address_part_stringlist *strlist =
283
0
    (struct sieve_address_part_stringlist *)_strlist;
284
0
  struct smtp_address item;
285
0
  string_t *item_unparsed;
286
0
  int ret;
287
288
0
  *str_r = NULL;
289
290
0
  while (*str_r == NULL) {
291
0
    if ((ret = sieve_address_list_next_item(
292
0
      strlist->addresses, &item, &item_unparsed)) <= 0)
293
0
      return ret;
294
295
0
    if (item.localpart == NULL) {
296
0
      if (item_unparsed != NULL) {
297
0
        if (_strlist->trace) {
298
0
          sieve_runtime_trace(
299
0
            _strlist->runenv, 0,
300
0
            "extracting '%s' part from non-address value '%s'",
301
0
            sieve_address_part_name(strlist->addrp),
302
0
            str_sanitize(str_c(item_unparsed), 80));
303
0
        }
304
305
0
        if (str_len(item_unparsed) == 0 ||
306
0
            sieve_address_part_is(strlist->addrp, all_address_part))
307
0
          *str_r = item_unparsed;
308
0
      }
309
0
    } else {
310
0
      const struct sieve_address_part *addrp = strlist->addrp;
311
0
      const char *part = NULL;
312
313
0
      if (_strlist->trace) {
314
0
        sieve_runtime_trace(
315
0
          _strlist->runenv, 0,
316
0
          "extracting '%s' part from address %s",
317
0
          sieve_address_part_name(strlist->addrp),
318
0
          smtp_address_encode_path(&item));
319
0
      }
320
321
0
      if (addrp->def != NULL &&
322
0
          addrp->def->extract_from != NULL)
323
0
        part = addrp->def->extract_from(addrp, &item);
324
325
0
      if (part != NULL)
326
0
        *str_r = t_str_new_const(part, strlen(part));
327
0
    }
328
0
  }
329
330
0
  return 1;
331
0
}
332
333
static void
334
sieve_address_part_stringlist_reset(struct sieve_stringlist *_strlist)
335
0
{
336
0
  struct sieve_address_part_stringlist *strlist =
337
0
    (struct sieve_address_part_stringlist *)_strlist;
338
339
0
  sieve_address_list_reset(strlist->addresses);
340
0
}
341
342
static int
343
sieve_address_part_stringlist_get_length(struct sieve_stringlist *_strlist)
344
0
{
345
0
  struct sieve_address_part_stringlist *strlist =
346
0
    (struct sieve_address_part_stringlist *)_strlist;
347
348
0
  return sieve_address_list_get_length(strlist->addresses);
349
0
}
350
351
static void
352
sieve_address_part_stringlist_set_trace(struct sieve_stringlist *_strlist,
353
          bool trace)
354
0
{
355
0
  struct sieve_address_part_stringlist *strlist =
356
0
    (struct sieve_address_part_stringlist *)_strlist;
357
358
0
  sieve_address_list_set_trace(strlist->addresses, trace);
359
0
}
360
361
/*
362
 * Default ADDRESS-PART, MATCH-TYPE, COMPARATOR access
363
 */
364
365
int sieve_addrmatch_opr_optional_dump(const struct sieve_dumptime_env *denv,
366
              sieve_size_t *address,
367
              signed int *opt_code)
368
0
{
369
0
  signed int _opt_code = 0;
370
0
  bool final = FALSE, opok = TRUE;
371
372
0
  if (opt_code == NULL) {
373
0
    opt_code = &_opt_code;
374
0
    final = TRUE;
375
0
  }
376
377
0
  while (opok) {
378
0
    int opt;
379
380
0
    if ((opt = sieve_opr_optional_dump(
381
0
      denv, address, opt_code)) <= 0)
382
0
      return opt;
383
384
0
    switch (*opt_code) {
385
0
    case SIEVE_MATCH_OPT_COMPARATOR:
386
0
      opok = sieve_opr_comparator_dump(denv, address);
387
0
      break;
388
0
    case SIEVE_MATCH_OPT_MATCH_TYPE:
389
0
      opok = sieve_opr_match_type_dump(denv, address);
390
0
      break;
391
0
    case SIEVE_AM_OPT_ADDRESS_PART:
392
0
      opok = sieve_opr_address_part_dump(denv, address);
393
0
      break;
394
0
    default:
395
0
      return (final ? -1 : 1);
396
0
    }
397
0
  }
398
399
0
  return -1;
400
0
}
401
402
int sieve_addrmatch_opr_optional_read(const struct sieve_runtime_env *renv,
403
              sieve_size_t *address,
404
              signed int *opt_code, int *exec_status,
405
              struct sieve_address_part *addrp,
406
              struct sieve_match_type *mtch,
407
              struct sieve_comparator *cmp)
408
0
{
409
0
  signed int _opt_code = 0;
410
0
  bool final = FALSE;
411
0
  int status = SIEVE_EXEC_OK;
412
413
0
  if (opt_code == NULL) {
414
0
    opt_code = &_opt_code;
415
0
    final = TRUE;
416
0
  }
417
418
0
  if (exec_status != NULL)
419
0
    *exec_status = SIEVE_EXEC_OK;
420
421
0
  while (status == SIEVE_EXEC_OK) {
422
0
    int opt;
423
424
0
    if ((opt = sieve_opr_optional_read(
425
0
      renv, address, opt_code)) <= 0){
426
0
      if (opt < 0 && exec_status != NULL)
427
0
        *exec_status = SIEVE_EXEC_BIN_CORRUPT;
428
0
      return opt;
429
0
    }
430
431
0
    switch (*opt_code) {
432
0
    case SIEVE_MATCH_OPT_COMPARATOR:
433
0
      if (cmp == NULL) {
434
0
        sieve_runtime_trace_error(
435
0
          renv, "unexpected comparator operand");
436
0
        if (exec_status != NULL)
437
0
          *exec_status = SIEVE_EXEC_BIN_CORRUPT;
438
0
        return -1;
439
0
      }
440
0
      status = sieve_opr_comparator_read(renv, address, cmp);
441
0
      break;
442
0
    case SIEVE_MATCH_OPT_MATCH_TYPE:
443
0
      if (mtch == NULL) {
444
0
        sieve_runtime_trace_error(
445
0
          renv, "unexpected match-type operand");
446
0
        if (exec_status != NULL)
447
0
          *exec_status = SIEVE_EXEC_BIN_CORRUPT;
448
0
        return -1;
449
0
      }
450
0
      status = sieve_opr_match_type_read(renv, address, mtch);
451
0
      break;
452
0
    case SIEVE_AM_OPT_ADDRESS_PART:
453
0
      if (addrp == NULL) {
454
0
        sieve_runtime_trace_error(
455
0
          renv, "unexpected address-part operand");
456
0
        if (exec_status != NULL)
457
0
          *exec_status = SIEVE_EXEC_BIN_CORRUPT;
458
0
        return -1;
459
0
      }
460
0
      status = sieve_opr_address_part_read(
461
0
        renv, address, addrp);
462
0
      break;
463
0
    default:
464
0
      if (final) {
465
0
        sieve_runtime_trace_error(
466
0
          renv, "invalid optional operand");
467
0
        if (exec_status != NULL)
468
0
          *exec_status = SIEVE_EXEC_BIN_CORRUPT;
469
0
        return -1;
470
0
      }
471
0
      return 1;
472
0
    }
473
0
  }
474
475
0
  if (exec_status != NULL)
476
0
    *exec_status = status;
477
0
  return -1;
478
0
}
479
480
/*
481
 * Core address-part modifiers
482
 */
483
484
static const char *
485
addrp_all_extract_from(const struct sieve_address_part *addrp ATTR_UNUSED,
486
           const struct smtp_address *address)
487
0
{
488
0
  if (address->localpart == NULL)
489
0
    return NULL;
490
0
  return smtp_address_encode(address);
491
0
}
492
493
static const char *
494
addrp_domain_extract_from(const struct sieve_address_part *addrp ATTR_UNUSED,
495
        const struct smtp_address *address)
496
0
{
497
0
  return address->domain;
498
0
}
499
500
static const char *
501
addrp_localpart_extract_from(const struct sieve_address_part *addrp ATTR_UNUSED,
502
           const struct smtp_address *address)
503
0
{
504
0
  return address->localpart;
505
0
}
506
507
const struct sieve_address_part_def all_address_part = {
508
  SIEVE_OBJECT("all", &address_part_operand, SIEVE_ADDRESS_PART_ALL),
509
  .extract_from = addrp_all_extract_from
510
};
511
512
const struct sieve_address_part_def local_address_part = {
513
  SIEVE_OBJECT("localpart", &address_part_operand,
514
         SIEVE_ADDRESS_PART_LOCAL),
515
  .extract_from = addrp_localpart_extract_from
516
};
517
518
const struct sieve_address_part_def domain_address_part = {
519
  SIEVE_OBJECT("domain", &address_part_operand,
520
         SIEVE_ADDRESS_PART_DOMAIN),
521
  .extract_from = addrp_domain_extract_from
522
};