Coverage Report

Created: 2026-04-27 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/mime/cmd-break.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2
 */
3
4
#include "sieve-common.h"
5
#include "sieve-code.h"
6
#include "sieve-extensions.h"
7
#include "sieve-commands.h"
8
#include "sieve-validator.h"
9
#include "sieve-generator.h"
10
#include "sieve-interpreter.h"
11
#include "sieve-binary.h"
12
#include "sieve-dump.h"
13
14
#include "ext-mime-common.h"
15
16
#include <ctype.h>
17
18
/* break
19
 *
20
 * Syntax:
21
 *   break [":name" <name: string>]
22
 *
23
 */
24
25
static bool
26
cmd_break_registered(struct sieve_validator *valdtr,
27
         const struct sieve_extension *ext,
28
         struct sieve_command_registration *cmd_reg);
29
static bool
30
cmd_break_pre_validate(struct sieve_validator *valdtr,
31
           struct sieve_command *cmd);
32
static bool
33
cmd_break_validate(struct sieve_validator *valdtr, struct sieve_command *cmd);
34
static bool
35
cmd_break_generate(const struct sieve_codegen_env *cgenv,
36
       struct sieve_command *ctx);
37
38
const struct sieve_command_def cmd_break = {
39
  .identifier = "break",
40
  .type = SCT_COMMAND,
41
  .positional_args = 0,
42
  .subtests = 0,
43
  .block_allowed = FALSE,
44
  .block_required = FALSE,
45
  .registered = cmd_break_registered,
46
  .pre_validate = cmd_break_pre_validate,
47
  .validate = cmd_break_validate,
48
  .generate = cmd_break_generate,
49
};
50
51
/*
52
 * Tagged arguments
53
 */
54
55
/* Forward declarations */
56
57
static bool
58
cmd_break_validate_name_tag(struct sieve_validator *valdtr,
59
          struct sieve_ast_argument **arg,
60
          struct sieve_command *cmd);
61
62
/* Argument objects */
63
64
static const struct sieve_argument_def break_name_tag = {
65
  .identifier = "name",
66
  .validate = cmd_break_validate_name_tag,
67
};
68
69
/*
70
 * Break operation
71
 */
72
73
static bool
74
cmd_break_operation_dump(const struct sieve_dumptime_env *denv,
75
       sieve_size_t *address);
76
static int
77
cmd_break_operation_execute(const struct sieve_runtime_env *renv,
78
          sieve_size_t *address);
79
80
const struct sieve_operation_def break_operation = {
81
  .mnemonic = "BREAK",
82
  .ext_def = &foreverypart_extension,
83
  .code = EXT_FOREVERYPART_OPERATION_BREAK,
84
  .dump = cmd_break_operation_dump,
85
  .execute = cmd_break_operation_execute,
86
};
87
88
/*
89
 * Validation data
90
 */
91
92
struct cmd_break_data {
93
  struct sieve_ast_argument *name;
94
  struct sieve_command *loop_cmd;
95
};
96
97
/*
98
 * Tag validation
99
 */
100
101
static bool
102
cmd_break_validate_name_tag(struct sieve_validator *valdtr,
103
          struct sieve_ast_argument **arg,
104
          struct sieve_command *cmd)
105
0
{
106
0
  struct cmd_break_data *data = (struct cmd_break_data *)cmd->data;
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
   *   :name <string>
114
   */
115
0
  if (!sieve_validate_tag_parameter(valdtr, cmd, tag, *arg, NULL, 0,
116
0
            SAAT_STRING, TRUE))
117
0
    return FALSE;
118
0
  data->name = *arg;
119
120
  /* Skip parameter */
121
0
  *arg = sieve_ast_argument_next(*arg);
122
0
  return TRUE;
123
0
}
124
125
/*
126
 * Command registration
127
 */
128
129
static bool
130
cmd_break_registered(struct sieve_validator *valdtr,
131
         const struct sieve_extension *ext,
132
         struct sieve_command_registration *cmd_reg)
133
0
{
134
0
  sieve_validator_register_tag(valdtr, cmd_reg, ext, &break_name_tag, 0);
135
0
  return TRUE;
136
0
}
137
138
/*
139
 * Command validation
140
 */
141
142
static bool
143
cmd_break_pre_validate(struct sieve_validator *valdtr ATTR_UNUSED,
144
           struct sieve_command *cmd)
145
0
{
146
0
  struct cmd_break_data *data;
147
0
  pool_t pool = sieve_command_pool(cmd);
148
149
0
  data = p_new(pool, struct cmd_break_data, 1);
150
0
  cmd->data = data;
151
0
  return TRUE;
152
0
}
153
154
static bool
155
cmd_break_validate(struct sieve_validator *valdtr, struct sieve_command *cmd)
156
0
{
157
0
  struct cmd_break_data *data = (struct cmd_break_data *)cmd->data;
158
0
  struct sieve_ast_node *node = cmd->ast_node;
159
0
  const char *name = (data->name == NULL ?
160
0
          NULL : sieve_ast_argument_strc(data->name));
161
162
0
  i_assert(node != NULL);
163
0
  while (node != NULL && node->command != NULL) {
164
0
    if (sieve_command_is(node->command, cmd_foreverypart)) {
165
0
      struct ext_foreverypart_loop *loop =
166
0
        (struct ext_foreverypart_loop *)
167
0
          node->command->data;
168
0
      if (name == NULL ||
169
0
          (name != NULL && loop->name != NULL &&
170
0
           strcmp(name, loop->name) == 0)) {
171
0
        data->loop_cmd = node->command;
172
0
        break;
173
0
      }
174
0
    }
175
0
    node = sieve_ast_node_parent(node);
176
0
  }
177
178
0
  if (data->loop_cmd == NULL) {
179
0
    if (name == NULL) {
180
0
      sieve_command_validate_error(
181
0
        valdtr, cmd,
182
0
        "the break command is not placed inside "
183
0
        "a foreverypart loop");
184
0
    } else {
185
0
      sieve_command_validate_error(
186
0
        valdtr, cmd,
187
0
        "the break command is not placed inside "
188
0
        "a foreverypart loop named '%s'", name);
189
0
    }
190
0
    return FALSE;
191
0
  }
192
193
0
  sieve_command_exit_block_unconditionally(cmd);
194
0
  return TRUE;
195
0
}
196
197
/*
198
 * Code generation
199
 */
200
201
static bool
202
cmd_break_generate(const struct sieve_codegen_env *cgenv,
203
       struct sieve_command *cmd)
204
0
{
205
0
  struct cmd_break_data *data = (struct cmd_break_data *)cmd->data;
206
0
  struct ext_foreverypart_loop *loop;
207
208
0
  i_assert(data->loop_cmd != NULL);
209
0
  loop = (struct ext_foreverypart_loop *)data->loop_cmd->data;
210
211
0
  sieve_operation_emit(cgenv->sblock, cmd->ext, &break_operation);
212
0
  sieve_jumplist_add(loop->exit_jumps,
213
0
         sieve_binary_emit_offset(cgenv->sblock, 0));
214
0
  return TRUE;
215
0
}
216
217
/*
218
 * Code dump
219
 */
220
221
static bool
222
cmd_break_operation_dump(const struct sieve_dumptime_env *denv,
223
       sieve_size_t *address)
224
0
{
225
0
  unsigned int pc = *address;
226
0
  sieve_offset_t offset;
227
228
0
  sieve_code_dumpf(denv, "BREAK");
229
0
  sieve_code_descend(denv);
230
231
0
  if (!sieve_binary_read_offset(denv->sblock, address, &offset))
232
0
    return FALSE;
233
234
0
  sieve_code_dumpf(denv, "END: %d [%08x]", offset, pc + offset);
235
0
  return TRUE;
236
0
}
237
238
/*
239
 * Code execution
240
 */
241
242
static int
243
cmd_break_operation_execute(const struct sieve_runtime_env *renv,
244
          sieve_size_t *address)
245
0
{
246
0
  struct sieve_interpreter_loop *loop;
247
0
  unsigned int pc = *address;
248
0
  sieve_offset_t offset;
249
0
  sieve_size_t loop_end;
250
251
  /*
252
   * Read operands
253
   */
254
255
0
  if (!sieve_binary_read_offset(renv->sblock, address, &offset))
256
0
  {
257
0
    sieve_runtime_trace_error(renv, "invalid loop end offset");
258
0
    return SIEVE_EXEC_BIN_CORRUPT;
259
0
  }
260
261
0
  loop_end = pc + offset;
262
263
  /*
264
   * Perform operation
265
   */
266
267
0
  sieve_runtime_trace(renv, SIEVE_TRLVL_ACTIONS, "break command");
268
0
  sieve_runtime_trace_descend(renv);
269
270
0
  loop = sieve_interpreter_loop_get(renv->interp, loop_end,
271
0
            &foreverypart_extension);
272
0
  if (loop == NULL) {
273
0
    sieve_runtime_trace_error(renv, "no matching loop found");
274
0
    return SIEVE_EXEC_BIN_CORRUPT;
275
0
  }
276
277
0
  sieve_interpreter_loop_break(renv->interp, loop);
278
0
  return SIEVE_EXEC_OK;
279
0
}