Coverage Report

Created: 2026-04-27 06:39

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