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/sieve-extensions.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 "mempool.h"
6
#include "hash.h"
7
#include "array.h"
8
9
#include "sieve-common.h"
10
#include "sieve-error.h"
11
#include "sieve-extensions.h"
12
13
/*
14
 * Forward declarations
15
 */
16
17
static void sieve_extension_registry_init(struct sieve_instance *svinst);
18
static void sieve_extension_registry_deinit(struct sieve_instance *svinst);
19
20
static void sieve_capability_registry_init(struct sieve_instance *svinst);
21
static void sieve_capability_registry_deinit(struct sieve_instance *svinst);
22
23
static int
24
_sieve_extension_register(struct sieve_instance *svinst,
25
        const struct sieve_extension_def *extdef,
26
        bool load, bool required,
27
        struct sieve_extension **ext_r);
28
29
static int
30
sieve_extensions_set_array(struct sieve_instance *svinst,
31
         const ARRAY_TYPE(const_string) *ext_names,
32
         bool global, bool implicit);
33
34
/*
35
 * Instance global context
36
 */
37
38
struct sieve_extension_registry {
39
  ARRAY(struct sieve_extension *) extensions;
40
  HASH_TABLE(const char *, struct sieve_extension *) extension_index;
41
  HASH_TABLE(const char *,
42
       struct sieve_capability_registration *) capabilities_index;
43
44
  /* Core language 'extensions' */
45
  const struct sieve_extension *comparator_extension;
46
  const struct sieve_extension *match_type_extension;
47
  const struct sieve_extension *address_part_extension;
48
49
  /* Preloaded extensions */
50
  ARRAY(const struct sieve_extension *) preloaded_extensions;
51
};
52
53
/*
54
 * Pre-loaded 'extensions'
55
 */
56
57
extern const struct sieve_extension_def comparator_extension;
58
extern const struct sieve_extension_def match_type_extension;
59
extern const struct sieve_extension_def address_part_extension;
60
61
/*
62
 * Dummy extensions
63
 */
64
65
/* FIXME: This is stupid. Define a comparator-* extension and be done with it */
66
67
const struct sieve_extension_def comparator_i_octet_extension = {
68
  .name = "comparator-i;octet",
69
};
70
71
const struct sieve_extension_def comparator_i_ascii_casemap_extension = {
72
  .name = "comparator-i;ascii-casemap",
73
};
74
75
const struct sieve_extension_def comparator_i_unicode_casemap_extension = {
76
  .name = "comparator-i;unicode-casemap",
77
};
78
79
/*
80
 * List of native extensions
81
 */
82
83
/* Dummy extensions */
84
85
extern const struct sieve_extension_def comparator_i_octet_extension;
86
extern const struct sieve_extension_def comparator_i_ascii_casemap_extension;
87
extern const struct sieve_extension_def comparator_i_unicode_casemap_extension;
88
89
const struct sieve_extension_def *sieve_dummy_extensions[] = {
90
  &comparator_i_octet_extension, &comparator_i_ascii_casemap_extension,
91
  &comparator_i_unicode_casemap_extension,
92
};
93
94
const unsigned int sieve_dummy_extensions_count =
95
  N_ELEMENTS(sieve_dummy_extensions);
96
97
/* Core */
98
99
extern const struct sieve_extension_def fileinto_extension;
100
extern const struct sieve_extension_def reject_extension;
101
extern const struct sieve_extension_def envelope_extension;
102
extern const struct sieve_extension_def encoded_character_extension;
103
104
extern const struct sieve_extension_def vacation_extension;
105
extern const struct sieve_extension_def subaddress_extension;
106
extern const struct sieve_extension_def comparator_i_ascii_numeric_extension;
107
extern const struct sieve_extension_def relational_extension;
108
extern const struct sieve_extension_def regex_extension;
109
extern const struct sieve_extension_def imap4flags_extension;
110
extern const struct sieve_extension_def copy_extension;
111
extern const struct sieve_extension_def include_extension;
112
extern const struct sieve_extension_def body_extension;
113
extern const struct sieve_extension_def variables_extension;
114
extern const struct sieve_extension_def enotify_extension;
115
extern const struct sieve_extension_def environment_extension;
116
extern const struct sieve_extension_def mailbox_extension;
117
extern const struct sieve_extension_def date_extension;
118
extern const struct sieve_extension_def index_extension;
119
extern const struct sieve_extension_def ihave_extension;
120
extern const struct sieve_extension_def duplicate_extension;
121
extern const struct sieve_extension_def mime_extension;
122
extern const struct sieve_extension_def foreverypart_extension;
123
extern const struct sieve_extension_def extracttext_extension;
124
extern const struct sieve_extension_def mboxmetadata_extension;
125
extern const struct sieve_extension_def servermetadata_extension;
126
127
extern const struct sieve_extension_def vacation_seconds_extension;
128
extern const struct sieve_extension_def spamtest_extension;
129
extern const struct sieve_extension_def spamtestplus_extension;
130
extern const struct sieve_extension_def virustest_extension;
131
extern const struct sieve_extension_def editheader_extension;
132
extern const struct sieve_extension_def special_use_extension;
133
extern const struct sieve_extension_def extlists_extension;
134
135
extern const struct sieve_extension_def vnd_debug_extension;
136
extern const struct sieve_extension_def vnd_environment_extension;
137
extern const struct sieve_extension_def vnd_report_extension;
138
139
#ifdef HAVE_SIEVE_UNFINISHED
140
extern const struct sieve_extension_def ereject_extension;
141
#endif
142
143
const struct sieve_extension_def *sieve_extensions[] = {
144
  /* Core extensions */
145
  &fileinto_extension, &reject_extension, &envelope_extension,
146
  &encoded_character_extension,
147
148
  /* 'Plugins' */
149
  &vacation_extension, &subaddress_extension,
150
  &comparator_i_ascii_numeric_extension,
151
  &relational_extension, &regex_extension, &imap4flags_extension,
152
  &copy_extension, &include_extension, &body_extension,
153
  &variables_extension, &enotify_extension, &environment_extension,
154
  &mailbox_extension, &date_extension, &index_extension, &ihave_extension,
155
  &duplicate_extension, &mime_extension, &foreverypart_extension,
156
  &extracttext_extension,
157
158
  /* Extra;
159
     These are not enabled by default, e.g. because explicit configuration
160
     is necessary to make these useful.
161
  */
162
  &vacation_seconds_extension, &spamtest_extension,
163
  &spamtestplus_extension, &virustest_extension, &editheader_extension,
164
  &mboxmetadata_extension, &servermetadata_extension,
165
  &special_use_extension, &extlists_extension,
166
167
  /* vnd.dovecot. */
168
  &vnd_debug_extension, &vnd_environment_extension, &vnd_report_extension,
169
170
#ifdef HAVE_SIEVE_UNFINISHED
171
  /* Unfinished extensions */
172
  &ereject_extension,
173
#endif
174
};
175
176
const unsigned int sieve_extensions_count = N_ELEMENTS(sieve_extensions);
177
178
/*
179
 * Extensions init/deinit
180
 */
181
182
int sieve_extensions_init(struct sieve_instance *svinst)
183
0
{
184
0
  unsigned int i;
185
0
  struct sieve_extension_registry *ext_reg =
186
0
    p_new(svinst->pool, struct sieve_extension_registry, 1);
187
0
  int ret;
188
189
0
  svinst->ext_reg = ext_reg;
190
191
0
  sieve_extension_registry_init(svinst);
192
0
  sieve_capability_registry_init(svinst);
193
194
  /* Preloaded 'extensions' */
195
0
  ret = sieve_extension_register(svinst, &comparator_extension, TRUE,
196
0
               &ext_reg->comparator_extension);
197
0
  i_assert(ret == 0);
198
0
  ret = sieve_extension_register(svinst, &match_type_extension, TRUE,
199
0
               &ext_reg->match_type_extension);
200
0
  i_assert(ret == 0);
201
0
  ret = sieve_extension_register(svinst, &address_part_extension, TRUE,
202
0
               &ext_reg->address_part_extension);
203
0
  i_assert(ret == 0);
204
205
0
  p_array_init(&ext_reg->preloaded_extensions, svinst->pool, 5);
206
0
  array_append(&ext_reg->preloaded_extensions,
207
0
         &ext_reg->comparator_extension, 1);
208
0
  array_append(&ext_reg->preloaded_extensions,
209
0
         &ext_reg->match_type_extension, 1);
210
0
  array_append(&ext_reg->preloaded_extensions,
211
0
         &ext_reg->address_part_extension, 1);
212
213
  /* Pre-load dummy extensions */
214
0
  for (i = 0; i < sieve_dummy_extensions_count; i++) {
215
0
    struct sieve_extension *ext;
216
217
0
    if (_sieve_extension_register(svinst, sieve_dummy_extensions[i],
218
0
                TRUE, FALSE, &ext) < 0)
219
0
      return -1;
220
0
    ext->dummy = TRUE;
221
0
  }
222
223
  /* Pre-load core extensions */
224
0
  for (i = 0; i < sieve_extensions_count; i++) {
225
0
    if (sieve_extension_register(svinst, sieve_extensions[i],
226
0
               FALSE, NULL) < 0)
227
0
      return -1;
228
0
  }
229
230
  /* More extensions can be added through plugins */
231
0
  return 0;
232
0
}
233
234
int sieve_extensions_load(struct sieve_instance *svinst)
235
0
{
236
  /* Apply sieve_extensions configuration */
237
0
  if (sieve_extensions_set_array(svinst, &svinst->set->extensions,
238
0
               FALSE, FALSE) < 0)
239
0
    return -1;
240
241
  /* Apply sieve_global_extensions configuration */
242
0
  if (sieve_extensions_set_array(svinst, &svinst->set->global_extensions,
243
0
               TRUE, FALSE) < 0)
244
0
    return -1;
245
246
  /* Apply sieve_implicit_extensions configuration */
247
0
  if (sieve_extensions_set_array(svinst,
248
0
               &svinst->set->implicit_extensions,
249
0
               FALSE, TRUE) < 0)
250
0
    return -1;
251
252
0
  return 0;
253
0
}
254
255
void sieve_extensions_deinit(struct sieve_instance *svinst)
256
0
{
257
0
  sieve_extension_registry_deinit(svinst);
258
0
  sieve_capability_registry_deinit(svinst);
259
0
}
260
261
/*
262
 * Pre-loaded extensions
263
 */
264
265
const struct sieve_extension *const *
266
sieve_extensions_get_preloaded(struct sieve_instance *svinst,
267
             unsigned int *count_r)
268
0
{
269
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
270
271
0
  return array_get(&ext_reg->preloaded_extensions, count_r);
272
0
}
273
274
/*
275
 * Extension registry
276
 */
277
278
static void _sieve_extension_unload(struct sieve_extension *ext);
279
280
static int _sieve_extension_load(struct sieve_extension *ext)
281
0
{
282
0
  int ret;
283
284
  /* Drop context if this is a reload */
285
0
  if (ext->context != NULL)
286
0
    _sieve_extension_unload(ext);
287
288
  /* Call load handler */
289
0
  if (ext->def == NULL || ext->def->load == NULL)
290
0
    return 0;
291
292
0
  ret = ext->def->load(ext, &ext->context);
293
0
  i_assert(ret <= 0);
294
0
  i_assert(ret == 0 || ext->context == NULL);
295
296
0
  if (ret < 0) {
297
0
    e_error(ext->svinst->event,
298
0
      "failed to load '%s' extension support.",
299
0
      ext->def->name);
300
0
    return -1;
301
0
  }
302
0
  return 0;
303
0
}
304
305
static void _sieve_extension_unload(struct sieve_extension *ext)
306
0
{
307
  /* Call unload handler */
308
0
  if (ext->def != NULL && ext->def->unload != NULL)
309
0
    ext->def->unload(ext);
310
0
  ext->context = NULL;
311
0
}
312
313
static void sieve_extension_registry_init(struct sieve_instance *svinst)
314
0
{
315
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
316
317
0
  p_array_init(&ext_reg->extensions, svinst->pool, 50);
318
0
  hash_table_create(&ext_reg->extension_index, default_pool, 0,
319
0
        str_hash, strcmp);
320
0
}
321
322
static void sieve_extension_registry_deinit(struct sieve_instance *svinst)
323
0
{
324
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
325
0
  struct sieve_extension *const *exts;
326
0
  unsigned int i, ext_count;
327
328
0
  if (!hash_table_is_created(ext_reg->extension_index))
329
0
    return;
330
331
0
  exts = array_get_modifiable(&ext_reg->extensions, &ext_count);
332
0
  for (i = 0; i < ext_count; i++)
333
0
    _sieve_extension_unload(exts[i]);
334
335
0
  hash_table_destroy(&ext_reg->extension_index);
336
0
}
337
338
static struct sieve_extension *
339
sieve_extension_lookup(struct sieve_instance *svinst, const char *name)
340
0
{
341
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
342
343
0
  return hash_table_lookup(ext_reg->extension_index, name);
344
0
}
345
346
static struct sieve_extension *
347
sieve_extension_alloc(struct sieve_instance *svinst,
348
          const struct sieve_extension_def *extdef)
349
0
{
350
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
351
0
  struct sieve_extension *ext, **extr;
352
0
  int ext_id;
353
354
0
  ext_id = (int)array_count(&ext_reg->extensions);
355
356
  /* Add extension to the registry */
357
0
  extr = array_append_space(&ext_reg->extensions);
358
0
  *extr = ext = p_new(svinst->pool, struct sieve_extension, 1);
359
0
  ext->id = ext_id;
360
0
  ext->def = extdef;
361
0
  ext->svinst = svinst;
362
0
  return ext;
363
0
}
364
365
static int
366
_sieve_extension_register(struct sieve_instance *svinst,
367
        const struct sieve_extension_def *extdef,
368
        bool load, bool required,
369
        struct sieve_extension **ext_r)
370
0
{
371
0
  struct sieve_extension *ext;
372
373
0
  if (ext_r != NULL)
374
0
    *ext_r = NULL;
375
0
  ext = sieve_extension_lookup(svinst, extdef->name);
376
377
  /* Register extension if it is not registered already */
378
0
  if (ext == NULL) {
379
0
    ext = sieve_extension_alloc(svinst, extdef);
380
0
    hash_table_insert(svinst->ext_reg->extension_index,
381
0
          extdef->name, ext);
382
0
  } else if (ext->overridden) {
383
    /* Create a dummy */
384
0
    ext = sieve_extension_alloc(svinst, extdef);
385
0
  } else {
386
    /* Re-register it if it were previously unregistered
387
     * (not going to happen)
388
     */
389
0
    i_assert(ext->def == NULL || ext->def == extdef);
390
0
    ext->def = extdef;
391
0
  }
392
393
  /* Enable extension */
394
0
  if (load || required) {
395
0
    ext->enabled = (ext->enabled || load);
396
397
    /* Call load handler if extension was not loaded already */
398
0
    if (!ext->loaded) {
399
0
      if (_sieve_extension_load(ext) < 0)
400
0
        return -1;
401
0
    }
402
0
    ext->loaded = TRUE;
403
0
  }
404
405
0
  ext->required = (ext->required || required);
406
407
0
  if (ext_r != NULL)
408
0
    *ext_r = ext;
409
0
  return 0;
410
0
}
411
412
static inline int
413
_sieve_extension_register_const(struct sieve_instance *svinst,
414
        const struct sieve_extension_def *extdef,
415
        bool load, bool required,
416
        const struct sieve_extension **ext_r)
417
0
{
418
0
  struct sieve_extension *ext;
419
420
0
  if (_sieve_extension_register(svinst, extdef, load, required,
421
0
              &ext) < 0) {
422
0
    if (ext_r != NULL)
423
0
      *ext_r = NULL;
424
0
    return -1;
425
0
  }
426
0
  if (ext_r != NULL)
427
0
    *ext_r = ext;
428
0
  return 0;
429
0
}
430
431
int sieve_extension_register(struct sieve_instance *svinst,
432
           const struct sieve_extension_def *extdef,
433
           bool load, const struct sieve_extension **ext_r)
434
0
{
435
0
  return _sieve_extension_register_const(svinst, extdef, load, FALSE,
436
0
                 ext_r);
437
0
}
438
439
void sieve_extension_unregister(const struct sieve_extension *ext)
440
0
{
441
0
  if (ext == NULL)
442
0
    return;
443
444
0
  struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg;
445
0
  struct sieve_extension *const *mod_ext;
446
0
  int ext_id = ext->id;
447
448
0
  if (ext_id >= 0 && ext_id < (int) array_count(&ext_reg->extensions)) {
449
0
    mod_ext = array_idx(&ext_reg->extensions, ext_id);
450
451
0
    sieve_extension_capabilities_unregister(*mod_ext);
452
0
    _sieve_extension_unload(*mod_ext);
453
0
    (*mod_ext)->loaded = FALSE;
454
0
    (*mod_ext)->enabled = FALSE;
455
0
    (*mod_ext)->def = NULL;
456
0
  }
457
0
}
458
459
int sieve_extension_require(struct sieve_instance *svinst,
460
          const struct sieve_extension_def *extdef, bool load,
461
          const struct sieve_extension **ext_r)
462
0
{
463
0
  return _sieve_extension_register_const(svinst, extdef, load, TRUE,
464
0
                 ext_r);
465
0
}
466
467
int sieve_extension_reload(const struct sieve_extension *ext)
468
0
{
469
0
  struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg;
470
0
  struct sieve_extension *const *mod_ext;
471
0
  int ext_id = ext->id;
472
473
  /* Let's not just cast the 'const' away */
474
0
  i_assert(ext_id >= 0 &&
475
0
     ext_id < (int) array_count(&ext_reg->extensions));
476
477
0
  mod_ext = array_idx(&ext_reg->extensions, ext_id);
478
0
  return _sieve_extension_load(*mod_ext);
479
0
}
480
481
int sieve_extension_replace(struct sieve_instance *svinst,
482
          const struct sieve_extension_def *extdef, bool load,
483
          const struct sieve_extension **ext_r)
484
0
{
485
0
  struct sieve_extension *ext;
486
487
0
  ext = sieve_extension_lookup(svinst, extdef->name);
488
0
  if (ext != NULL)
489
0
    sieve_extension_unregister(ext);
490
491
0
  return sieve_extension_register(svinst, extdef, load, ext_r);
492
0
}
493
494
void sieve_extension_override(struct sieve_instance *svinst, const char *name,
495
            const struct sieve_extension *ext)
496
0
{
497
0
  struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg;
498
0
  struct sieve_extension *const *mod_ext;
499
0
  struct sieve_extension *old_ext;
500
501
0
  old_ext = sieve_extension_lookup(svinst, name);
502
0
  if (old_ext == ext)
503
0
    return;
504
0
  i_assert(old_ext == NULL || !old_ext->overridden);
505
506
0
  i_assert(ext->id >= 0 &&
507
0
     ext->id < (int)array_count(&ext_reg->extensions));
508
0
  mod_ext = array_idx(&ext_reg->extensions, ext->id);
509
510
0
  hash_table_update(ext_reg->extension_index, name, *mod_ext);
511
0
  if (old_ext != NULL)
512
0
    old_ext->overridden = TRUE;
513
0
}
514
515
unsigned int sieve_extensions_get_count(struct sieve_instance *svinst)
516
0
{
517
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
518
519
0
  return array_count(&ext_reg->extensions);
520
0
}
521
522
const struct sieve_extension *const *
523
sieve_extensions_get_all(struct sieve_instance *svinst, unsigned int *count_r)
524
0
{
525
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
526
527
0
  return (const struct sieve_extension *const *)
528
0
    array_get(&ext_reg->extensions, count_r);
529
0
}
530
531
const struct sieve_extension *
532
sieve_extension_get_by_id(struct sieve_instance *svinst, unsigned int ext_id)
533
0
{
534
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
535
0
  struct sieve_extension *const *ext;
536
537
0
  if (ext_id < array_count(&ext_reg->extensions)) {
538
0
    ext = array_idx(&ext_reg->extensions, ext_id);
539
540
0
    if ((*ext)->def != NULL &&
541
0
        ((*ext)->enabled || (*ext)->required))
542
0
      return *ext;
543
0
  }
544
0
  return NULL;
545
0
}
546
547
const struct sieve_extension *
548
sieve_extension_get_by_name(struct sieve_instance *svinst, const char *name)
549
0
{
550
0
  const struct sieve_extension *ext;
551
552
0
  if (*name == '@')
553
0
    return NULL;
554
0
  if (strlen(name) > 128)
555
0
    return NULL;
556
557
0
  ext = sieve_extension_lookup(svinst, name);
558
0
  if (ext == NULL || ext->def == NULL ||
559
0
      (!ext->enabled && !ext->required))
560
0
    return NULL;
561
0
  return ext;
562
0
}
563
564
static inline bool _sieve_extension_listable(const struct sieve_extension *ext)
565
0
{
566
0
  return (ext->enabled && ext->def != NULL &&
567
0
    *(ext->def->name) != '@' && !ext->dummy &&
568
0
    !ext->global && !ext->overridden);
569
0
}
570
571
const char *sieve_extensions_get_string(struct sieve_instance *svinst)
572
0
{
573
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
574
0
  string_t *extstr = t_str_new(256);
575
0
  struct sieve_extension *const *exts;
576
0
  unsigned int i, ext_count;
577
578
0
  exts = array_get(&ext_reg->extensions, &ext_count);
579
0
  if (ext_count > 0) {
580
0
    i = 0;
581
582
    /* Find first listable extension */
583
0
    while (i < ext_count && !_sieve_extension_listable(exts[i]))
584
0
      i++;
585
586
0
    if (i < ext_count) {
587
      /* Add first to string */
588
0
      str_append(extstr, exts[i]->def->name);
589
0
      i++;
590
591
      /* Add others */
592
0
      for (; i < ext_count; i++) {
593
0
        if (_sieve_extension_listable(exts[i])) {
594
0
          str_append_c(extstr, ' ');
595
0
          str_append(extstr, exts[i]->def->name);
596
0
        }
597
0
      }
598
0
    }
599
0
  }
600
0
  return str_c(extstr);
601
0
}
602
603
static int
604
sieve_extension_set_enabled(struct sieve_extension *ext, bool enabled)
605
0
{
606
0
  int ret = 0;
607
608
0
  if (enabled) {
609
0
    ext->enabled = TRUE;
610
611
0
    if (!ext->loaded)
612
0
      ret = _sieve_extension_load(ext);
613
614
0
    ext->loaded = TRUE;
615
0
  } else {
616
0
    ext->enabled = FALSE;
617
0
  }
618
619
0
  return ret;
620
0
}
621
622
static int
623
sieve_extension_set_global(struct sieve_extension *ext, bool enabled)
624
0
{
625
0
  int ret = 0;
626
627
0
  if (enabled) {
628
0
    ret = sieve_extension_set_enabled(ext, TRUE);
629
0
    ext->global = TRUE;
630
0
  } else {
631
0
    ext->global = FALSE;
632
0
  }
633
634
0
  return ret;
635
0
}
636
637
static int
638
sieve_extension_set_implicit(struct sieve_extension *ext, bool enabled)
639
0
{
640
0
  int ret = 0;
641
642
0
  if (enabled) {
643
0
    ret = sieve_extension_set_enabled(ext, TRUE);
644
0
    ext->implicit = TRUE;
645
0
  } else {
646
0
    ext->implicit = FALSE;
647
0
  }
648
649
0
  return ret;
650
0
}
651
652
static int
653
sieve_extensions_set_array(struct sieve_instance *svinst,
654
         const ARRAY_TYPE(const_string) *ext_names,
655
         bool global, bool implicit)
656
0
{
657
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
658
0
  const char *name;
659
660
0
  if (array_is_empty(ext_names))
661
0
    return 0;
662
663
0
  int ret = 0;
664
0
  array_foreach_elem(ext_names, name) {
665
0
    struct sieve_extension *ext;
666
667
0
    ext = hash_table_lookup(ext_reg->extension_index, name);
668
0
    if (ext == NULL || ext->def == NULL ||
669
0
        *(ext->def->name) == '@') {
670
0
      e_warning(svinst->event,
671
0
          "ignored unknown extension '%s' "
672
0
          "while configuring available extensions",
673
0
          name);
674
0
      continue;
675
0
    }
676
0
    if (ext->id < 0)
677
0
      continue;
678
679
    /* Perform actual activation/deactivation */
680
0
    if (global) {
681
0
      if (sieve_extension_set_global(ext, TRUE) < 0) {
682
0
        ret = -1;
683
0
        break;
684
0
      }
685
0
    } else if (implicit) {
686
0
      if (sieve_extension_set_implicit(ext, TRUE) < 0) {
687
0
        ret = -1;
688
0
        break;
689
0
      }
690
0
    } else {
691
0
      if (sieve_extension_set_enabled(ext, TRUE) < 0) {
692
0
        ret = -1;
693
0
        break;
694
0
      }
695
0
    }
696
0
  }
697
698
0
  return ret;
699
0
}
700
701
int sieve_extensions_set_string(struct sieve_instance *svinst,
702
        const char *ext_string, bool global,
703
        bool implicit)
704
0
{
705
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
706
0
  ARRAY(const struct sieve_extension *) enabled_extensions;
707
0
  ARRAY(const struct sieve_extension *) disabled_extensions;
708
0
  const struct sieve_extension *const *ext_enabled;
709
0
  const struct sieve_extension *const *ext_disabled;
710
0
  struct sieve_extension **exts;
711
0
  const char **ext_names;
712
0
  unsigned int i, ext_count, ena_count, dis_count;
713
0
  bool relative = FALSE;
714
715
0
  if (ext_string == NULL) {
716
0
    if (global || implicit)
717
0
      return 0;
718
719
    /* Enable all */
720
0
    exts = array_get_modifiable(&ext_reg->extensions, &ext_count);
721
722
0
    for (i = 0; i < ext_count; i++) {
723
0
      if (sieve_extension_set_enabled(exts[i], TRUE) < 0)
724
0
        return -1;
725
0
    }
726
0
    return 0;
727
0
  }
728
729
0
  int ret = 0;
730
0
  T_BEGIN {
731
0
    t_array_init(&enabled_extensions,
732
0
           array_count(&ext_reg->extensions));
733
0
    t_array_init(&disabled_extensions,
734
0
           array_count(&ext_reg->extensions));
735
736
0
    ext_names = t_strsplit_spaces(ext_string, " \t");
737
738
0
    while (*ext_names != NULL) {
739
0
      const char *name = *ext_names;
740
741
0
      ext_names++;
742
743
0
      if (*name != '\0') {
744
0
        const struct sieve_extension *ext;
745
0
        char op = '\0'; /* No add/remove operation */
746
747
0
        if (*name == '+' || /* Add to existing config */
748
0
            *name == '-') { /* Remove from existing config */
749
0
          op = *name++;
750
0
          relative = TRUE;
751
0
        }
752
753
0
        if (*name == '@')
754
0
          ext = NULL;
755
0
        else {
756
0
          ext = hash_table_lookup(ext_reg->extension_index,
757
0
                name);
758
0
        }
759
760
0
        if (ext == NULL || ext->def == NULL) {
761
0
          e_warning(svinst->event,
762
0
              "ignored unknown extension '%s' while configuring "
763
0
              "available extensions", name);
764
0
          continue;
765
0
        }
766
767
0
        if (op == '-') {
768
0
          array_append(&disabled_extensions,
769
0
                 &ext, 1);
770
0
        } else {
771
0
          array_append(&enabled_extensions,
772
0
                 &ext, 1);
773
0
        }
774
0
      }
775
0
    }
776
777
0
    exts = array_get_modifiable(&ext_reg->extensions, &ext_count);
778
0
    ext_enabled = array_get(&enabled_extensions, &ena_count);
779
0
    ext_disabled = array_get(&disabled_extensions, &dis_count);
780
781
    /* Set new extension status */
782
783
0
    for (i = 0; i < ext_count; i++) {
784
0
      unsigned int j;
785
0
      bool enabled = FALSE;
786
787
0
      if (exts[i]->id < 0 || exts[i]->def == NULL ||
788
0
          *(exts[i]->def->name) == '@')
789
0
        continue;
790
791
      /* If extensions are specified relative to the default
792
         set, we first need to check which ones are disabled
793
       */
794
0
      if (relative) {
795
0
        if (global)
796
0
          enabled = exts[i]->global;
797
0
        else if (implicit)
798
0
          enabled = exts[i]->implicit;
799
0
        else
800
0
          enabled = exts[i]->enabled;
801
802
0
        if (enabled) {
803
          /* Disable if explicitly disabled */
804
0
          for (j = 0; j < dis_count; j++) {
805
0
            if (ext_disabled[j]->def == exts[i]->def) {
806
0
              enabled = FALSE;
807
0
              break;
808
0
            }
809
0
          }
810
0
        }
811
0
      }
812
813
      /* Enable if listed with '+' or no prefix */
814
0
      for (j = 0; j < ena_count; j++) {
815
0
        if (ext_enabled[j]->def == exts[i]->def) {
816
0
          enabled = TRUE;
817
0
          break;
818
0
        }
819
0
      }
820
821
      /* Perform actual activation/deactivation */
822
0
      if (global) {
823
0
        if (sieve_extension_set_global(exts[i],
824
0
                     enabled) < 0) {
825
0
          ret = -1;
826
0
          break;
827
0
        }
828
0
      } else if (implicit) {
829
0
        if (sieve_extension_set_implicit(exts[i],
830
0
                 enabled) < 0) {
831
0
          ret = -1;
832
0
          break;
833
0
        }
834
0
      } else {
835
0
        if (sieve_extension_set_enabled(exts[i],
836
0
                enabled) < 0) {
837
0
          ret = -1;
838
0
          break;
839
0
        }
840
0
      }
841
0
    }
842
0
  } T_END;
843
844
0
  return ret;
845
0
}
846
847
const struct sieve_extension *
848
sieve_get_match_type_extension(struct sieve_instance *svinst)
849
0
{
850
0
  return svinst->ext_reg->match_type_extension;
851
0
}
852
853
const struct sieve_extension *
854
sieve_get_comparator_extension(struct sieve_instance *svinst)
855
0
{
856
0
  return svinst->ext_reg->comparator_extension;
857
0
}
858
859
const struct sieve_extension *
860
sieve_get_address_part_extension(struct sieve_instance *svinst)
861
0
{
862
0
  return svinst->ext_reg->address_part_extension;
863
0
}
864
865
void sieve_enable_debug_extension(struct sieve_instance *svinst)
866
0
{
867
0
  const struct sieve_extension *ext;
868
0
  int ret;
869
870
0
  ret = sieve_extension_register(svinst, &vnd_debug_extension, TRUE,
871
0
               &ext);
872
0
  i_assert(ret == 0);
873
0
}
874
875
/*
876
 * Extension capabilities
877
 */
878
879
struct sieve_capability_registration {
880
  const struct sieve_extension *ext;
881
  const struct sieve_extension_capabilities *capabilities;
882
};
883
884
void sieve_capability_registry_init(struct sieve_instance *svinst)
885
0
{
886
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
887
888
0
  hash_table_create(&ext_reg->capabilities_index, default_pool, 0,
889
0
        str_hash, strcmp);
890
0
}
891
892
void sieve_capability_registry_deinit(struct sieve_instance *svinst)
893
0
{
894
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
895
896
0
  if (!hash_table_is_created(ext_reg->capabilities_index))
897
0
    return;
898
899
0
  hash_table_destroy(&svinst->ext_reg->capabilities_index);
900
0
}
901
902
void sieve_extension_capabilities_register(
903
  const struct sieve_extension *ext,
904
  const struct sieve_extension_capabilities *cap)
905
0
{
906
0
  struct sieve_instance *svinst = ext->svinst;
907
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
908
0
  struct sieve_capability_registration *reg;
909
910
0
  reg = hash_table_lookup(ext_reg->capabilities_index, cap->name);
911
0
  if (reg != NULL) {
912
    /* Already registered */
913
0
    return;
914
0
  }
915
916
0
  reg = p_new(svinst->pool, struct sieve_capability_registration, 1);
917
0
  reg->ext = ext;
918
0
  reg->capabilities = cap;
919
920
0
  hash_table_insert(ext_reg->capabilities_index, cap->name, reg);
921
0
}
922
923
void sieve_extension_capabilities_unregister(const struct sieve_extension *ext)
924
0
{
925
0
  struct sieve_extension_registry *ext_reg = ext->svinst->ext_reg;
926
0
  struct hash_iterate_context *hictx;
927
0
  const char *name;
928
0
  struct sieve_capability_registration *reg;
929
930
0
  hictx = hash_table_iterate_init(ext_reg->capabilities_index);
931
0
  while (hash_table_iterate(hictx, ext_reg->capabilities_index,
932
0
          &name, &reg)) {
933
0
    if (reg->ext == ext)
934
0
      hash_table_remove(ext_reg->capabilities_index, name);
935
0
  }
936
0
  hash_table_iterate_deinit(&hictx);
937
0
}
938
939
const char *
940
sieve_extension_capabilities_get_string(struct sieve_instance *svinst,
941
          const char *cap_name)
942
0
{
943
0
  struct sieve_extension_registry *ext_reg = svinst->ext_reg;
944
0
  const struct sieve_capability_registration *cap_reg =
945
0
    hash_table_lookup(ext_reg->capabilities_index, cap_name);
946
0
  const struct sieve_extension_capabilities *cap;
947
948
0
  if (cap_reg == NULL || cap_reg->capabilities == NULL)
949
0
    return NULL;
950
951
0
  cap = cap_reg->capabilities;
952
0
  if (cap->get_string == NULL || !cap_reg->ext->enabled)
953
0
    return NULL;
954
955
0
  return cap->get_string(cap_reg->ext);
956
0
}