Coverage Report

Created: 2026-08-31 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/mime/cmd-extracttext.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "str.h"
5
#include "array.h"
6
7
#include "sieve-common.h"
8
#include "sieve-extensions.h"
9
#include "sieve-code.h"
10
#include "sieve-ast.h"
11
#include "sieve-commands.h"
12
#include "sieve-binary.h"
13
#include "sieve-message.h"
14
#include "sieve-validator.h"
15
#include "sieve-generator.h"
16
#include "sieve-interpreter.h"
17
#include "sieve-dump.h"
18
19
#include "sieve-ext-variables.h"
20
21
#include "ext-mime-common.h"
22
23
/*
24
 * Extracttext command
25
 *
26
 * Syntax:
27
 *    extracttext [MODIFIER] [":first" number] <varname: string>
28
 */
29
30
static bool
31
cmd_extracttext_registered(struct sieve_validator *valdtr,
32
         const struct sieve_extension *ext,
33
         struct sieve_command_registration *cmd_reg);
34
static bool
35
cmd_extracttext_validate(struct sieve_validator *valdtr,
36
       struct sieve_command *cmd);
37
static bool
38
cmd_extracttext_generate(const struct sieve_codegen_env *cgenv,
39
       struct sieve_command *ctx);
40
41
const struct sieve_command_def cmd_extracttext = {
42
  .identifier = "extracttext",
43
  .type = SCT_COMMAND,
44
  .positional_args = 1,
45
  .subtests = 0,
46
  .block_allowed = FALSE,
47
  .block_required = FALSE,
48
  .registered = cmd_extracttext_registered,
49
  .validate = cmd_extracttext_validate,
50
  .generate = cmd_extracttext_generate,
51
};
52
53
/*
54
 * Extracttext command tags
55
 */
56
57
enum cmd_extracttext_optional {
58
  CMD_EXTRACTTEXT_OPT_END,
59
  CMD_EXTRACTTEXT_OPT_FIRST
60
};
61
62
static bool
63
cmd_extracttext_validate_first_tag(struct sieve_validator *valdtr,
64
           struct sieve_ast_argument **arg,
65
           struct sieve_command *cmd);
66
67
static const struct sieve_argument_def extracttext_from_tag = {
68
  .identifier = "first",
69
  .validate = cmd_extracttext_validate_first_tag,
70
};
71
72
/*
73
 * Extracttext operation
74
 */
75
76
static bool
77
cmd_extracttext_operation_dump(const struct sieve_dumptime_env *denv,
78
             sieve_size_t *address);
79
static int
80
cmd_extracttext_operation_execute(const struct sieve_runtime_env *renv,
81
          sieve_size_t *address);
82
83
const struct sieve_operation_def extracttext_operation = {
84
  .mnemonic = "EXTRACTTEXT",
85
  .ext_def = &extracttext_extension,
86
  .dump = cmd_extracttext_operation_dump,
87
  .execute = cmd_extracttext_operation_execute,
88
};
89
90
/*
91
 * Compiler context
92
 */
93
94
struct cmd_extracttext_context {
95
  ARRAY_TYPE(sieve_variables_modifier) modifiers;
96
};
97
98
/*
99
 * Tag validation
100
 */
101
102
static bool
103
cmd_extracttext_validate_first_tag(struct sieve_validator *valdtr,
104
           struct sieve_ast_argument **arg,
105
           struct sieve_command *cmd)
106
0
{
107
0
  struct sieve_ast_argument *tag = *arg;
108
109
  /* Detach the tag itself */
110
0
  *arg = sieve_ast_arguments_detach(*arg,1);
111
112
  /* Check syntax:
113
   *   :first <number>
114
   */
115
0
  if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
116
0
            SAAT_NUMBER, FALSE) )
117
0
    return FALSE;
118
119
  /* Skip parameter */
120
0
  *arg = sieve_ast_argument_next(*arg);
121
122
0
  return TRUE;
123
0
}
124
125
/* Command registration */
126
127
static bool
128
cmd_extracttext_registered(struct sieve_validator *valdtr,
129
         const struct sieve_extension *ext,
130
         struct sieve_command_registration *cmd_reg)
131
0
{
132
0
  struct ext_extracttext_context *extctx = ext->context;
133
134
0
  sieve_validator_register_tag(valdtr, cmd_reg, ext,
135
0
             &extracttext_from_tag,
136
0
             CMD_EXTRACTTEXT_OPT_FIRST);
137
0
  sieve_variables_modifiers_link_tag(valdtr, extctx->var_ext, cmd_reg);
138
0
  return TRUE;
139
0
}
140
141
/*
142
 * Command validation
143
 */
144
145
static bool
146
cmd_extracttext_validate(struct sieve_validator *valdtr,
147
       struct sieve_command *cmd)
148
0
{
149
0
  const struct sieve_extension *this_ext = cmd->ext;
150
0
  struct ext_extracttext_context *extctx = this_ext->context;
151
0
  struct sieve_ast_node *node = cmd->ast_node;
152
0
  struct sieve_ast_argument *arg = cmd->first_positional;
153
0
  pool_t pool = sieve_command_pool(cmd);
154
0
  struct cmd_extracttext_context *sctx;
155
156
  /* Create command context */
157
0
  sctx = p_new(pool, struct cmd_extracttext_context, 1);
158
0
  p_array_init(&sctx->modifiers, pool, 4);
159
0
  cmd->data = sctx;
160
161
  /* Validate modifiers */
162
0
  if (!sieve_variables_modifiers_validate(valdtr, cmd, &sctx->modifiers))
163
0
    return FALSE;
164
165
  /* Validate varname argument */
166
0
  if (!sieve_validate_positional_argument(valdtr, cmd, arg, "varname", 1,
167
0
            SAAT_STRING))
168
0
    return FALSE;
169
0
  if (!sieve_variable_argument_activate(extctx->var_ext, extctx->var_ext,
170
0
                valdtr, cmd, arg, TRUE))
171
0
    return FALSE;
172
173
  /* Check foreverypart context */
174
0
  i_assert(node != NULL);
175
0
  while (node != NULL) {
176
0
    if (node->command != NULL &&
177
0
        sieve_command_is(node->command, cmd_foreverypart))
178
0
      break;
179
0
    node = sieve_ast_node_parent(node);
180
0
  }
181
182
0
  if (node == NULL) {
183
0
    sieve_command_validate_error(
184
0
      valdtr, cmd,
185
0
      "the extracttext command is not placed inside "
186
0
      "a foreverypart loop");
187
0
    return FALSE;
188
0
  }
189
0
  return TRUE;
190
0
}
191
192
/*
193
 * Code generation
194
 */
195
196
static bool
197
cmd_extracttext_generate(const struct sieve_codegen_env *cgenv,
198
       struct sieve_command *cmd)
199
0
{
200
0
  const struct sieve_extension *this_ext = cmd->ext;
201
0
  struct sieve_binary_block *sblock = cgenv->sblock;
202
0
  struct cmd_extracttext_context *sctx =
203
0
    (struct cmd_extracttext_context *) cmd->data;
204
205
0
  sieve_operation_emit(sblock, this_ext, &extracttext_operation);
206
207
  /* Generate arguments */
208
0
  if (!sieve_generate_arguments(cgenv, cmd, NULL))
209
0
    return FALSE;
210
211
  /* Generate modifiers */
212
0
  if (!sieve_variables_modifiers_generate(cgenv, &sctx->modifiers))
213
0
    return FALSE;
214
215
0
  return TRUE;
216
0
}
217
218
/*
219
 * Code dump
220
 */
221
222
static bool
223
cmd_extracttext_operation_dump(const struct sieve_dumptime_env *denv,
224
             sieve_size_t *address)
225
0
{
226
0
  int opt_code = 0;
227
228
0
  sieve_code_dumpf(denv, "EXTRACTTEXT");
229
0
  sieve_code_descend(denv);
230
231
  /* Dump optional operands */
232
233
0
  for (;;) {
234
0
    int opt;
235
0
    bool opok = TRUE;
236
237
0
    opt = sieve_opr_optional_dump(denv, address, &opt_code);
238
0
    if (opt < 0)
239
0
      return FALSE;
240
0
    if (opt == 0)
241
0
      break;
242
243
0
    switch (opt_code) {
244
0
    case CMD_EXTRACTTEXT_OPT_FIRST:
245
0
      opok = sieve_opr_number_dump(denv, address, "first");
246
0
      break;
247
0
    default:
248
0
      return FALSE;
249
0
    }
250
0
    if (!opok)
251
0
      return FALSE;
252
0
  }
253
254
  /* Print both variable name and string value */
255
0
  if (!sieve_opr_string_dump(denv, address, "varname"))
256
0
    return FALSE;
257
258
0
  return sieve_variables_modifiers_code_dump(denv, address);
259
0
}
260
261
/*
262
 * Code execution
263
 */
264
265
static int
266
cmd_extracttext_operation_execute(const struct sieve_runtime_env *renv,
267
          sieve_size_t *address)
268
0
{
269
0
  const struct sieve_extension *this_ext = renv->oprtn->ext;
270
0
  struct ext_extracttext_context *extctx = this_ext->context;
271
0
  struct sieve_variable_storage *storage;
272
0
  ARRAY_TYPE(sieve_variables_modifier) modifiers;
273
0
  struct ext_foreverypart_runtime_loop *sfploop;
274
0
  struct sieve_message_part *mpart;
275
0
  struct sieve_message_part_data mpart_data;
276
0
  int opt_code = 0;
277
0
  sieve_number_t first = 0;
278
0
  string_t *value;
279
0
  unsigned int var_index;
280
0
  bool have_first = FALSE;
281
0
  int ret = SIEVE_EXEC_OK;
282
283
  /*
284
   * Read the normal operands
285
   */
286
287
  /* Optional operands */
288
289
0
  for (;;) {
290
0
    int opt;
291
292
0
    opt = sieve_opr_optional_read(renv, address, &opt_code);
293
0
    if (opt < 0)
294
0
      return SIEVE_EXEC_BIN_CORRUPT;
295
0
    if (opt == 0)
296
0
      break;
297
298
0
    switch (opt_code) {
299
0
    case CMD_EXTRACTTEXT_OPT_FIRST:
300
0
      ret = sieve_opr_number_read(renv, address, "first",
301
0
                &first);
302
0
      have_first = TRUE;
303
0
      break;
304
0
    default:
305
0
      sieve_runtime_trace_error(
306
0
        renv, "unknown optional operand");
307
0
      return SIEVE_EXEC_BIN_CORRUPT;
308
0
    }
309
0
    if (ret <= 0)
310
0
      return ret;
311
0
  }
312
313
  /* Varname operand */
314
315
0
  ret = sieve_variable_operand_read(renv, address, "varname",
316
0
            &storage, &var_index);
317
0
  if (ret <= 0)
318
0
    return ret;
319
320
  /* Modifiers */
321
322
0
  ret = sieve_variables_modifiers_code_read(renv, extctx->var_ext,
323
0
              address, &modifiers);
324
0
  if (ret <= 0)
325
0
    return ret;
326
327
  /*
328
   * Determine and assign the value
329
   */
330
331
0
  sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "extracttext command");
332
0
  sieve_runtime_trace_descend(renv);
333
334
0
  sfploop = ext_foreverypart_runtime_loop_get_current(renv);
335
0
  if (sfploop == NULL) {
336
0
    sieve_runtime_trace_error(renv, "outside foreverypart context");
337
0
    return SIEVE_EXEC_BIN_CORRUPT;
338
0
  }
339
340
  /* Get current message part */
341
0
  mpart = sieve_message_part_iter_current(&sfploop->part_iter);
342
0
  i_assert(mpart != NULL);
343
344
  /* Get message part content */
345
0
  sieve_message_part_get_data(mpart, &mpart_data, TRUE);
346
347
  /* Apply ":first" limit, if any */
348
0
  if (!have_first || (size_t)first > mpart_data.size) {
349
0
    value = t_str_new_const(mpart_data.content, mpart_data.size);
350
0
  } else {
351
0
    value = t_str_new((size_t)first);
352
0
    str_append_data(value, mpart_data.content, (size_t)first);
353
0
  }
354
355
  /* Apply modifiers */
356
0
  ret = sieve_variables_modifiers_apply(renv, extctx->var_ext,
357
0
                &modifiers, &value);
358
0
  if (ret <= 0)
359
0
    return ret;
360
361
  /* Actually assign the value if all is well */
362
0
  i_assert (value != NULL);
363
0
  if (!sieve_variable_assign(storage, var_index, value))
364
0
    return SIEVE_EXEC_BIN_CORRUPT;
365
366
  /* Trace */
367
0
  if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS)) {
368
0
    const char *var_name, *var_id;
369
370
0
    (void)sieve_variable_get_identifier(storage, var_index,
371
0
                &var_name);
372
0
    var_id = sieve_variable_get_varid(storage, var_index);
373
374
0
    sieve_runtime_trace_here(renv, 0, "assign '%s' [%s] = \"%s\"",
375
0
           var_name, var_id, str_c(value));
376
0
  }
377
378
0
  return SIEVE_EXEC_OK;
379
0
}