Coverage Report

Created: 2026-07-30 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/include/cmd-global.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
5
#include "sieve-common.h"
6
#include "sieve-code.h"
7
#include "sieve-commands.h"
8
#include "sieve-validator.h"
9
#include "sieve-generator.h"
10
#include "sieve-binary.h"
11
#include "sieve-interpreter.h"
12
#include "sieve-dump.h"
13
14
#include "sieve-ext-variables.h"
15
16
#include "ext-include-common.h"
17
#include "ext-include-binary.h"
18
#include "ext-include-variables.h"
19
20
/*
21
 * Commands
22
 */
23
24
static bool
25
cmd_global_validate(struct sieve_validator *valdtr, struct sieve_command *cmd);
26
static bool
27
cmd_global_generate(const struct sieve_codegen_env *cgenv,
28
        struct sieve_command *cmd);
29
30
const struct sieve_command_def cmd_global = {
31
  .identifier = "global",
32
  .type = SCT_COMMAND,
33
  .positional_args = 1,
34
  .subtests = 0,
35
  .block_allowed = FALSE,
36
  .block_required = FALSE,
37
  .validate = cmd_global_validate,
38
  .generate = cmd_global_generate,
39
};
40
41
/*
42
 * Operations
43
 */
44
45
static bool
46
opc_global_dump(const struct sieve_dumptime_env *denv, sieve_size_t *address);
47
static int
48
opc_global_execute(const struct sieve_runtime_env *renv, sieve_size_t *address);
49
50
/* Global operation */
51
52
const struct sieve_operation_def global_operation = {
53
  .mnemonic = "GLOBAL",
54
  .ext_def = &include_extension,
55
  .code = EXT_INCLUDE_OPERATION_GLOBAL,
56
  .dump = opc_global_dump,
57
  .execute = opc_global_execute,
58
};
59
60
/*
61
 * Validation
62
 */
63
64
static inline struct sieve_argument *
65
_create_variable_argument(struct sieve_command *cmd,
66
        struct sieve_variable *var)
67
0
{
68
0
  struct sieve_argument *argument =
69
0
    sieve_argument_create(cmd->ast_node->ast, NULL, cmd->ext, 0);
70
71
0
  argument->data = var;
72
0
  return argument;
73
0
}
74
75
static bool
76
cmd_global_validate(struct sieve_validator *valdtr, struct sieve_command *cmd)
77
0
{
78
0
  const struct sieve_extension *this_ext = cmd->ext;
79
0
  struct sieve_ast_argument *arg = cmd->first_positional;
80
0
  struct sieve_command *prev = sieve_command_prev(cmd);
81
82
  /* Check for use of variables extension */
83
0
  if (!ext_include_validator_have_variables(this_ext, valdtr)) {
84
0
    sieve_command_validate_error(
85
0
      valdtr, cmd,
86
0
      "%s command requires that variables extension is active",
87
0
      sieve_command_identifier(cmd));
88
0
    return FALSE;
89
0
  }
90
91
  /* Register global variable */
92
0
  if (sieve_ast_argument_type(arg) == SAAT_STRING) {
93
    /* Single string */
94
0
    const char *identifier = sieve_ast_argument_strc(arg);
95
0
    struct sieve_variable *var;
96
97
0
    var = ext_include_variable_import_global(valdtr, cmd,
98
0
               identifier);
99
0
    if (var == NULL)
100
0
      return FALSE;
101
102
0
    arg->argument = _create_variable_argument(cmd, var);
103
0
  } else if (sieve_ast_argument_type(arg) == SAAT_STRING_LIST) {
104
    /* String list */
105
0
    struct sieve_ast_argument *stritem =
106
0
      sieve_ast_strlist_first(arg);
107
108
0
    while (stritem != NULL) {
109
0
      const char *identifier =
110
0
        sieve_ast_argument_strc(stritem);
111
0
      struct sieve_variable *var;
112
113
0
      var = ext_include_variable_import_global(valdtr, cmd,
114
0
                 identifier);
115
0
      if (var == NULL)
116
0
        return FALSE;
117
118
0
      stritem->argument = _create_variable_argument(cmd, var);
119
0
      stritem = sieve_ast_strlist_next(stritem);
120
0
    }
121
0
  } else {
122
    /* Something else */
123
0
    sieve_argument_validate_error(
124
0
      valdtr, arg,
125
0
      "the %s command accepts a single string or string list argument, "
126
0
      "but %s was found", sieve_command_identifier(cmd),
127
0
      sieve_ast_argument_name(arg));
128
0
    return FALSE;
129
0
  }
130
131
  /* Join global commands with predecessors if possible */
132
0
  if (sieve_commands_equal(prev, cmd) &&
133
0
      !sieve_validator_failed(valdtr)) {
134
    /* Join this command's string list with the previous one */
135
0
    prev->first_positional = sieve_ast_stringlist_join(
136
0
      prev->first_positional, cmd->first_positional);
137
138
0
    if (prev->first_positional == NULL) {
139
      /* Not going to happen unless MAXINT stringlist items
140
         are specified */
141
0
      sieve_command_validate_error(
142
0
        valdtr, cmd, "compiler reached AST limit "
143
0
        "(script too complex)");
144
0
      return FALSE;
145
0
    }
146
147
    /* Detach this command node */
148
0
    sieve_ast_node_detach(cmd->ast_node);
149
0
  }
150
151
0
  return TRUE;
152
0
}
153
154
/*
155
 * Code generation
156
 */
157
158
static bool
159
cmd_global_generate(const struct sieve_codegen_env *cgenv,
160
        struct sieve_command *cmd)
161
0
{
162
0
  struct sieve_ast_argument *arg = cmd->first_positional;
163
164
0
  sieve_operation_emit(cgenv->sblock, cmd->ext, &global_operation);
165
166
0
  if (sieve_ast_argument_type(arg) == SAAT_STRING) {
167
    /* Single string */
168
0
    struct sieve_variable *var =
169
0
      (struct sieve_variable *)arg->argument->data;
170
171
0
    (void)sieve_binary_emit_unsigned(cgenv->sblock, 1);
172
0
    (void)sieve_binary_emit_unsigned(cgenv->sblock, var->index);
173
0
  } else if (sieve_ast_argument_type(arg) == SAAT_STRING_LIST) {
174
    /* String list */
175
0
    struct sieve_ast_argument *stritem =
176
0
      sieve_ast_strlist_first(arg);
177
178
0
    (void)sieve_binary_emit_unsigned(cgenv->sblock,
179
0
             sieve_ast_strlist_count(arg));
180
181
0
    while (stritem != NULL) {
182
0
      struct sieve_variable *var = (struct sieve_variable *)
183
0
        stritem->argument->data;
184
185
0
      (void)sieve_binary_emit_unsigned(cgenv->sblock,
186
0
               var->index);
187
0
      stritem = sieve_ast_strlist_next(stritem);
188
0
    }
189
0
  } else {
190
0
    i_unreached();
191
0
  }
192
0
  return TRUE;
193
0
}
194
195
/*
196
 * Code dump
197
 */
198
199
static bool
200
opc_global_dump(const struct sieve_dumptime_env *denv, sieve_size_t *address)
201
0
{
202
0
  const struct sieve_extension *this_ext = denv->oprtn->ext;
203
0
  unsigned int count, i, var_count;
204
0
  struct sieve_variable_scope_binary *global_vars;
205
0
  struct sieve_variable_scope *global_scope;
206
0
  struct sieve_variable *const *vars;
207
208
0
  if (!sieve_binary_read_unsigned(denv->sblock, address, &count))
209
0
    return FALSE;
210
211
0
  sieve_code_dumpf(denv, "GLOBAL (count: %u):", count);
212
213
0
  global_vars = ext_include_binary_get_global_scope(this_ext, denv->sbin);
214
0
  global_scope = sieve_variable_scope_binary_get(global_vars);
215
0
  vars = sieve_variable_scope_get_variables(global_scope, &var_count);
216
217
0
  sieve_code_descend(denv);
218
219
0
  for (i = 0; i < count; i++) {
220
0
    unsigned int index;
221
222
0
    sieve_code_mark(denv);
223
0
    if (!sieve_binary_read_unsigned(denv->sblock, address,
224
0
            &index) ||
225
0
        index >= var_count)
226
0
      return FALSE;
227
228
0
    sieve_code_dumpf(denv, "%d: VAR[%d]: '%s'",
229
0
         i, index, vars[index]->identifier);
230
0
  }
231
232
0
  return TRUE;
233
0
}
234
235
/*
236
 * Execution
237
 */
238
239
static int
240
opc_global_execute(const struct sieve_runtime_env *renv, sieve_size_t *address)
241
0
{
242
0
  const struct sieve_extension *this_ext = renv->oprtn->ext;
243
0
  struct sieve_variable_scope_binary *global_vars;
244
0
  struct sieve_variable_scope *global_scope;
245
0
  struct sieve_variable_storage *storage;
246
0
  struct sieve_variable *const *vars;
247
0
  unsigned int var_count, count, i;
248
249
0
  if (!sieve_binary_read_unsigned(renv->sblock, address, &count)) {
250
0
    sieve_runtime_trace_error(
251
0
      renv, "global: count operand invalid");
252
0
    return SIEVE_EXEC_BIN_CORRUPT;
253
0
  }
254
255
0
  global_vars = ext_include_binary_get_global_scope(this_ext, renv->sbin);
256
0
  global_scope = sieve_variable_scope_binary_get(global_vars);
257
0
  vars = sieve_variable_scope_get_variables(global_scope, &var_count);
258
0
  storage = ext_include_interpreter_get_global_variables(this_ext,
259
0
                     renv->interp);
260
261
0
  for (i = 0; i < count; i++) {
262
0
    unsigned int index;
263
264
0
    if (!sieve_binary_read_unsigned(renv->sblock, address,
265
0
            &index)) {
266
0
      sieve_runtime_trace_error(
267
0
        renv, "global: variable index operand invalid");
268
0
      return SIEVE_EXEC_BIN_CORRUPT;
269
0
    }
270
271
0
    if (index >= var_count) {
272
0
      sieve_runtime_trace_error(
273
0
        renv, "global: "
274
0
        "variable index %u is invalid in global storage "
275
0
        "(> %u)", index, var_count);
276
0
      return SIEVE_EXEC_BIN_CORRUPT;
277
0
    }
278
279
0
    sieve_runtime_trace(
280
0
      renv, SIEVE_TRLVL_COMMANDS,
281
0
      "global: exporting variable '%s' [gvid: %u, vid: %u]",
282
0
      vars[index]->identifier, i, index);
283
284
    /* Make sure variable is initialized (export) */
285
0
    (void)sieve_variable_get_modifiable(storage, index, NULL);
286
0
  }
287
288
0
  return SIEVE_EXEC_OK;
289
0
}