Coverage Report

Created: 2026-08-13 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/sieve-match.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "mempool.h"
5
#include "hash.h"
6
#include "array.h"
7
#include "str-sanitize.h"
8
9
#include "sieve-extensions.h"
10
#include "sieve-commands.h"
11
#include "sieve-stringlist.h"
12
#include "sieve-code.h"
13
#include "sieve-binary.h"
14
#include "sieve-validator.h"
15
#include "sieve-generator.h"
16
#include "sieve-interpreter.h"
17
#include "sieve-dump.h"
18
#include "sieve-comparators.h"
19
#include "sieve-match-types.h"
20
#include "sieve-runtime-trace.h"
21
22
#include "sieve-match.h"
23
24
/*
25
 * Matching implementation
26
 */
27
28
struct sieve_match_context *
29
sieve_match_begin(const struct sieve_runtime_env *renv,
30
      const struct sieve_match_type *mcht,
31
      const struct sieve_comparator *cmp)
32
0
{
33
0
  struct sieve_match_context *mctx;
34
0
  pool_t pool;
35
36
  /* Reject unimplemented match-type */
37
0
  if (mcht->def == NULL || (mcht->def->match == NULL &&
38
0
      mcht->def->match_keys == NULL && mcht->def->match_key == NULL))
39
0
      return NULL;
40
41
  /* Create match context */
42
0
  pool = pool_alloconly_create("sieve_match_context", 1024);
43
0
  mctx = p_new(pool, struct sieve_match_context, 1);
44
0
  mctx->pool = pool;
45
0
  mctx->runenv = renv;
46
0
  mctx->match_type = mcht;
47
0
  mctx->comparator = cmp;
48
0
  mctx->exec_status = SIEVE_EXEC_OK;
49
0
  mctx->trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
50
51
  /* Trace */
52
0
  if (mctx->trace) {
53
0
    sieve_runtime_trace_descend(renv);
54
0
    sieve_runtime_trace(
55
0
      renv, 0, "starting ':%s' match with '%s' comparator:",
56
0
      sieve_match_type_name(mcht),
57
0
      sieve_comparator_name(cmp));
58
0
  }
59
60
  /* Initialize match type */
61
0
  if (mcht->def != NULL && mcht->def->match_init != NULL)
62
0
    mcht->def->match_init(mctx);
63
64
0
  return mctx;
65
0
}
66
67
int sieve_match_value(struct sieve_match_context *mctx,
68
         const char *value, size_t value_size,
69
         struct sieve_stringlist *key_list)
70
0
{
71
0
  const struct sieve_match_type *mcht = mctx->match_type;
72
0
  const struct sieve_runtime_env *renv = mctx->runenv;
73
0
  int match, ret;
74
75
0
  if (mctx->trace) {
76
0
    sieve_runtime_trace(renv, 0, "matching value '%s'",
77
0
            str_sanitize(value, 80));
78
0
  }
79
80
  /* Match to key values */
81
82
0
  sieve_stringlist_reset(key_list);
83
84
0
  if (mctx->trace)
85
0
    sieve_stringlist_set_trace(key_list, TRUE);
86
87
0
  sieve_runtime_trace_descend(renv);
88
89
0
  if (mcht->def->match_keys != NULL) {
90
    /* Call match-type's own key match handler */
91
0
    match = mcht->def->match_keys(mctx, value, value_size,
92
0
                key_list);
93
0
  } else {
94
0
    string_t *key_item = NULL;
95
96
    /* Default key match loop */
97
0
    match = 0;
98
0
    while (match == 0 &&
99
0
           (ret = sieve_stringlist_next_item(
100
0
        key_list, &key_item)) > 0) T_BEGIN {
101
0
      match = mcht->def->match_key(
102
0
        mctx, value, value_size,
103
0
        str_c(key_item), str_len(key_item));
104
0
      if (mctx->trace) {
105
0
        sieve_runtime_trace(
106
0
          renv, 0, "with key '%s' => %d",
107
0
          str_sanitize(str_c(key_item), 80),
108
0
          match);
109
0
      }
110
0
    } T_END;
111
112
0
    if (ret < 0) {
113
0
      mctx->exec_status = key_list->exec_status;
114
0
      match = -1;
115
0
    }
116
0
  }
117
118
0
  sieve_runtime_trace_ascend(renv);
119
120
0
  if (mctx->match_status < 0 || match < 0)
121
0
    mctx->match_status = -1;
122
0
  else {
123
0
    mctx->match_status = (mctx->match_status > match ?
124
0
              mctx->match_status : match);
125
0
  }
126
0
  return match;
127
0
}
128
129
int sieve_match_end(struct sieve_match_context **mctx, int *exec_status)
130
0
{
131
0
  const struct sieve_match_type *mcht = (*mctx)->match_type;
132
0
  const struct sieve_runtime_env *renv = (*mctx)->runenv;
133
0
  int match = (*mctx)->match_status;
134
135
0
  if (mcht->def != NULL && mcht->def->match_deinit != NULL)
136
0
    mcht->def->match_deinit(*mctx);
137
138
0
  if (exec_status != NULL)
139
0
    *exec_status = (*mctx)->exec_status;
140
141
0
  pool_unref(&(*mctx)->pool);
142
143
0
  sieve_runtime_trace(
144
0
    renv, SIEVE_TRLVL_MATCHING, "finishing match with result: %s",
145
0
    (match > 0 ? "matched" :
146
0
     (match < 0 ? "error" : "not matched")));
147
0
  sieve_runtime_trace_ascend(renv);
148
149
0
  return match;
150
0
}
151
152
int sieve_match(const struct sieve_runtime_env *renv,
153
    const struct sieve_match_type *mcht,
154
    const struct sieve_comparator *cmp,
155
    struct sieve_stringlist *value_list,
156
    struct sieve_stringlist *key_list, int *exec_status)
157
0
{
158
0
  struct sieve_match_context *mctx;
159
0
  string_t *value_item = NULL;
160
0
  int match, ret;
161
162
0
  if ((mctx = sieve_match_begin(renv, mcht, cmp)) == NULL)
163
0
    return 0;
164
165
  /* Match value to keys */
166
167
0
  sieve_stringlist_reset(value_list);
168
0
  if (mctx->trace)
169
0
    sieve_stringlist_set_trace(value_list, TRUE);
170
171
0
  if (mcht->def->match != NULL) {
172
    /* Call match-type's match handler */
173
0
    match = mctx->match_status =
174
0
      mcht->def->match(mctx, value_list, key_list);
175
0
  } else {
176
    /* Default value match loop */
177
0
    match = 0;
178
0
    while (match == 0 &&
179
0
           (ret = sieve_stringlist_next_item(
180
0
        value_list, &value_item)) > 0) {
181
182
0
      match = sieve_match_value(
183
0
        mctx, str_c(value_item), str_len(value_item),
184
0
        key_list);
185
0
    }
186
187
0
    if (ret < 0) {
188
0
      mctx->exec_status = value_list->exec_status;
189
0
      match = -1;
190
0
    }
191
0
  }
192
193
0
  (void)sieve_match_end(&mctx, exec_status);
194
0
  return match;
195
0
}
196
197
/*
198
 * Reading match operands
199
 */
200
201
int sieve_match_opr_optional_dump(const struct sieve_dumptime_env *denv,
202
          sieve_size_t *address, int *opt_code)
203
0
{
204
0
  int _opt_code = 0;
205
0
  bool final = FALSE, opok = TRUE;
206
207
0
  if (opt_code == NULL) {
208
0
    opt_code = &_opt_code;
209
0
    final = TRUE;
210
0
  }
211
212
0
  while (opok) {
213
0
    int opt;
214
215
0
    if ((opt = sieve_opr_optional_dump(denv, address,
216
0
               opt_code)) <= 0)
217
0
      return opt;
218
219
0
    switch (*opt_code) {
220
0
    case SIEVE_MATCH_OPT_COMPARATOR:
221
0
      opok = sieve_opr_comparator_dump(denv, address);
222
0
      break;
223
0
    case SIEVE_MATCH_OPT_MATCH_TYPE:
224
0
      opok = sieve_opr_match_type_dump(denv, address);
225
0
      break;
226
0
    default:
227
0
      return (final ? -1 : 1);
228
0
    }
229
0
  }
230
0
  return -1;
231
0
}
232
233
int sieve_match_opr_optional_read(const struct sieve_runtime_env *renv,
234
          sieve_size_t *address, int *opt_code,
235
          int *exec_status,
236
          struct sieve_comparator *cmp,
237
          struct sieve_match_type *mcht)
238
0
{
239
0
  int _opt_code = 0;
240
0
  bool final = FALSE;
241
0
  int status = SIEVE_EXEC_OK;
242
243
0
  if (opt_code == NULL) {
244
0
    opt_code = &_opt_code;
245
0
    final = TRUE;
246
0
  }
247
248
0
  if (exec_status != NULL)
249
0
    *exec_status = SIEVE_EXEC_OK;
250
251
0
  while (status == SIEVE_EXEC_OK) {
252
0
    int opt;
253
254
0
    if ((opt = sieve_opr_optional_read(
255
0
      renv, address, opt_code)) <= 0) {
256
0
      if (opt < 0 && exec_status != NULL)
257
0
        *exec_status = SIEVE_EXEC_BIN_CORRUPT;
258
0
      return opt;
259
0
    }
260
261
0
    switch (*opt_code) {
262
0
    case SIEVE_MATCH_OPT_COMPARATOR:
263
0
      if (cmp == NULL) {
264
0
        sieve_runtime_trace_error(
265
0
          renv, "unexpected comparator operand");
266
0
        if (exec_status != NULL)
267
0
          *exec_status = SIEVE_EXEC_BIN_CORRUPT;
268
0
        return -1;
269
0
      }
270
0
      status = sieve_opr_comparator_read(renv, address, cmp);
271
0
      break;
272
0
    case SIEVE_MATCH_OPT_MATCH_TYPE:
273
0
      if (mcht == NULL) {
274
0
        sieve_runtime_trace_error(
275
0
          renv, "unexpected match-type operand");
276
0
        if (exec_status != NULL)
277
0
          *exec_status = SIEVE_EXEC_BIN_CORRUPT;
278
0
        return -1;
279
0
      }
280
0
      status = sieve_opr_match_type_read(renv, address, mcht);
281
0
      break;
282
0
    default:
283
0
      if (final) {
284
0
        sieve_runtime_trace_error(
285
0
          renv, "invalid optional operand");
286
0
        if (exec_status != NULL)
287
0
          *exec_status = SIEVE_EXEC_BIN_CORRUPT;
288
0
        return -1;
289
0
      }
290
0
      return 1;
291
0
    }
292
0
  }
293
294
0
  if (exec_status != NULL)
295
0
    *exec_status = status;
296
0
  return -1;
297
0
}