Coverage Report

Created: 2026-08-08 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-operands.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "hash.h"
5
#include "str.h"
6
#include "array.h"
7
8
#include "sieve-common.h"
9
#include "sieve-extensions.h"
10
#include "sieve-ast.h"
11
#include "sieve-binary.h"
12
#include "sieve-code.h"
13
#include "sieve-match-types.h"
14
#include "sieve-commands.h"
15
#include "sieve-validator.h"
16
#include "sieve-generator.h"
17
#include "sieve-dump.h"
18
#include "sieve-interpreter.h"
19
20
#include "ext-variables-common.h"
21
#include "ext-variables-limits.h"
22
#include "ext-variables-name.h"
23
#include "ext-variables-dump.h"
24
#include "ext-variables-operands.h"
25
26
/*
27
 * Variable operand
28
 */
29
30
static bool
31
opr_variable_dump(const struct sieve_dumptime_env *denv,
32
      const struct sieve_operand *oprnd, sieve_size_t *address);
33
static int
34
opr_variable_read_value(const struct sieve_runtime_env *renv,
35
      const struct sieve_operand *oprnd,
36
      sieve_size_t *address, string_t **str_r);
37
38
const struct sieve_opr_string_interface variable_interface = {
39
  opr_variable_dump,
40
  opr_variable_read_value,
41
};
42
43
const struct sieve_operand_def variable_operand = {
44
  .name = "variable",
45
  .ext_def = &variables_extension,
46
  .code = EXT_VARIABLES_OPERAND_VARIABLE,
47
  .class = &string_class,
48
  .interface = &variable_interface,
49
};
50
51
void sieve_variables_opr_variable_emit(struct sieve_binary_block *sblock,
52
               const struct sieve_extension *var_ext,
53
               struct sieve_variable *var)
54
0
{
55
0
  i_assert(sieve_extension_is(var_ext, variables_extension));
56
57
0
  if (var->ext == NULL) {
58
    /* Default variable storage */
59
0
    (void)sieve_operand_emit(sblock, var_ext, &variable_operand);
60
0
    (void)sieve_binary_emit_byte(sblock, 0); /* Default */
61
0
    (void)sieve_binary_emit_unsigned(sblock, var->index);
62
0
    return;
63
0
  }
64
65
0
  (void)sieve_operand_emit(sblock, var_ext, &variable_operand);
66
0
  (void)sieve_binary_emit_extension(sblock, var->ext, 1); /* Extension */
67
0
  (void)sieve_binary_emit_unsigned(sblock, var->index);
68
0
}
69
70
static bool
71
opr_variable_dump(const struct sieve_dumptime_env *denv,
72
      const struct sieve_operand *oprnd, sieve_size_t *address)
73
0
{
74
0
  const struct sieve_extension *this_ext = oprnd->ext;
75
0
  unsigned int index = 0;
76
0
  const struct sieve_extension *ext;
77
0
  unsigned int code = 1; /* Initially set to offset value */
78
0
  const char *identifier;
79
80
0
  if (!sieve_binary_read_extension(denv->sblock, address, &code, &ext))
81
0
    return FALSE;
82
0
  if (!sieve_binary_read_unsigned(denv->sblock, address, &index))
83
0
    return FALSE;
84
85
0
  identifier = ext_variables_dump_get_identifier(this_ext, denv,
86
0
                   ext, index);
87
0
  identifier = (identifier == NULL ? "??" : identifier);
88
89
0
  if (oprnd->field_name != NULL) {
90
0
    sieve_code_dumpf(denv, "%s: VAR[%s] ${%s}",
91
0
         oprnd->field_name,
92
0
         sieve_ext_variables_get_varid(ext, index),
93
0
         identifier);
94
0
  } else {
95
0
    sieve_code_dumpf(denv, "VAR[%s] ${%s}",
96
0
         sieve_ext_variables_get_varid(ext, index),
97
0
         identifier);
98
0
  }
99
0
  return TRUE;
100
0
}
101
102
static int
103
opr_variable_read_value(const struct sieve_runtime_env *renv,
104
      const struct sieve_operand *oprnd,
105
      sieve_size_t *address, string_t **str_r)
106
0
{
107
0
  const struct sieve_extension *this_ext = oprnd->ext;
108
0
  const struct sieve_extension *ext;
109
0
  unsigned int code = 1; /* Initially set to offset value */
110
0
  struct sieve_variable_storage *storage;
111
0
  unsigned int index = 0;
112
113
0
  if (!sieve_binary_read_extension(renv->sblock, address, &code, &ext)) {
114
0
    sieve_runtime_trace_operand_error(
115
0
      renv, oprnd, "variable operand corrupt: "
116
0
      "invalid extension byte");
117
0
    return SIEVE_EXEC_BIN_CORRUPT;
118
0
  }
119
120
0
  storage = sieve_ext_variables_runtime_get_storage(this_ext, renv, ext);
121
0
  if (storage == NULL) {
122
0
    sieve_runtime_trace_operand_error(
123
0
      renv, oprnd, "variable operand corrupt: "
124
0
      "extension has no storage");
125
0
    return SIEVE_EXEC_BIN_CORRUPT;
126
0
  }
127
128
0
  if (sieve_binary_read_unsigned(renv->sblock, address, &index)) {
129
    /* Parameter str can be NULL if we are requested to only skip
130
       and not actually read the argument.
131
     */
132
0
    if (str_r != NULL) {
133
0
      if (!sieve_variable_get(storage, index, str_r))
134
0
        return SIEVE_EXEC_FAILURE;
135
136
0
      if (*str_r == NULL)
137
0
        *str_r = t_str_new(0);
138
0
    }
139
0
    return SIEVE_EXEC_OK;
140
0
  }
141
142
0
  sieve_runtime_trace_operand_error(
143
0
    renv, oprnd, "variable operand corrupt: "
144
0
    "invalid variable index");
145
0
  return SIEVE_EXEC_BIN_CORRUPT;
146
0
}
147
148
int sieve_variable_operand_read_data(
149
  const struct sieve_runtime_env *renv, struct sieve_operand *oprnd,
150
  sieve_size_t *address, const char *field_name,
151
  struct sieve_variable_storage **storage_r, unsigned int *var_index_r)
152
0
{
153
0
  const struct sieve_extension *ext;
154
0
  unsigned int code = 1; /* Initially set to offset value */
155
0
  unsigned int idx = 0;
156
157
0
  oprnd->field_name = field_name;
158
159
0
  if (!sieve_operand_is_variable(oprnd)) {
160
0
    sieve_runtime_trace_operand_error(
161
0
      renv, oprnd, "expected variable operand but found %s",
162
0
      sieve_operand_name(oprnd));
163
0
    return SIEVE_EXEC_BIN_CORRUPT;
164
0
  }
165
0
  if (!sieve_binary_read_extension(renv->sblock, address, &code, &ext)) {
166
0
    sieve_runtime_trace_operand_error(
167
0
      renv, oprnd, "variable operand corrupt: "
168
0
      "invalid extension byte");
169
0
    return SIEVE_EXEC_BIN_CORRUPT;
170
0
  }
171
172
0
  *storage_r = sieve_ext_variables_runtime_get_storage(
173
0
    oprnd->ext, renv, ext);
174
0
  if (*storage_r == NULL) {
175
0
    sieve_runtime_trace_operand_error(
176
0
      renv, oprnd, "variable operand corrupt: "
177
0
      "extension has no storage");
178
0
    return SIEVE_EXEC_BIN_CORRUPT;
179
0
  }
180
0
  if (!sieve_binary_read_unsigned(renv->sblock, address, &idx)) {
181
0
    sieve_runtime_trace_operand_error(
182
0
      renv, oprnd, "variable operand corrupt: "
183
0
      "invalid variable index");
184
0
    return SIEVE_EXEC_BIN_CORRUPT;
185
0
  }
186
187
0
  *var_index_r = idx;
188
0
  return SIEVE_EXEC_OK;
189
0
}
190
191
int sieve_variable_operand_read(const struct sieve_runtime_env *renv,
192
        sieve_size_t *address, const char *field_name,
193
        struct sieve_variable_storage **storage_r,
194
        unsigned int *var_index_r)
195
0
{
196
0
  struct sieve_operand operand;
197
0
  int ret;
198
199
0
  ret = sieve_operand_runtime_read(renv, address, field_name, &operand);
200
0
  if (ret <= 0)
201
0
    return ret;
202
203
0
  return sieve_variable_operand_read_data(
204
0
    renv, &operand, address, field_name, storage_r, var_index_r);
205
0
}
206
207
/*
208
 * Match value operand
209
 */
210
211
static bool
212
opr_match_value_dump(const struct sieve_dumptime_env *denv,
213
         const struct sieve_operand *oprnd, sieve_size_t *address);
214
static int
215
opr_match_value_read(const struct sieve_runtime_env *renv,
216
         const struct sieve_operand *oprnd, sieve_size_t *address,
217
         string_t **str_r);
218
219
const struct sieve_opr_string_interface match_value_interface = {
220
  opr_match_value_dump,
221
  opr_match_value_read,
222
};
223
224
const struct sieve_operand_def match_value_operand = {
225
  .name = "match-value",
226
  .ext_def = &variables_extension,
227
  .code = EXT_VARIABLES_OPERAND_MATCH_VALUE,
228
  .class = &string_class,
229
  .interface = &match_value_interface,
230
};
231
232
void sieve_variables_opr_match_value_emit(struct sieve_binary_block *sblock,
233
            const struct sieve_extension *var_ext,
234
            unsigned int index)
235
0
{
236
0
  i_assert(sieve_extension_is(var_ext, variables_extension));
237
0
  (void)sieve_operand_emit(sblock, var_ext, &match_value_operand);
238
0
  (void)sieve_binary_emit_unsigned(sblock, index);
239
0
}
240
241
static bool
242
opr_match_value_dump(const struct sieve_dumptime_env *denv,
243
         const struct sieve_operand *oprnd, sieve_size_t *address)
244
0
{
245
0
  unsigned int index = 0;
246
247
0
  if (sieve_binary_read_unsigned(denv->sblock, address, &index)) {
248
0
    if (oprnd->field_name != NULL) {
249
0
      sieve_code_dumpf(denv, "%s: MATCHVAL %lu",
250
0
           oprnd->field_name,
251
0
           (unsigned long)index);
252
0
    } else {
253
0
      sieve_code_dumpf(denv, "MATCHVAL %lu",
254
0
           (unsigned long)index);
255
0
    }
256
0
    return TRUE;
257
0
  }
258
259
0
  return FALSE;
260
0
}
261
262
static int
263
opr_match_value_read(const struct sieve_runtime_env *renv,
264
         const struct sieve_operand *oprnd, sieve_size_t *address,
265
         string_t **str_r)
266
0
{
267
0
  const struct sieve_extension *this_ext = oprnd->ext;
268
0
  const struct ext_variables_context *extctx =
269
0
    ext_variables_get_context(this_ext);
270
0
  unsigned int index = 0;
271
272
0
  if (sieve_binary_read_unsigned(renv->sblock, address, &index)) {
273
    /* Parameter str can be NULL if we are requested to only skip
274
       and not actually read the argument. */
275
0
    if (str_r != NULL) {
276
0
      sieve_match_values_get(renv, index, str_r);
277
278
0
      if (*str_r == NULL)
279
0
        *str_r = t_str_new(0);
280
0
      else if (str_len(*str_r) >
281
0
         extctx->set->max_value_size) {
282
0
        str_truncate_utf8(
283
0
          *str_r, extctx->set->max_value_size);
284
0
      }
285
0
    }
286
0
    return SIEVE_EXEC_OK;
287
0
  }
288
289
0
  sieve_runtime_trace_operand_error(
290
0
    renv, oprnd, "match value operand corrupt: invalid index data");
291
0
  return SIEVE_EXEC_BIN_CORRUPT;
292
0
}