Coverage Report

Created: 2026-05-30 06:37

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