Coverage Report

Created: 2026-05-30 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/testsuite/testsuite-objects.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2
 */
3
4
#include "lib.h"
5
#include "string.h"
6
#include "ostream.h"
7
#include "hash.h"
8
#include "mail-storage.h"
9
10
#include "sieve.h"
11
#include "sieve-code.h"
12
#include "sieve-commands.h"
13
#include "sieve-extensions.h"
14
#include "sieve-validator.h"
15
#include "sieve-generator.h"
16
#include "sieve-binary.h"
17
#include "sieve-dump.h"
18
19
#include "testsuite-common.h"
20
#include "testsuite-objects.h"
21
#include "testsuite-message.h"
22
23
/*
24
 * Testsuite core objects
25
 */
26
27
enum testsuite_object_code {
28
  TESTSUITE_OBJECT_MESSAGE,
29
  TESTSUITE_OBJECT_ENVELOPE
30
};
31
32
const struct testsuite_object_def *testsuite_core_objects[] = {
33
  &message_testsuite_object,
34
  &envelope_testsuite_object,
35
};
36
37
const unsigned int testsuite_core_objects_count =
38
  N_ELEMENTS(testsuite_core_objects);
39
40
/*
41
 * Testsuite object registry
42
 */
43
44
static inline struct sieve_validator_object_registry *
45
_get_object_registry(struct sieve_validator *valdtr)
46
0
{
47
0
  struct testsuite_validator_context *ctx =
48
0
    testsuite_validator_context_get(valdtr);
49
50
0
  return ctx->object_registrations;
51
0
}
52
53
void testsuite_object_register(struct sieve_validator *valdtr,
54
             const struct sieve_extension *ext,
55
             const struct testsuite_object_def *tobj_def)
56
0
{
57
0
  struct sieve_validator_object_registry *regs =
58
0
    _get_object_registry(valdtr);
59
60
0
  sieve_validator_object_registry_add(regs, ext, &tobj_def->obj_def);
61
0
}
62
63
static const struct testsuite_object *
64
testsuite_object_create(struct sieve_validator *valdtr,
65
      struct sieve_command *cmd, const char *identifier)
66
0
{
67
0
  struct sieve_validator_object_registry *regs =
68
0
    _get_object_registry(valdtr);
69
0
  struct sieve_object object;
70
0
  struct testsuite_object *tobj;
71
72
0
  if (!sieve_validator_object_registry_find(regs, identifier, &object))
73
0
    return NULL;
74
75
0
  tobj = p_new(sieve_command_pool(cmd), struct testsuite_object, 1);
76
0
  tobj->object = object;
77
0
  tobj->def = (const struct testsuite_object_def *)object.def;
78
79
0
  return tobj;
80
0
}
81
82
void testsuite_register_core_objects(struct testsuite_validator_context *ctx)
83
0
{
84
0
  struct sieve_validator_object_registry *regs =
85
0
    ctx->object_registrations;
86
0
  unsigned int i;
87
88
  /* Register core testsuite objects */
89
0
  for (i = 0; i < testsuite_core_objects_count; i++) {
90
0
    const struct testsuite_object_def *tobj_def =
91
0
      testsuite_core_objects[i];
92
93
0
    sieve_validator_object_registry_add(
94
0
      regs, testsuite_ext, &tobj_def->obj_def);
95
0
  }
96
0
}
97
98
/*
99
 * Testsuite object code
100
 */
101
102
const struct sieve_operand_class sieve_testsuite_object_operand_class =
103
  { "testsuite object" };
104
105
static const struct sieve_extension_objects core_testsuite_objects =
106
  SIEVE_EXT_DEFINE_OBJECTS(testsuite_core_objects);
107
108
const struct sieve_operand_def testsuite_object_operand = {
109
  .name = "testsuite-object",
110
  .ext_def = &testsuite_extension,
111
  .code = TESTSUITE_OPERAND_OBJECT,
112
  .class = &sieve_testsuite_object_operand_class,
113
  .interface = &core_testsuite_objects,
114
};
115
116
static void
117
testsuite_object_emit(struct sieve_binary_block *sblock,
118
          const struct testsuite_object *tobj, int member_id)
119
0
{
120
0
  sieve_opr_object_emit(sblock, tobj->object.ext, tobj->object.def);
121
122
0
  if (tobj->def != NULL && tobj->def->get_member_id != NULL)
123
0
    (void)sieve_binary_emit_byte(sblock, (unsigned char)member_id);
124
0
}
125
126
bool testsuite_object_read(struct sieve_binary_block *sblock,
127
         sieve_size_t *address, struct testsuite_object *tobj)
128
0
{
129
0
  struct sieve_operand oprnd;
130
131
0
  if (!sieve_operand_read(sblock, address, NULL, &oprnd))
132
0
    return FALSE;
133
134
0
  if (!sieve_opr_object_read_data(sblock, &oprnd,
135
0
          &sieve_testsuite_object_operand_class,
136
0
          address, &tobj->object))
137
0
    return FALSE;
138
139
0
  tobj->def = (const struct testsuite_object_def *)tobj->object.def;
140
0
  i_assert(tobj->def != NULL);
141
0
  return TRUE;
142
0
}
143
144
bool testsuite_object_read_member(struct sieve_binary_block *sblock,
145
          sieve_size_t *address,
146
          struct testsuite_object *tobj,
147
         int *member_id_r)
148
0
{
149
0
  if (!testsuite_object_read(sblock, address, tobj))
150
0
    return FALSE;
151
152
0
  *member_id_r = -1;
153
0
  if (tobj->def->get_member_id != NULL) {
154
0
    if (!sieve_binary_read_code(sblock, address, member_id_r))
155
0
      return FALSE;
156
0
  }
157
0
  return TRUE;
158
0
}
159
160
const char *
161
testsuite_object_member_name(const struct testsuite_object *object,
162
           int member_id)
163
0
{
164
0
  const struct testsuite_object_def *obj_def = object->def;
165
0
  const char *member = NULL;
166
167
0
  if (obj_def->get_member_id != NULL) {
168
0
    if (obj_def->get_member_name != NULL)
169
0
      member = obj_def->get_member_name(member_id);
170
0
  } else
171
0
    return obj_def->obj_def.identifier;
172
173
0
  if (member == NULL) {
174
0
    return t_strdup_printf("%s.%d", obj_def->obj_def.identifier,
175
0
               member_id);
176
0
  }
177
0
  return t_strdup_printf("%s.%s", obj_def->obj_def.identifier, member);
178
0
}
179
180
bool testsuite_object_dump(const struct sieve_dumptime_env *denv,
181
         sieve_size_t *address)
182
0
{
183
0
  struct testsuite_object object;
184
0
  int member_id;
185
186
0
  sieve_code_mark(denv);
187
188
0
  if (!testsuite_object_read_member(denv->sblock, address,
189
0
            &object, &member_id))
190
0
    return FALSE;
191
192
0
  sieve_code_dumpf(denv, "%s: %s",
193
0
       sieve_testsuite_object_operand_class.name,
194
0
      testsuite_object_member_name(&object, member_id));
195
0
  return TRUE;
196
0
}
197
198
/*
199
 * Testsuite object argument
200
 */
201
202
static bool
203
arg_testsuite_object_generate(const struct sieve_codegen_env *cgenv,
204
            struct sieve_ast_argument *arg,
205
            struct sieve_command *cmd);
206
207
const struct sieve_argument_def testsuite_object_argument = {
208
  .identifier = "testsuite-object",
209
  .generate = arg_testsuite_object_generate
210
};
211
212
struct testsuite_object_argctx {
213
  const struct testsuite_object *object;
214
  int member;
215
};
216
217
bool testsuite_object_argument_activate(struct sieve_validator *valdtr,
218
          struct sieve_ast_argument *arg,
219
          struct sieve_command *cmd)
220
0
{
221
0
  const char *objname = sieve_ast_argument_strc(arg);
222
0
  const struct testsuite_object *tobj;
223
0
  int member_id;
224
0
  const char *member;
225
0
  struct testsuite_object_argctx *ctx;
226
227
  /* Parse the object specifier */
228
229
0
  member = strchr(objname, '.');
230
0
  if (member != NULL) {
231
0
    objname = t_strdup_until(objname, member);
232
0
    member++;
233
0
  }
234
235
  /* Find the object */
236
237
0
  tobj = testsuite_object_create(valdtr, cmd, objname);
238
0
  if (tobj == NULL) {
239
0
    sieve_argument_validate_error(valdtr, arg,
240
0
                "unknown testsuite object '%s'",
241
0
                objname);
242
0
    return FALSE;
243
0
  }
244
245
  /* Find the object member */
246
247
0
  member_id = -1;
248
0
  if (member != NULL) {
249
0
    if (tobj->def == NULL || tobj->def->get_member_id == NULL ||
250
0
        (member_id = tobj->def->get_member_id(member)) == -1) {
251
0
      sieve_argument_validate_error(valdtr, arg,
252
0
        "member '%s' does not exist for testsuite object '%s'",
253
0
        member, objname);
254
0
      return FALSE;
255
0
    }
256
0
  }
257
258
  /* Assign argument context */
259
260
0
  ctx = p_new(sieve_command_pool(cmd), struct testsuite_object_argctx, 1);
261
0
  ctx->object = tobj;
262
0
  ctx->member = member_id;
263
264
0
  arg->argument = sieve_argument_create(
265
0
    arg->ast, &testsuite_object_argument, testsuite_ext, 0);
266
0
  arg->argument->data = ctx;
267
0
  return TRUE;
268
0
}
269
270
static bool
271
arg_testsuite_object_generate(const struct sieve_codegen_env *cgenv,
272
            struct sieve_ast_argument *arg,
273
            struct sieve_command *cmd ATTR_UNUSED)
274
0
{
275
0
  struct testsuite_object_argctx *ctx =
276
0
    (struct testsuite_object_argctx *)arg->argument->data;
277
278
0
  testsuite_object_emit(cgenv->sblock, ctx->object, ctx->member);
279
0
  return TRUE;
280
0
}
281
282
/*
283
 * Testsuite core object implementation
284
 */
285
286
static bool tsto_message_set_member(const struct sieve_runtime_env *renv,
287
            int id, string_t *value);
288
289
static int tsto_envelope_get_member_id(const char *identifier);
290
static const char *tsto_envelope_get_member_name(int id);
291
static bool tsto_envelope_set_member(const struct sieve_runtime_env *renv,
292
             int id, string_t *value);
293
294
const struct testsuite_object_def message_testsuite_object = {
295
  SIEVE_OBJECT("message", &testsuite_object_operand,
296
         TESTSUITE_OBJECT_MESSAGE),
297
  .set_member = tsto_message_set_member,
298
};
299
300
const struct testsuite_object_def envelope_testsuite_object = {
301
  SIEVE_OBJECT("envelope", &testsuite_object_operand,
302
         TESTSUITE_OBJECT_ENVELOPE),
303
  .get_member_id = tsto_envelope_get_member_id,
304
  .get_member_name = tsto_envelope_get_member_name,
305
  .set_member = tsto_envelope_set_member,
306
};
307
308
enum testsuite_object_envelope_field {
309
  TESTSUITE_OBJECT_ENVELOPE_FROM,
310
  TESTSUITE_OBJECT_ENVELOPE_TO,
311
  TESTSUITE_OBJECT_ENVELOPE_ORIG_TO,
312
  TESTSUITE_OBJECT_ENVELOPE_AUTH_USER,
313
};
314
315
static bool
316
tsto_message_set_member(const struct sieve_runtime_env *renv, int id,
317
      string_t *value)
318
0
{
319
0
  if (id != -1)
320
0
    return FALSE;
321
322
0
  testsuite_message_set_string(renv, value);
323
0
  return TRUE;
324
0
}
325
326
static int tsto_envelope_get_member_id(const char *identifier)
327
0
{
328
0
  if (strcasecmp(identifier, "from") == 0)
329
0
    return TESTSUITE_OBJECT_ENVELOPE_FROM;
330
0
  if (strcasecmp(identifier, "to") == 0)
331
0
    return TESTSUITE_OBJECT_ENVELOPE_TO;
332
0
  if (strcasecmp(identifier, "orig_to") == 0)
333
0
    return TESTSUITE_OBJECT_ENVELOPE_ORIG_TO;
334
0
  if (strcasecmp(identifier, "auth") == 0)
335
0
    return TESTSUITE_OBJECT_ENVELOPE_AUTH_USER;
336
0
  return -1;
337
0
}
338
339
static const char *tsto_envelope_get_member_name(int id)
340
0
{
341
0
  switch (id) {
342
0
  case TESTSUITE_OBJECT_ENVELOPE_FROM:
343
0
    return "from";
344
0
  case TESTSUITE_OBJECT_ENVELOPE_TO:
345
0
    return "to";
346
0
  case TESTSUITE_OBJECT_ENVELOPE_ORIG_TO:
347
0
    return "orig_to";
348
0
  case TESTSUITE_OBJECT_ENVELOPE_AUTH_USER:
349
0
    return "auth";
350
0
  }
351
0
  return NULL;
352
0
}
353
354
static bool
355
tsto_envelope_set_member(const struct sieve_runtime_env *renv, int id,
356
       string_t *value)
357
0
{
358
0
  switch (id) {
359
0
  case TESTSUITE_OBJECT_ENVELOPE_FROM:
360
0
    testsuite_envelope_set_sender(renv, str_c(value));
361
0
    return TRUE;
362
0
  case TESTSUITE_OBJECT_ENVELOPE_TO:
363
0
    testsuite_envelope_set_recipient(renv, str_c(value));
364
0
    return TRUE;
365
0
  case TESTSUITE_OBJECT_ENVELOPE_ORIG_TO:
366
0
    testsuite_envelope_set_orig_recipient(renv, str_c(value));
367
0
    return TRUE;
368
0
  case TESTSUITE_OBJECT_ENVELOPE_AUTH_USER:
369
0
    testsuite_envelope_set_auth_user(renv, str_c(value));
370
0
    return TRUE;
371
0
  }
372
0
  return FALSE;
373
0
}