Coverage Report

Created: 2026-08-08 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/relational/ext-relational-common.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
/* Syntax:
4
5
     MATCH-TYPE =/ COUNT / VALUE
6
     COUNT = ":count" relational-match
7
     VALUE = ":value" relational-match
8
     relational-match = DQUOTE ( "gt" / "ge" / "lt"
9
                               / "le" / "eq" / "ne" ) DQUOTE
10
 */
11
12
#include "lib.h"
13
#include "str.h"
14
#include "str-sanitize.h"
15
16
#include "sieve-common.h"
17
#include "sieve-ast.h"
18
#include "sieve-code.h"
19
#include "sieve-extensions.h"
20
#include "sieve-commands.h"
21
#include "sieve-comparators.h"
22
#include "sieve-match-types.h"
23
#include "sieve-validator.h"
24
#include "sieve-generator.h"
25
#include "sieve-interpreter.h"
26
27
#include "ext-relational-common.h"
28
29
/*
30
 * Forward declarations
31
 */
32
33
const struct sieve_match_type_def *rel_match_types[];
34
35
/*
36
 * Validation
37
 */
38
39
bool mcht_relational_validate(struct sieve_validator *valdtr,
40
            struct sieve_ast_argument **arg,
41
            struct sieve_match_type_context *ctx)
42
0
{
43
0
  struct sieve_match_type *mcht;
44
0
  enum relational_match rel_match = REL_MATCH_INVALID;
45
0
  pool_t pool = sieve_ast_argument_pool(ctx->argument);
46
0
  string_t *rel_match_ident;
47
48
  /* Check syntax:
49
       relational-match = DQUOTE ( "gt" / "ge" / "lt"
50
                                 / "le" / "eq" / "ne" ) DQUOTE
51
52
     So, actually this must be a constant string and it is implemented as
53
     such.
54
   */
55
56
  /* Did we get a string in the first place? */
57
0
  if (*arg == NULL || (*arg)->type != SAAT_STRING) {
58
0
    sieve_argument_validate_error(
59
0
      valdtr, (*arg == NULL ? ctx->argument : *arg),
60
0
      "the :%s match-type requires a constant string argument being "
61
0
      "one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", "
62
0
      "but %s was found",
63
0
      sieve_match_type_name(ctx->match_type),
64
0
      (*arg == NULL ?
65
0
       "none" : sieve_ast_argument_name(*arg)));
66
0
    return FALSE;
67
0
  }
68
69
  /* Check the relational match id */
70
71
0
  rel_match_ident = sieve_ast_argument_str(*arg);
72
0
  if (str_len(rel_match_ident) == 2) {
73
0
    const char *rel_match_id = str_c(rel_match_ident);
74
75
0
    switch (rel_match_id[0]) {
76
    /* "gt" or "ge" */
77
0
    case 'g':
78
0
      switch (rel_match_id[1]) {
79
0
      case 't':
80
0
        rel_match = REL_MATCH_GREATER;
81
0
        break;
82
0
      case 'e':
83
0
        rel_match = REL_MATCH_GREATER_EQUAL;
84
0
        break;
85
0
      default:
86
0
        rel_match = REL_MATCH_INVALID;
87
0
      }
88
0
      break;
89
    /* "lt" or "le" */
90
0
    case 'l':
91
0
      switch (rel_match_id[1]) {
92
0
      case 't':
93
0
        rel_match = REL_MATCH_LESS;
94
0
        break;
95
0
      case 'e':
96
0
        rel_match = REL_MATCH_LESS_EQUAL;
97
0
        break;
98
0
      default:
99
0
        rel_match = REL_MATCH_INVALID;
100
0
      }
101
0
      break;
102
    /* "eq" */
103
0
    case 'e':
104
0
      if (rel_match_id[1] == 'q')
105
0
        rel_match = REL_MATCH_EQUAL;
106
0
      else
107
0
        rel_match = REL_MATCH_INVALID;
108
0
      break;
109
    /* "ne" */
110
0
    case 'n':
111
0
      if (rel_match_id[1] == 'e')
112
0
        rel_match = REL_MATCH_NOT_EQUAL;
113
0
      else
114
0
        rel_match = REL_MATCH_INVALID;
115
0
      break;
116
    /* invalid */
117
0
    default:
118
0
      rel_match = REL_MATCH_INVALID;
119
0
    }
120
0
  }
121
122
0
  if (rel_match >= REL_MATCH_INVALID) {
123
0
    sieve_argument_validate_error(
124
0
      valdtr, *arg,
125
0
      "the :%s match-type requires a constant string argument being "
126
0
      "one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", "
127
0
      "but \"%s\" was found",
128
0
      sieve_match_type_name(ctx->match_type),
129
0
      str_sanitize(str_c(rel_match_ident), 32));
130
0
    return FALSE;
131
0
  }
132
133
  /* Delete argument */
134
0
  *arg = sieve_ast_arguments_detach(*arg, 1);
135
136
  /* Not used just yet */
137
0
  ctx->ctx_data = (void *)rel_match;
138
139
  /* Override the actual match type with a parameter-specific one
140
   * FIXME: ugly!
141
   */
142
0
  mcht = p_new(pool, struct sieve_match_type, 1);
143
0
  mcht->object.ext = ctx->match_type->object.ext;
144
0
  SIEVE_OBJECT_SET_DEF(mcht, rel_match_types[
145
0
    REL_MATCH_INDEX(ctx->match_type->object.def->code, rel_match)]);
146
0
  ctx->match_type = mcht;
147
148
0
  return TRUE;
149
0
}
150
151
/*
152
 * Relational match-type operand
153
 */
154
155
const struct sieve_match_type_def *rel_match_types[] = {
156
  &rel_match_value_gt, &rel_match_value_ge, &rel_match_value_lt,
157
  &rel_match_value_le, &rel_match_value_eq, &rel_match_value_ne,
158
  &rel_match_count_gt, &rel_match_count_ge, &rel_match_count_lt,
159
  &rel_match_count_le, &rel_match_count_eq, &rel_match_count_ne,
160
};
161
162
static const struct sieve_extension_objects ext_match_types =
163
  SIEVE_EXT_DEFINE_MATCH_TYPES(rel_match_types);
164
165
const struct sieve_operand_def rel_match_type_operand = {
166
  .name = "relational match",
167
  .ext_def = &relational_extension,
168
  .class = &sieve_match_type_operand_class,
169
  .interface = &ext_match_types,
170
};