Coverage Report

Created: 2026-08-13 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/environment/ext-environment-common.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "hash.h"
5
6
#include "sieve-common.h"
7
#include "sieve-extensions.h"
8
#include "sieve-interpreter.h"
9
10
#include "ext-environment-common.h"
11
12
struct ext_environment_interpreter_context;
13
14
/*
15
 * Core environment items
16
 */
17
18
static const struct sieve_environment_item_def *core_env_items[] = {
19
  &domain_env_item,
20
  &host_env_item,
21
  &location_env_item,
22
  &phase_env_item,
23
  &name_env_item,
24
  &version_env_item,
25
};
26
27
static unsigned int core_env_items_count = N_ELEMENTS(core_env_items);
28
29
static void
30
sieve_environment_item_insert(
31
  struct ext_environment_interpreter_context *ctx,
32
  struct sieve_interpreter *interp, const struct sieve_extension *ext,
33
  const struct sieve_environment_item_def *item_def);
34
35
/*
36
 * Validator context
37
 */
38
39
struct ext_environment_interpreter_context {
40
  HASH_TABLE(const char *,
41
       const struct sieve_environment_item *) name_items;
42
  ARRAY(const struct sieve_environment_item *) prefix_items;
43
44
  bool active:1;
45
};
46
47
static void
48
ext_environment_interpreter_extension_free(const struct sieve_extension *ext,
49
             struct sieve_interpreter *interp,
50
             void *context);
51
52
struct sieve_interpreter_extension environment_interpreter_extension = {
53
  .ext_def = &environment_extension,
54
  .free = ext_environment_interpreter_extension_free,
55
};
56
57
static struct ext_environment_interpreter_context *
58
ext_environment_interpreter_context_create(
59
  const struct sieve_extension *this_ext,
60
  struct sieve_interpreter *interp)
61
0
{
62
0
  pool_t pool = sieve_interpreter_pool(interp);
63
0
  struct ext_environment_interpreter_context *ctx;
64
65
0
  ctx = p_new(pool, struct ext_environment_interpreter_context, 1);
66
67
0
  hash_table_create(&ctx->name_items, default_pool, 0, str_hash, strcmp);
68
0
  i_array_init(&ctx->prefix_items, 16);
69
70
0
  sieve_interpreter_extension_register(interp, this_ext,
71
0
               &environment_interpreter_extension,
72
0
               ctx);
73
0
  return ctx;
74
0
}
75
76
static void
77
ext_environment_interpreter_extension_free(
78
  const struct sieve_extension *ext ATTR_UNUSED,
79
  struct sieve_interpreter *interp ATTR_UNUSED, void *context)
80
0
{
81
0
  struct ext_environment_interpreter_context *ctx =
82
0
    (struct ext_environment_interpreter_context *)context;
83
84
0
  hash_table_destroy(&ctx->name_items);
85
0
  array_free(&ctx->prefix_items);
86
0
}
87
88
static struct ext_environment_interpreter_context *
89
ext_environment_interpreter_context_get(const struct sieve_extension *this_ext,
90
          struct sieve_interpreter *interp)
91
0
{
92
0
  struct ext_environment_interpreter_context *ctx =
93
0
    (struct ext_environment_interpreter_context *)
94
0
    sieve_interpreter_extension_get_context(interp, this_ext);
95
96
0
  if (ctx == NULL) {
97
0
    ctx = ext_environment_interpreter_context_create(
98
0
      this_ext, interp);
99
0
  }
100
0
  return ctx;
101
0
}
102
103
void ext_environment_interpreter_init(const struct sieve_extension *this_ext,
104
              struct sieve_interpreter *interp)
105
0
{
106
0
  struct ext_environment_interpreter_context *ctx;
107
0
  unsigned int i;
108
109
  /* Create our context */
110
0
  ctx = ext_environment_interpreter_context_get(this_ext, interp);
111
112
0
  for (i = 0; i < core_env_items_count; i++) {
113
0
    sieve_environment_item_insert(ctx, interp, this_ext,
114
0
                core_env_items[i]);
115
0
  }
116
117
0
  ctx->active = TRUE;
118
0
}
119
120
bool sieve_ext_environment_is_active(const struct sieve_extension *env_ext,
121
             struct sieve_interpreter *interp)
122
0
{
123
0
  struct ext_environment_interpreter_context *ctx =
124
0
    ext_environment_interpreter_context_get(env_ext, interp);
125
126
0
  return (ctx != NULL && ctx->active);
127
0
}
128
129
/*
130
 * Registration
131
 */
132
133
static void
134
sieve_environment_item_insert(
135
  struct ext_environment_interpreter_context *ctx,
136
  struct sieve_interpreter *interp, const struct sieve_extension *ext,
137
  const struct sieve_environment_item_def *item_def)
138
0
{
139
0
  pool_t pool = sieve_interpreter_pool(interp);
140
0
  struct sieve_environment_item *item_mod;
141
142
0
  item_mod = p_new(pool, struct sieve_environment_item, 1);
143
0
  item_mod->def = item_def;
144
0
  item_mod->ext = ext;
145
146
0
  const struct sieve_environment_item *item = item_mod;
147
0
  if (!item_def->prefix)
148
0
    hash_table_insert(ctx->name_items, item_def->name, item);
149
0
  else
150
0
    array_append(&ctx->prefix_items, &item, 1);
151
0
}
152
153
void sieve_environment_item_register(
154
  const struct sieve_extension *env_ext, struct sieve_interpreter *interp,
155
  const struct sieve_extension *ext,
156
  const struct sieve_environment_item_def *item_def)
157
0
{
158
0
  struct ext_environment_interpreter_context *ctx;
159
160
0
  i_assert(sieve_extension_is(env_ext, environment_extension));
161
0
  ctx = ext_environment_interpreter_context_get(env_ext, interp);
162
163
0
  sieve_environment_item_insert(ctx, interp, ext, item_def);
164
0
}
165
166
/*
167
 * Retrieval
168
 */
169
170
static const struct sieve_environment_item *
171
ext_environment_item_lookup(struct ext_environment_interpreter_context *ctx,
172
          const char **_name)
173
0
{
174
0
  const struct sieve_environment_item *item;
175
0
  const char *suffix, *name = *_name;
176
177
0
  item = hash_table_lookup(ctx->name_items, name);
178
0
  if (item != NULL)
179
0
    return item;
180
181
0
  array_foreach_elem(&ctx->prefix_items, item) {
182
0
    i_assert(item->def->prefix);
183
184
0
    if (str_begins(name, item->def->name, &suffix)) {
185
0
      if (*suffix == '.')
186
0
        ++suffix;
187
188
0
      *_name = suffix;
189
0
      return item;
190
0
    }
191
0
  }
192
0
  return NULL;
193
0
}
194
195
const char *
196
ext_environment_item_get_value(const struct sieve_extension *env_ext,
197
             const struct sieve_runtime_env *renv,
198
             const char *name)
199
0
{
200
0
  struct ext_environment_interpreter_context *ctx;
201
0
  const struct sieve_environment_item *item;
202
203
0
  i_assert(sieve_extension_is(env_ext, environment_extension));
204
0
  ctx = ext_environment_interpreter_context_get(env_ext, renv->interp);
205
206
0
  item = ext_environment_item_lookup(ctx, &name);
207
0
  if (item == NULL)
208
0
    return NULL;
209
210
0
  i_assert(item->def != NULL);
211
0
  if (item->def->value != NULL)
212
0
    return item->def->value;
213
0
  if (item->def->get_value != NULL)
214
0
    return item->def->get_value(renv, item, name);
215
0
  return NULL;
216
0
}
217
218
/*
219
 * Default environment items
220
 */
221
222
/* "domain":
223
224
     The primary DNS domain associated with the Sieve execution context, usually
225
     but not always a proper suffix of the host name.
226
 */
227
228
static const char *
229
envit_domain_get_value(const struct sieve_runtime_env *renv,
230
           const struct sieve_environment_item *item ATTR_UNUSED,
231
           const char *name ATTR_UNUSED)
232
0
{
233
0
  const struct sieve_execute_env *eenv = renv->exec_env;
234
235
0
  return eenv->svinst->domainname;
236
0
}
237
238
const struct sieve_environment_item_def domain_env_item = {
239
  .name = "domain",
240
  .get_value = envit_domain_get_value,
241
};
242
243
/* "host":
244
245
     The fully-qualified domain name of the host where the Sieve script is
246
     executing.
247
 */
248
249
static const char *
250
envit_host_get_value(const struct sieve_runtime_env *renv,
251
         const struct sieve_environment_item *item ATTR_UNUSED,
252
         const char *name ATTR_UNUSED)
253
0
{
254
0
  const struct sieve_execute_env *eenv = renv->exec_env;
255
256
0
  return eenv->svinst->hostname;
257
0
}
258
259
const struct sieve_environment_item_def host_env_item = {
260
  .name = "host",
261
  .get_value = envit_host_get_value,
262
};
263
264
/* "location":
265
266
     Sieve evaluation can be performed at various different points as messages
267
     are processed. This item provides additional information about the type of
268
     service that is evaluating the script.  Possible values are:
269
      "MTA" - the Sieve script is being evaluated by a Message Transfer Agent
270
      "MDA" - evaluation is being performed by a Mail Delivery Agent
271
      "MUA" - evaluation is being performed by a Mail User Agent (right...)
272
      "MS"  - evaluation is being performed by a Message Store
273
 */
274
275
static const char *
276
envit_location_get_value(const struct sieve_runtime_env *renv,
277
       const struct sieve_environment_item *item ATTR_UNUSED,
278
       const char *name ATTR_UNUSED)
279
0
{
280
0
  const struct sieve_execute_env *eenv = renv->exec_env;
281
282
0
  switch (eenv->svinst->env_location ) {
283
0
  case SIEVE_ENV_LOCATION_MDA:
284
0
    return "MDA";
285
0
  case SIEVE_ENV_LOCATION_MTA:
286
0
    return "MTA";
287
0
  case SIEVE_ENV_LOCATION_MS:
288
0
    return "MS";
289
0
  default:
290
0
    break;
291
0
  }
292
0
  return NULL;
293
0
}
294
295
const struct sieve_environment_item_def location_env_item = {
296
  .name = "location",
297
  .get_value = envit_location_get_value
298
};
299
300
/* "phase":
301
302
     The point relative to final delivery where the Sieve script is being
303
     evaluated.  Possible values are "pre", "during", and "post", referring
304
     respectively to processing before, during, and after final delivery has
305
     taken place.
306
 */
307
308
static const char *
309
envit_phase_get_value(const struct sieve_runtime_env *renv,
310
          const struct sieve_environment_item *item ATTR_UNUSED,
311
          const char *name ATTR_UNUSED)
312
0
{
313
0
  const struct sieve_execute_env *eenv = renv->exec_env;
314
315
0
  switch (eenv->svinst->delivery_phase) {
316
0
  case SIEVE_DELIVERY_PHASE_PRE:
317
0
    return "pre";
318
0
  case SIEVE_DELIVERY_PHASE_DURING:
319
0
    return "during";
320
0
  case SIEVE_DELIVERY_PHASE_POST:
321
0
    return "post";
322
0
  default:
323
0
    break;
324
0
  }
325
0
  return NULL;
326
0
}
327
328
const struct sieve_environment_item_def phase_env_item = {
329
  .name = "phase",
330
  .get_value = envit_phase_get_value
331
};
332
333
/* "name":
334
335
    The product name associated with the Sieve interpreter.
336
 */
337
338
const struct sieve_environment_item_def name_env_item = {
339
  .name = "name",
340
  .value = PIGEONHOLE_NAME" Sieve"
341
};
342
343
/* "version":
344
345
   The product version associated with the Sieve interpreter. The meaning of the
346
   product version string is product-specific and should always be considered
347
   in the context of the product name given by the "name" item.
348
 */
349
350
const struct sieve_environment_item_def version_env_item = {
351
  .name = "version",
352
  .value = PIGEONHOLE_VERSION,
353
};