Coverage Report

Created: 2026-06-15 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/include/cmd-include.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2
 */
3
4
#include "lib.h"
5
#include "str-sanitize.h"
6
7
#include "sieve-common.h"
8
#include "sieve-script.h"
9
#include "sieve-ast.h"
10
#include "sieve-code.h"
11
#include "sieve-extensions.h"
12
#include "sieve-commands.h"
13
#include "sieve-validator.h"
14
#include "sieve-binary.h"
15
#include "sieve-generator.h"
16
#include "sieve-interpreter.h"
17
#include "sieve-dump.h"
18
19
#include "ext-include-common.h"
20
#include "ext-include-binary.h"
21
22
/*
23
 * Include command
24
 *
25
 * Syntax:
26
 *   include [LOCATION] [":once"] [":optional"] <value: string>
27
 *
28
 * [LOCATION]:
29
 *   ":personal" / ":global"
30
 */
31
32
static bool
33
cmd_include_registered(struct sieve_validator *valdtr,
34
           const struct sieve_extension *ext,
35
           struct sieve_command_registration *cmd_reg);
36
static bool
37
cmd_include_pre_validate(struct sieve_validator *valdtr ATTR_UNUSED,
38
       struct sieve_command *cmd);
39
static bool
40
cmd_include_validate(struct sieve_validator *valdtr,
41
         struct sieve_command *cmd);
42
static bool
43
cmd_include_generate(const struct sieve_codegen_env *cgenv,
44
         struct sieve_command *ctx);
45
46
const struct sieve_command_def cmd_include = {
47
  .identifier = "include",
48
  .type = SCT_COMMAND,
49
  .positional_args = 1,
50
  .subtests = 0,
51
  .block_allowed = FALSE,
52
  .block_required = FALSE,
53
  .registered = cmd_include_registered,
54
  .pre_validate = cmd_include_pre_validate,
55
  .validate = cmd_include_validate,
56
  .generate = cmd_include_generate,
57
};
58
59
/*
60
 * Include operation
61
 */
62
63
static bool
64
opc_include_dump(const struct sieve_dumptime_env *denv, sieve_size_t *address);
65
static int
66
opc_include_execute(const struct sieve_runtime_env *renv,
67
        sieve_size_t *address);
68
69
const struct sieve_operation_def include_operation = {
70
  .mnemonic = "include",
71
  .ext_def = &include_extension,
72
  .code = EXT_INCLUDE_OPERATION_INCLUDE,
73
  .dump = opc_include_dump,
74
  .execute = opc_include_execute,
75
};
76
77
/*
78
 * Context structures
79
 */
80
81
struct cmd_include_context_data {
82
  enum ext_include_script_location location;
83
  const char *script_name;
84
  struct sieve_script *script;
85
  enum ext_include_flags flags;
86
87
  bool location_assigned:1;
88
};
89
90
/*
91
 * Tagged arguments
92
 */
93
94
static bool
95
cmd_include_validate_location_tag(struct sieve_validator *valdtr,
96
          struct sieve_ast_argument **arg,
97
          struct sieve_command *cmd);
98
99
static const struct sieve_argument_def include_personal_tag = {
100
  .identifier = "personal",
101
  .validate = cmd_include_validate_location_tag,
102
};
103
104
static const struct sieve_argument_def include_global_tag = {
105
  .identifier = "global",
106
  .validate = cmd_include_validate_location_tag,
107
};
108
109
static bool
110
cmd_include_validate_boolean_tag(struct sieve_validator *valdtr,
111
         struct sieve_ast_argument **arg,
112
         struct sieve_command *cmd);
113
114
static const struct sieve_argument_def include_once_tag = {
115
  .identifier = "once",
116
  .validate = cmd_include_validate_boolean_tag,
117
};
118
119
static const struct sieve_argument_def include_optional_tag = {
120
  .identifier = "optional",
121
  .validate = cmd_include_validate_boolean_tag,
122
};
123
124
/*
125
 * Tag validation
126
 */
127
128
static bool
129
cmd_include_validate_location_tag(struct sieve_validator *valdtr,
130
          struct sieve_ast_argument **arg,
131
          struct sieve_command *cmd)
132
0
{
133
0
  struct cmd_include_context_data *ctx_data =
134
0
    (struct cmd_include_context_data *)cmd->data;
135
136
0
  if (ctx_data->location_assigned) {
137
0
    sieve_argument_validate_error(
138
0
      valdtr, *arg,
139
0
      "include: cannot use location tags ':personal' and ':global' "
140
0
      "multiple times");
141
0
    return FALSE;
142
0
  }
143
144
0
  if (sieve_argument_is(*arg, include_personal_tag))
145
0
    ctx_data->location = EXT_INCLUDE_LOCATION_PERSONAL;
146
0
  else if (sieve_argument_is(*arg, include_global_tag))
147
0
    ctx_data->location = EXT_INCLUDE_LOCATION_GLOBAL;
148
0
  else
149
0
    return FALSE;
150
151
0
  ctx_data->location_assigned = TRUE;
152
153
  /* Delete this tag (for now) */
154
0
  *arg = sieve_ast_arguments_detach(*arg, 1);
155
156
0
  return TRUE;
157
0
}
158
159
static bool
160
cmd_include_validate_boolean_tag(struct sieve_validator *valdtr ATTR_UNUSED,
161
         struct sieve_ast_argument **arg,
162
         struct sieve_command *cmd)
163
0
{
164
0
  struct cmd_include_context_data *ctx_data =
165
0
    (struct cmd_include_context_data *)cmd->data;
166
167
0
  if (sieve_argument_is(*arg, include_once_tag))
168
0
    ctx_data->flags |= EXT_INCLUDE_FLAG_ONCE;
169
0
  else
170
0
    ctx_data->flags |= EXT_INCLUDE_FLAG_OPTIONAL;
171
172
  /* Delete this tag (for now) */
173
0
  *arg = sieve_ast_arguments_detach(*arg, 1);
174
175
0
  return TRUE;
176
0
}
177
178
/*
179
 * Command registration
180
 */
181
182
static bool
183
cmd_include_registered(struct sieve_validator *valdtr,
184
           const struct sieve_extension *ext,
185
           struct sieve_command_registration *cmd_reg)
186
0
{
187
0
  sieve_validator_register_tag(valdtr, cmd_reg, ext,
188
0
             &include_personal_tag, 0);
189
0
  sieve_validator_register_tag(valdtr, cmd_reg, ext,
190
0
             &include_global_tag, 0);
191
0
  sieve_validator_register_tag(valdtr, cmd_reg, ext,
192
0
             &include_once_tag, 0);
193
0
  sieve_validator_register_tag(valdtr, cmd_reg, ext,
194
0
             &include_optional_tag, 0);
195
196
0
  return TRUE;
197
0
}
198
199
/*
200
 * Command validation
201
 */
202
203
static bool
204
cmd_include_pre_validate(struct sieve_validator *valdtr ATTR_UNUSED,
205
       struct sieve_command *cmd)
206
0
{
207
0
  struct cmd_include_context_data *ctx_data;
208
209
  /* Assign context */
210
0
  ctx_data = p_new(sieve_command_pool(cmd),
211
0
       struct cmd_include_context_data, 1);
212
0
  ctx_data->location = EXT_INCLUDE_LOCATION_PERSONAL;
213
0
  cmd->data = ctx_data;
214
215
0
  return TRUE;
216
0
}
217
218
static bool
219
cmd_include_validate(struct sieve_validator *valdtr,
220
         struct sieve_command *cmd)
221
0
{
222
0
  const struct sieve_extension *this_ext = cmd->ext;
223
0
  struct sieve_ast_argument *arg = cmd->first_positional;
224
0
  struct cmd_include_context_data *ctx_data =
225
0
    (struct cmd_include_context_data *)cmd->data;
226
0
  struct sieve_script *script;
227
0
  const char *script_name;
228
0
  enum sieve_error error_code = SIEVE_ERROR_NONE;
229
230
  /* Check argument */
231
0
  if (!sieve_validate_positional_argument(valdtr, cmd, arg, "value",
232
0
            1, SAAT_STRING))
233
0
    return FALSE;
234
235
0
  if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE))
236
0
    return FALSE;
237
238
  /*
239
   * Variables are not allowed.
240
   */
241
0
  if (!sieve_argument_is_string_literal(arg)) {
242
0
    sieve_argument_validate_error(
243
0
      valdtr, arg,
244
0
      "the include command requires a constant string for its value argument");
245
0
    return FALSE;
246
0
  }
247
248
  /* Find the script */
249
250
0
  script_name = sieve_ast_argument_strc(arg);
251
252
0
  if (!sieve_script_name_is_valid(script_name)) {
253
0
    sieve_argument_validate_error(
254
0
      valdtr, arg, "include: invalid script name '%s'",
255
0
      str_sanitize(script_name, 80));
256
0
    return FALSE;
257
0
  }
258
259
  /* Open script */
260
0
  if (ext_include_open_script(this_ext, ctx_data->location,
261
0
            sieve_validator_script_cause(valdtr),
262
0
            script_name, &script, &error_code) < 0) {
263
0
    if (error_code != SIEVE_ERROR_NOT_FOUND) {
264
0
      sieve_argument_validate_error(
265
0
        valdtr, arg,
266
0
        "failed to access included %s script '%s' "
267
0
        "(refer to server log for more information)",
268
0
        ext_include_script_location_name(ctx_data->location),
269
0
        str_sanitize(script_name, 80));
270
0
      return FALSE;
271
    /* Not found */
272
0
    } else {
273
0
      enum sieve_compile_flags cpflags =
274
0
        sieve_validator_compile_flags(valdtr);
275
276
0
      if ((ctx_data->flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0) {
277
        /* :optional */
278
0
      } else if ((cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0) {
279
        /* Script is being uploaded */
280
0
        sieve_argument_validate_warning(
281
0
          valdtr, arg,
282
0
          "included %s script '%s' does not exist (ignored during upload)",
283
0
          ext_include_script_location_name(ctx_data->location),
284
0
          str_sanitize(script_name, 80));
285
0
        ctx_data->flags |= EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD;
286
287
0
      } else {
288
        /* Should have existed */
289
0
        sieve_argument_validate_error(
290
0
          valdtr, arg,
291
0
          "included %s script '%s' does not exist",
292
0
          ext_include_script_location_name(ctx_data->location),
293
0
          str_sanitize(script_name, 80));
294
0
        return FALSE;
295
0
      }
296
0
    }
297
0
  }
298
299
0
  ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script);
300
0
  ctx_data->script_name = p_strdup(sieve_command_pool(cmd), script_name);
301
0
  ctx_data->script = script;
302
303
0
  (void)sieve_ast_arguments_detach(arg, 1);
304
0
  return TRUE;
305
0
}
306
307
/*
308
 * Code Generation
309
 */
310
311
static bool
312
cmd_include_generate(const struct sieve_codegen_env *cgenv,
313
         struct sieve_command *cmd)
314
0
{
315
0
  struct cmd_include_context_data *ctx_data =
316
0
    (struct cmd_include_context_data *)cmd->data;
317
0
  const struct ext_include_script_info *included;
318
0
  int ret;
319
320
  /* Compile (if necessary) and include the script into the binary.
321
     This yields the id of the binary block containing the compiled byte
322
     code. */
323
0
  ret = ext_include_generate_include(cgenv, cmd, ctx_data->location,
324
0
             ctx_data->script_name,
325
0
             ctx_data->flags, ctx_data->script,
326
0
             &included);
327
0
  if (ret < 0)
328
0
    return FALSE;
329
0
  if (ret > 0) {
330
0
    (void)sieve_operation_emit(cgenv->sblock, cmd->ext,
331
0
             &include_operation);
332
0
    (void)sieve_binary_emit_unsigned(cgenv->sblock, included->id);
333
0
    (void)sieve_binary_emit_byte(cgenv->sblock, ctx_data->flags);
334
0
  }
335
0
  return TRUE;
336
0
}
337
338
/*
339
 * Code dump
340
 */
341
342
static bool opc_include_dump(const struct sieve_dumptime_env *denv,
343
           sieve_size_t *address)
344
0
{
345
0
  const struct ext_include_script_info *included;
346
0
  struct ext_include_binary_context *binctx;
347
0
  unsigned int include_id, flags;
348
349
0
  sieve_code_dumpf(denv, "INCLUDE:");
350
351
0
  sieve_code_mark(denv);
352
0
  if (!sieve_binary_read_unsigned(denv->sblock, address, &include_id))
353
0
    return FALSE;
354
0
  if (!sieve_binary_read_byte(denv->sblock, address, &flags))
355
0
    return FALSE;
356
357
0
  binctx = ext_include_binary_get_context(denv->oprtn->ext, denv->sbin);
358
0
  included = ext_include_binary_script_get_included(binctx, include_id);
359
0
  if (included == NULL)
360
0
    return FALSE;
361
0
  if (included->block == NULL) {
362
0
    if (!HAS_ALL_BITS(included->flags, EXT_INCLUDE_FLAG_OPTIONAL))
363
0
      return FALSE;
364
365
0
    sieve_code_descend(denv);
366
0
    sieve_code_dumpf(
367
0
      denv, "script: %s(optional) [ID: %d, BLOCK: -]",
368
0
      ((flags & EXT_INCLUDE_FLAG_ONCE) != 0 ? "(once) " : ""),
369
0
      include_id);
370
0
    return TRUE;
371
0
  }
372
373
0
  sieve_code_descend(denv);
374
0
  sieve_code_dumpf(
375
0
    denv, "script: '%s' %s%s[ID: %d, BLOCK: %d]",
376
0
    sieve_script_label(included->script),
377
0
    ((flags & EXT_INCLUDE_FLAG_ONCE) != 0 ? "(once) " : ""),
378
0
    ((flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ? "(optional) " : ""),
379
0
    include_id, sieve_binary_block_get_id(included->block));
380
381
0
  return TRUE;
382
0
}
383
384
/*
385
 * Execution
386
 */
387
388
static int
389
opc_include_execute(const struct sieve_runtime_env *renv,
390
        sieve_size_t *address)
391
0
{
392
0
  unsigned int include_id, flags;
393
394
0
  if (!sieve_binary_read_unsigned(renv->sblock, address, &include_id)) {
395
0
    sieve_runtime_trace_error(renv, "invalid include-id operand");
396
0
    return SIEVE_EXEC_BIN_CORRUPT;
397
0
  }
398
399
0
  if (!sieve_binary_read_unsigned(renv->sblock, address, &flags)) {
400
0
    sieve_runtime_trace_error(renv, "invalid flags operand");
401
0
    return SIEVE_EXEC_BIN_CORRUPT;
402
0
  }
403
404
0
  return ext_include_execute_include(renv, include_id,
405
0
             (enum ext_include_flags)flags);
406
0
}