Coverage Report

Created: 2026-10-28 07:22

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