Coverage Report

Created: 2026-06-09 06:49

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