Coverage Report

Created: 2026-03-11 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/testsuite/testsuite-substitutions.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.h"
7
#include "sieve-code.h"
8
#include "sieve-commands.h"
9
#include "sieve-binary.h"
10
#include "sieve-generator.h"
11
#include "sieve-interpreter.h"
12
#include "sieve-dump.h"
13
14
#include "testsuite-common.h"
15
#include "testsuite-substitutions.h"
16
17
/*
18
 * Forward declarations
19
 */
20
21
void testsuite_opr_substitution_emit(struct sieve_binary_block *sblock,
22
             const struct testsuite_substitution *tsub,
23
             const char *param);
24
25
/*
26
 * Testsuite substitutions
27
 */
28
29
/* FIXME: make this extendible */
30
31
enum {
32
  TESTSUITE_SUBSTITUTION_FILE,
33
};
34
35
static const struct testsuite_substitution_def testsuite_file_substitution;
36
37
static const struct testsuite_substitution_def *substitutions[] = {
38
  &testsuite_file_substitution,
39
};
40
41
static const unsigned int substitutions_count = N_ELEMENTS(substitutions);
42
43
static inline const struct testsuite_substitution_def *
44
testsuite_substitution_get(unsigned int code)
45
0
{
46
0
  if (code >= substitutions_count)
47
0
    return NULL;
48
49
0
  return substitutions[code];
50
0
}
51
52
static const struct testsuite_substitution *
53
testsuite_substitution_create(struct sieve_ast *ast, const char *identifier)
54
0
{
55
0
  unsigned int i;
56
57
0
  for (i = 0; i < substitutions_count; i++) {
58
0
    if (strcasecmp(substitutions[i]->obj_def.identifier,
59
0
             identifier) == 0) {
60
0
      const struct testsuite_substitution_def *tsub_def =
61
0
        substitutions[i];
62
0
      struct testsuite_substitution *tsub;
63
64
0
      tsub = p_new(sieve_ast_pool(ast),
65
0
             struct testsuite_substitution, 1);
66
0
      tsub->object.def = &tsub_def->obj_def;
67
0
      tsub->object.ext = testsuite_ext;
68
0
      tsub->def = tsub_def;
69
70
0
      return tsub;
71
0
    }
72
0
  }
73
74
0
  return NULL;
75
0
}
76
77
/*
78
 * Substitution argument
79
 */
80
81
static bool
82
arg_testsuite_substitution_generate(const struct sieve_codegen_env *cgenv,
83
            struct sieve_ast_argument *arg,
84
            struct sieve_command *context);
85
86
struct _testsuite_substitution_context {
87
  const struct testsuite_substitution *tsub;
88
  const char *param;
89
};
90
91
const struct sieve_argument_def testsuite_substitution_argument = {
92
  .identifier = "@testsuite-substitution",
93
  .generate = arg_testsuite_substitution_generate,
94
};
95
96
struct sieve_ast_argument *
97
testsuite_substitution_argument_create(
98
  struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast *ast,
99
  unsigned int source_line, const char *substitution, const char *param)
100
0
{
101
0
  const struct testsuite_substitution *tsub;
102
0
  struct _testsuite_substitution_context *tsctx;
103
0
  struct sieve_ast_argument *arg;
104
0
  pool_t pool;
105
106
0
  tsub = testsuite_substitution_create(ast, substitution);
107
0
  if (tsub == NULL)
108
0
    return NULL;
109
110
0
  arg = sieve_ast_argument_create(ast, source_line);
111
0
  arg->type = SAAT_STRING;
112
113
0
  pool = sieve_ast_pool(ast);
114
0
  tsctx = p_new(pool, struct _testsuite_substitution_context, 1);
115
0
  tsctx->tsub = tsub;
116
0
  tsctx->param = p_strdup(pool, param);
117
118
0
  arg->argument = sieve_argument_create
119
0
    (ast, &testsuite_substitution_argument, testsuite_ext, 0);
120
0
  arg->argument->data = tsctx;
121
0
  return arg;
122
0
}
123
124
static bool
125
arg_testsuite_substitution_generate(const struct sieve_codegen_env *cgenv,
126
            struct sieve_ast_argument *arg,
127
            struct sieve_command *context ATTR_UNUSED)
128
0
{
129
0
  struct _testsuite_substitution_context *tsctx =
130
0
    (struct _testsuite_substitution_context *)arg->argument->data;
131
132
0
  testsuite_opr_substitution_emit(cgenv->sblock, tsctx->tsub, tsctx->param);
133
0
  return TRUE;
134
0
}
135
136
/*
137
 * Substitution operand
138
 */
139
140
static bool
141
opr_substitution_dump(const struct sieve_dumptime_env *denv,
142
          const struct sieve_operand *oprnd, sieve_size_t *address);
143
static int
144
opr_substitution_read_value(const struct sieve_runtime_env *renv,
145
          const struct sieve_operand *oprnd,
146
          sieve_size_t *address, string_t **str);
147
148
const struct sieve_opr_string_interface testsuite_substitution_interface = {
149
  opr_substitution_dump,
150
  opr_substitution_read_value
151
};
152
153
const struct sieve_operand_def testsuite_substitution_operand = {
154
  .name = "test-substitution",
155
  .ext_def = &testsuite_extension,
156
  .code = TESTSUITE_OPERAND_SUBSTITUTION,
157
  .class = &string_class,
158
  .interface = &testsuite_substitution_interface
159
};
160
161
void testsuite_opr_substitution_emit(struct sieve_binary_block *sblock,
162
             const struct testsuite_substitution *tsub,
163
             const char *param)
164
0
{
165
  /* Default variable storage */
166
0
  (void)sieve_operand_emit(sblock, testsuite_ext,
167
0
         &testsuite_substitution_operand);
168
0
  (void)sieve_binary_emit_unsigned(sblock, tsub->object.def->code);
169
0
  (void)sieve_binary_emit_cstring(sblock, param);
170
0
}
171
172
static bool
173
opr_substitution_dump(const struct sieve_dumptime_env *denv,
174
          const struct sieve_operand *oprnd, sieve_size_t *address)
175
0
{
176
0
  unsigned int code = 0;
177
0
  const struct testsuite_substitution_def *tsub;
178
0
  string_t *param;
179
180
0
  if (!sieve_binary_read_unsigned(denv->sblock, address, &code))
181
0
    return FALSE;
182
183
0
  tsub = testsuite_substitution_get(code);
184
0
  if (tsub == NULL)
185
0
    return FALSE;
186
187
0
  if (!sieve_binary_read_string(denv->sblock, address, &param))
188
0
    return FALSE;
189
190
0
  if (oprnd->field_name != NULL) {
191
0
    sieve_code_dumpf(denv, "%s: TEST_SUBS %%{%s:%s}",
192
0
         oprnd->field_name, tsub->obj_def.identifier,
193
0
         str_c(param));
194
0
  } else {
195
0
    sieve_code_dumpf(denv, "TEST_SUBS %%{%s:%s}",
196
0
         tsub->obj_def.identifier, str_c(param));
197
0
  }
198
0
  return TRUE;
199
0
}
200
201
static int
202
opr_substitution_read_value(const struct sieve_runtime_env *renv,
203
          const struct sieve_operand *oprnd ATTR_UNUSED,
204
          sieve_size_t *address, string_t **str_r)
205
0
{
206
0
  const struct testsuite_substitution_def *tsub;
207
0
  unsigned int code = 0;
208
0
  string_t *param;
209
210
0
  if (!sieve_binary_read_unsigned(renv->sblock, address, &code))
211
0
    return SIEVE_EXEC_BIN_CORRUPT;
212
213
0
  tsub = testsuite_substitution_get(code);
214
0
  if (tsub == NULL)
215
0
    return SIEVE_EXEC_FAILURE;
216
217
  /* Parameter str can be NULL if we are requested to only skip and not
218
     actually read the argument.
219
   */
220
0
  if (str_r == NULL) {
221
0
    if (!sieve_binary_read_string(renv->sblock, address, NULL))
222
0
      return SIEVE_EXEC_BIN_CORRUPT;
223
0
    return SIEVE_EXEC_OK;
224
0
  }
225
226
0
  if (!sieve_binary_read_string(renv->sblock, address, &param))
227
0
    return SIEVE_EXEC_BIN_CORRUPT;
228
0
  if (!tsub->get_value(str_c(param), str_r))
229
0
    return SIEVE_EXEC_FAILURE;
230
231
0
  return SIEVE_EXEC_OK;
232
0
}
233
234
/*
235
 * Testsuite substitution definitions
236
 */
237
238
static bool
239
testsuite_file_substitution_get_value(const char *param, string_t **result);
240
241
static const struct testsuite_substitution_def
242
testsuite_file_substitution = {
243
  SIEVE_OBJECT("file", &testsuite_substitution_operand,
244
         TESTSUITE_SUBSTITUTION_FILE),
245
  .get_value = testsuite_file_substitution_get_value,
246
};
247
248
static bool
249
testsuite_file_substitution_get_value(const char *param, string_t **result)
250
0
{
251
0
  *result = t_str_new(256);
252
253
0
  str_printfa(*result, "[FILE: %s]", param);
254
0
  return TRUE;
255
0
}