Coverage Report

Created: 2026-04-12 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/tst-size.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2
 */
3
4
#include "lib.h"
5
#include "mail-storage.h"
6
7
#include "sieve-common.h"
8
#include "sieve-code.h"
9
#include "sieve-message.h"
10
#include "sieve-commands.h"
11
#include "sieve-validator.h"
12
#include "sieve-generator.h"
13
#include "sieve-interpreter.h"
14
#include "sieve-dump.h"
15
16
/*
17
 * Size test
18
 *
19
 * Syntax:
20
 *    size <":over" / ":under"> <limit: number>
21
 */
22
23
static bool
24
tst_size_registered(struct sieve_validator *valdtr,
25
        const struct sieve_extension *ext,
26
        struct sieve_command_registration *cmd_reg);
27
static bool
28
tst_size_pre_validate(struct sieve_validator *valdtr,
29
          struct sieve_command *tst);
30
static bool
31
tst_size_validate(struct sieve_validator *valdtr, struct sieve_command *tst);
32
static bool
33
tst_size_generate(const struct sieve_codegen_env *cgenv,
34
      struct sieve_command *ctx);
35
36
const struct sieve_command_def tst_size = {
37
  .identifier = "size",
38
  .type = SCT_TEST,
39
  .positional_args = 1,
40
  .subtests = 0,
41
  .block_allowed = FALSE,
42
  .block_required = FALSE,
43
  .registered = tst_size_registered,
44
  .pre_validate = tst_size_pre_validate,
45
  .validate = tst_size_validate,
46
  .generate = tst_size_generate
47
};
48
49
/*
50
 * Size operations
51
 */
52
53
static bool
54
tst_size_operation_dump(const struct sieve_dumptime_env *denv,
55
      sieve_size_t *address);
56
static int
57
tst_size_operation_execute(const struct sieve_runtime_env *renv,
58
         sieve_size_t *address);
59
60
const struct sieve_operation_def tst_size_over_operation = {
61
  .mnemonic = "SIZE-OVER",
62
  .code = SIEVE_OPERATION_SIZE_OVER,
63
  .dump = tst_size_operation_dump,
64
  .execute = tst_size_operation_execute
65
};
66
67
const struct sieve_operation_def tst_size_under_operation = {
68
  .mnemonic = "SIZE-UNDER",
69
  .code = SIEVE_OPERATION_SIZE_UNDER,
70
  .dump = tst_size_operation_dump,
71
  .execute = tst_size_operation_execute
72
};
73
74
/*
75
 * Context data
76
 */
77
78
struct tst_size_context_data {
79
  enum { SIZE_UNASSIGNED, SIZE_UNDER, SIZE_OVER } type;
80
};
81
82
#define TST_SIZE_ERROR_DUP_TAG \
83
  "exactly one of the ':under' or ':over' tags must be specified " \
84
  "for the size test, but more were found"
85
86
/*
87
 * Tag validation
88
 */
89
90
static bool
91
tst_size_validate_over_tag(struct sieve_validator *valdtr,
92
         struct sieve_ast_argument **arg,
93
         struct sieve_command *tst)
94
0
{
95
0
  struct tst_size_context_data *ctx_data =
96
0
    (struct tst_size_context_data *)tst->data;
97
98
0
  if (ctx_data->type != SIZE_UNASSIGNED) {
99
0
    sieve_argument_validate_error(valdtr, *arg,
100
0
                TST_SIZE_ERROR_DUP_TAG);
101
0
    return FALSE;
102
0
  }
103
104
0
  ctx_data->type = SIZE_OVER;
105
106
  /* Delete this tag */
107
0
  *arg = sieve_ast_arguments_detach(*arg, 1);
108
109
0
  return TRUE;
110
0
}
111
112
static bool
113
tst_size_validate_under_tag(struct sieve_validator *valdtr,
114
          struct sieve_ast_argument **arg ATTR_UNUSED,
115
          struct sieve_command *tst)
116
0
{
117
0
  struct tst_size_context_data *ctx_data =
118
0
    (struct tst_size_context_data *)tst->data;
119
120
0
  if (ctx_data->type != SIZE_UNASSIGNED) {
121
0
    sieve_argument_validate_error(valdtr, *arg,
122
0
                TST_SIZE_ERROR_DUP_TAG);
123
0
    return FALSE;
124
0
  }
125
126
0
  ctx_data->type = SIZE_UNDER;
127
128
  /* Delete this tag */
129
0
  *arg = sieve_ast_arguments_detach(*arg, 1);
130
131
0
  return TRUE;
132
0
}
133
134
/*
135
 * Test registration
136
 */
137
138
static const struct sieve_argument_def size_over_tag = {
139
  .identifier = "over",
140
  .validate = tst_size_validate_over_tag
141
};
142
143
static const struct sieve_argument_def size_under_tag = {
144
  .identifier = "under",
145
  .validate = tst_size_validate_under_tag,
146
};
147
148
static bool
149
tst_size_registered(struct sieve_validator *valdtr,
150
        const struct sieve_extension *ext ATTR_UNUSED,
151
        struct sieve_command_registration *cmd_reg)
152
0
{
153
  /* Register our tags */
154
0
  sieve_validator_register_tag(valdtr, cmd_reg, NULL, &size_over_tag, 0);
155
0
  sieve_validator_register_tag(valdtr, cmd_reg, NULL, &size_under_tag, 0);
156
157
0
  return TRUE;
158
0
}
159
160
/*
161
 * Test validation
162
 */
163
164
static bool
165
tst_size_pre_validate(struct sieve_validator *valdtr ATTR_UNUSED,
166
          struct sieve_command *tst)
167
0
{
168
0
  struct tst_size_context_data *ctx_data;
169
170
  /* Assign context */
171
0
  ctx_data = p_new(sieve_command_pool(tst),
172
0
       struct tst_size_context_data, 1);
173
0
  ctx_data->type = SIZE_UNASSIGNED;
174
0
  tst->data = ctx_data;
175
176
0
  return TRUE;
177
0
}
178
179
static bool
180
tst_size_validate(struct sieve_validator *valdtr, struct sieve_command *tst)
181
0
{
182
0
  struct tst_size_context_data *ctx_data =
183
0
    (struct tst_size_context_data *)tst->data;
184
0
  struct sieve_ast_argument *arg = tst->first_positional;
185
186
0
  if (ctx_data->type == SIZE_UNASSIGNED) {
187
0
    sieve_command_validate_error(valdtr, tst,
188
0
      "the size test requires either the :under or the :over tag "
189
0
      "to be specified");
190
0
    return FALSE;
191
0
  }
192
193
0
  if (!sieve_validate_positional_argument(valdtr, tst, arg, "limit", 1,
194
0
            SAAT_NUMBER))
195
0
    return FALSE;
196
197
0
  return sieve_validator_argument_activate(valdtr, tst, arg, FALSE);
198
0
}
199
200
/*
201
 * Code generation
202
 */
203
204
bool tst_size_generate(const struct sieve_codegen_env *cgenv,
205
           struct sieve_command *tst)
206
0
{
207
0
  struct tst_size_context_data *ctx_data =
208
0
    (struct tst_size_context_data *)tst->data;
209
210
0
  if (ctx_data->type == SIZE_OVER) {
211
0
    sieve_operation_emit(cgenv->sblock, NULL,
212
0
             &tst_size_over_operation);
213
0
  } else {
214
0
    sieve_operation_emit(cgenv->sblock, NULL,
215
0
             &tst_size_under_operation);
216
0
  }
217
218
  /* Generate arguments */
219
0
  if (!sieve_generate_arguments(cgenv, tst, NULL))
220
0
    return FALSE;
221
222
0
  return TRUE;
223
0
}
224
225
/*
226
 * Code dump
227
 */
228
229
static bool
230
tst_size_operation_dump(const struct sieve_dumptime_env *denv,
231
      sieve_size_t *address)
232
0
{
233
0
  sieve_code_dumpf(denv, "%s", sieve_operation_mnemonic(denv->oprtn));
234
0
  sieve_code_descend(denv);
235
236
0
  return sieve_opr_number_dump(denv, address, "limit");
237
0
}
238
239
/*
240
 * Code execution
241
 */
242
243
static inline bool
244
tst_size_get(const struct sieve_runtime_env *renv, sieve_number_t *size)
245
0
{
246
0
  struct mail *mail = sieve_message_get_mail(renv->msgctx);
247
0
  uoff_t psize;
248
249
0
  if (mail_get_physical_size(mail, &psize) < 0)
250
0
    return FALSE;
251
252
0
  *size = psize;
253
0
  return TRUE;
254
0
}
255
256
static int
257
tst_size_operation_execute(const struct sieve_runtime_env *renv,
258
         sieve_size_t *address)
259
0
{
260
0
  sieve_number_t mail_size, limit;
261
0
  int ret;
262
263
  /*
264
   * Read operands
265
   */
266
267
  /* Read size limit */
268
0
  if ((ret = sieve_opr_number_read(renv, address, "limit", &limit)) <= 0)
269
0
    return ret;
270
271
  /*
272
   * Perform test
273
   */
274
275
  /* Get the size of the message */
276
0
  if (!tst_size_get(renv, &mail_size)) {
277
    /* FIXME: improve this error */
278
0
    e_error(renv->event, "failed to assess message size");
279
0
    return SIEVE_EXEC_FAILURE;
280
0
  }
281
282
  /* Perform the test */
283
0
  if (sieve_operation_is(renv->oprtn, tst_size_over_operation)) {
284
0
    sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "size :over test");
285
286
0
    if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING)) {
287
0
      sieve_runtime_trace_descend(renv);
288
289
0
      sieve_runtime_trace(
290
0
        renv, 0, "comparing message size %llu",
291
0
        (unsigned long long)mail_size);
292
0
      sieve_runtime_trace(
293
0
        renv, 0, "with upper limit %llu",
294
0
        (unsigned long long)limit);
295
0
    }
296
297
0
    sieve_interpreter_set_test_result(renv->interp,
298
0
              (mail_size > limit));
299
0
  } else {
300
0
    sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS,
301
0
            "size :under test");
302
303
0
    if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING)) {
304
0
      sieve_runtime_trace_descend(renv);
305
306
0
      sieve_runtime_trace(
307
0
        renv, 0, "comparing message size %llu",
308
0
        (unsigned long long)mail_size);
309
0
      sieve_runtime_trace(
310
0
        renv, 0, "with lower limit %llu",
311
0
        (unsigned long long)limit);
312
0
    }
313
314
0
    sieve_interpreter_set_test_result(renv->interp,
315
0
              (mail_size < limit));
316
0
  }
317
0
  return SIEVE_EXEC_OK;
318
0
}