Coverage Report

Created: 2026-07-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/imap4flags/cmd-flag.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
5
#include "sieve-code.h"
6
#include "sieve-stringlist.h"
7
#include "sieve-commands.h"
8
#include "sieve-validator.h"
9
#include "sieve-generator.h"
10
#include "sieve-interpreter.h"
11
#include "sieve-dump.h"
12
13
#include "ext-imap4flags-common.h"
14
15
/*
16
 * Commands
17
 */
18
19
/* Forward declarations */
20
21
static bool
22
cmd_flag_generate(const struct sieve_codegen_env *cgenv,
23
      struct sieve_command *ctx);
24
25
/* Setflag command
26
 *
27
 * Syntax:
28
 *   setflag [<variablename: string>] <list-of-flags: string-list>
29
 */
30
31
const struct sieve_command_def cmd_setflag = {
32
  .identifier = "setflag",
33
  .type = SCT_COMMAND,
34
  .positional_args = -1, /* We check positional arguments ourselves */
35
  .subtests = 0,
36
  .block_allowed = FALSE,
37
  .block_required = FALSE,
38
  .validate = ext_imap4flags_command_validate,
39
  .generate = cmd_flag_generate,
40
};
41
42
/* Addflag command
43
 *
44
 * Syntax:
45
 *   addflag [<variablename: string>] <list-of-flags: string-list>
46
 */
47
48
const struct sieve_command_def cmd_addflag = {
49
  .identifier = "addflag",
50
  .type = SCT_COMMAND,
51
  .positional_args = -1, /* We check positional arguments ourselves */
52
  .subtests = 0,
53
  .block_allowed = FALSE,
54
  .block_required = FALSE,
55
  .validate = ext_imap4flags_command_validate,
56
  .generate = cmd_flag_generate,
57
};
58
59
60
/* Removeflag command
61
 *
62
 * Syntax:
63
 *   removeflag [<variablename: string>] <list-of-flags: string-list>
64
 */
65
66
const struct sieve_command_def cmd_removeflag = {
67
  .identifier = "removeflag",
68
  .type = SCT_COMMAND,
69
  .positional_args = -1, /* We check positional arguments ourselves */
70
  .subtests = 0,
71
  .block_allowed = FALSE,
72
  .block_required = FALSE,
73
  .validate = ext_imap4flags_command_validate,
74
  .generate = cmd_flag_generate,
75
};
76
77
/*
78
 * Operations
79
 */
80
81
/* Forward declarations */
82
83
bool cmd_flag_operation_dump(const struct sieve_dumptime_env *denv,
84
           sieve_size_t *address);
85
static int
86
cmd_flag_operation_execute(const struct sieve_runtime_env *renv,
87
         sieve_size_t *address);
88
89
/* Setflag operation */
90
91
const struct sieve_operation_def setflag_operation = {
92
  .mnemonic = "SETFLAG",
93
  .ext_def = &imap4flags_extension,
94
  .code = EXT_IMAP4FLAGS_OPERATION_SETFLAG,
95
  .dump = cmd_flag_operation_dump,
96
  .execute = cmd_flag_operation_execute,
97
};
98
99
/* Addflag operation */
100
101
const struct sieve_operation_def addflag_operation = {
102
  .mnemonic = "ADDFLAG",
103
  .ext_def = &imap4flags_extension,
104
  .code = EXT_IMAP4FLAGS_OPERATION_ADDFLAG,
105
  .dump = cmd_flag_operation_dump,
106
  .execute = cmd_flag_operation_execute,
107
};
108
109
/* Removeflag operation */
110
111
const struct sieve_operation_def removeflag_operation = {
112
  .mnemonic = "REMOVEFLAG",
113
  .ext_def = &imap4flags_extension,
114
  .code = EXT_IMAP4FLAGS_OPERATION_REMOVEFLAG,
115
  .dump = cmd_flag_operation_dump,
116
  .execute = cmd_flag_operation_execute,
117
};
118
119
/*
120
 * Code generation
121
 */
122
123
static bool
124
cmd_flag_generate(const struct sieve_codegen_env *cgenv,
125
      struct sieve_command *cmd)
126
0
{
127
0
  struct sieve_ast_argument *arg1, *arg2;
128
129
  /* Emit operation */
130
0
  if (sieve_command_is(cmd, cmd_setflag)) {
131
0
    sieve_operation_emit(cgenv->sblock, cmd->ext,
132
0
             &setflag_operation);
133
0
  } else if (sieve_command_is(cmd, cmd_addflag)) {
134
0
    sieve_operation_emit(cgenv->sblock, cmd->ext,
135
0
             &addflag_operation);
136
0
  } else if (sieve_command_is(cmd, cmd_removeflag)) {
137
0
    sieve_operation_emit(cgenv->sblock, cmd->ext,
138
0
             &removeflag_operation);
139
0
  }
140
141
0
  arg1 = cmd->first_positional;
142
0
  arg2 = sieve_ast_argument_next(arg1);
143
144
0
  if (arg2 == NULL) {
145
    /* No variable */
146
0
    sieve_opr_omitted_emit(cgenv->sblock);
147
0
    if (!sieve_generate_argument(cgenv, arg1, cmd))
148
0
      return FALSE;
149
0
  } else {
150
    /* Full command */
151
0
    if (!sieve_generate_argument(cgenv, arg1, cmd))
152
0
      return FALSE;
153
0
    if (!sieve_generate_argument(cgenv, arg2, cmd))
154
0
      return FALSE;
155
0
  }
156
0
  return TRUE;
157
0
}
158
159
/*
160
 * Code dump
161
 */
162
163
bool cmd_flag_operation_dump(const struct sieve_dumptime_env *denv,
164
           sieve_size_t *address)
165
0
{
166
0
  struct sieve_operand oprnd;
167
168
0
  sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(denv->oprtn));
169
0
  sieve_code_descend(denv);
170
171
0
  sieve_code_mark(denv);
172
0
  if (!sieve_operand_read(denv->sblock, address, NULL, &oprnd)) {
173
0
    sieve_code_dumpf(denv, "ERROR: INVALID OPERAND");
174
0
    return FALSE;
175
0
  }
176
177
0
  if (!sieve_operand_is_omitted(&oprnd)) {
178
0
    return (sieve_opr_string_dump_data(denv, &oprnd,
179
0
               address, "variable name") &&
180
0
      sieve_opr_stringlist_dump(denv, address,
181
0
              "list of flags"));
182
0
  }
183
0
  return sieve_opr_stringlist_dump(denv, address, "list of flags");
184
0
}
185
186
/*
187
 * Code execution
188
 */
189
190
static int
191
cmd_flag_operation_execute(const struct sieve_runtime_env *renv,
192
         sieve_size_t *address)
193
0
{
194
0
  const struct sieve_operation *op = renv->oprtn;
195
0
  struct sieve_operand oprnd;
196
0
  struct sieve_stringlist *flag_list;
197
0
  struct sieve_variable_storage *storage;
198
0
  unsigned int var_index;
199
0
  ext_imapflag_flag_operation_t flag_op;
200
0
  int ret;
201
202
  /*
203
   * Read operands
204
   */
205
206
  /* Read bare operand (two types possible) */
207
0
  ret = sieve_operand_runtime_read(renv, address, NULL, &oprnd);
208
0
  if (ret <= 0)
209
0
    return ret;
210
211
  /* Variable operand (optional) */
212
0
  if (!sieve_operand_is_omitted(&oprnd)) {
213
    /* Read the variable operand */
214
0
    ret = sieve_variable_operand_read_data(
215
0
      renv, &oprnd, address, "variable",
216
0
      &storage, &var_index);
217
0
    if (ret <= 0)
218
0
      return ret;
219
220
    /* Read flag list */
221
0
    ret = sieve_opr_stringlist_read(renv, address, "flag-list",
222
0
            &flag_list);
223
0
    if (ret <= 0)
224
0
      return ret;
225
226
  /* Flag-list operand */
227
0
  } else {
228
0
    storage = NULL;
229
0
    var_index = 0;
230
231
    /* Read flag list */
232
0
    ret = sieve_opr_stringlist_read(renv, address, "flag-list",
233
0
            &flag_list);
234
0
    if (ret <= 0)
235
0
      return ret;
236
0
  }
237
238
  /*
239
   * Perform operation
240
   */
241
242
  /* Determine what to do */
243
244
0
  if (sieve_operation_is(op, setflag_operation)) {
245
0
    sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
246
0
            "setflag command");
247
0
    flag_op = sieve_ext_imap4flags_set_flags;
248
0
  } else if (sieve_operation_is(op, addflag_operation)) {
249
0
    sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
250
0
            "addflag command");
251
0
    flag_op = sieve_ext_imap4flags_add_flags;
252
0
  } else if (sieve_operation_is(op, removeflag_operation)) {
253
0
    sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
254
0
            "removeflag command");
255
0
    flag_op = sieve_ext_imap4flags_remove_flags;
256
0
  } else {
257
0
    i_unreached();
258
0
  }
259
260
0
  sieve_runtime_trace_descend(renv);
261
262
  /* Perform requested operation */
263
0
  return flag_op(renv, op->ext, storage, var_index, flag_list);
264
0
}