Coverage Report

Created: 2026-07-30 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/spamvirustest/ext-spamvirustest-settings.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "array.h"
5
#include "settings.h"
6
#include "settings-parser.h"
7
8
#include "ext-spamvirustest-settings.h"
9
10
#include <ctype.h>
11
12
static bool
13
ext_spamtest_settings_check(void *_set, pool_t pool, const char **error_r);
14
static bool
15
ext_virustest_settings_check(void *_set, pool_t pool, const char **error_r);
16
17
#undef DEF
18
#define DEF(type, name) \
19
  SETTING_DEFINE_STRUCT_##type("sieve_spamtest_"#name, name, \
20
             struct ext_spamvirustest_settings)
21
22
static const struct setting_define ext_spamtest_setting_defines[] = {
23
  DEF(STR, status_header),
24
  DEF(ENUM, status_type),
25
26
  DEF(STR, score_max_header),
27
  DEF(STR, score_max_value),
28
29
  { .type = SET_STRLIST, .key = "sieve_spamtest_text_value",
30
    .offset = offsetof(struct ext_spamvirustest_settings,
31
           text_value) },
32
33
  SETTING_DEFINE_LIST_END,
34
};
35
36
#undef DEF
37
#define DEF(type, name) \
38
  SETTING_DEFINE_STRUCT_##type("sieve_virustest_"#name, name, \
39
             struct ext_spamvirustest_settings)
40
41
static const struct setting_define ext_virustest_setting_defines[] = {
42
  DEF(STR, status_header),
43
  DEF(ENUM, status_type),
44
45
  DEF(STR, score_max_header),
46
  DEF(STR, score_max_value),
47
48
  { .type = SET_STRLIST, .key = "sieve_virustest_text_value",
49
    .offset = offsetof(struct ext_spamvirustest_settings,
50
           text_value) },
51
52
  SETTING_DEFINE_LIST_END,
53
};
54
55
static const struct ext_spamvirustest_settings ext_spamvirustest_default_settings = {
56
  .status_header = "",
57
  .status_type = "score:strlen:text",
58
59
  .score_max_header = "",
60
  .score_max_value = "",
61
62
  .text_value = ARRAY_INIT,
63
};
64
65
const struct setting_parser_info ext_spamtest_setting_parser_info = {
66
  .name = "sieve_spamtest",
67
68
  .defines = ext_spamtest_setting_defines,
69
  .defaults = &ext_spamvirustest_default_settings,
70
71
  .struct_size = sizeof(struct ext_spamvirustest_settings),
72
73
  .check_func = ext_spamtest_settings_check,
74
75
  .pool_offset1 = 1 + offsetof(struct ext_spamvirustest_settings, pool),
76
};
77
78
const struct setting_parser_info ext_virustest_setting_parser_info = {
79
  .name = "sieve_virustest",
80
81
  .defines = ext_virustest_setting_defines,
82
  .defaults = &ext_spamvirustest_default_settings,
83
84
  .struct_size = sizeof(struct ext_spamvirustest_settings),
85
86
  .check_func = ext_virustest_settings_check,
87
88
  .pool_offset1 = 1 + offsetof(struct ext_spamvirustest_settings, pool),
89
};
90
91
/* <settings checks> */
92
bool ext_spamvirustest_parse_decimal_value(const char *str_value,
93
             float *value_r, const char **error_r)
94
0
{
95
0
  const char *p = str_value;
96
0
  float value;
97
0
  float sign = 1;
98
0
  int digits;
99
100
0
  if (*p == '\0') {
101
0
    *error_r = "empty value";
102
0
    return FALSE;
103
0
  }
104
105
0
  if (*p == '+' || *p == '-') {
106
0
    if (*p == '-')
107
0
      sign = -1;
108
0
    p++;
109
0
  }
110
111
0
  value = 0;
112
0
  digits = 0;
113
0
  while (i_isdigit(*p)) {
114
0
    value = value*10 + (*p-'0');
115
0
    if (digits++ > 4) {
116
0
      *error_r = t_strdup_printf(
117
0
        "Decimal value has too many digits before radix point: %s",
118
0
        str_value);
119
0
      return FALSE;
120
0
    }
121
0
    p++;
122
0
  }
123
124
0
  if (*p == '.' || *p == ',') {
125
0
    float radix = .1;
126
0
    p++;
127
128
0
    digits = 0;
129
0
    while (i_isdigit(*p)) {
130
0
      value = value + (*p-'0')*radix;
131
132
0
      if (digits++ > 4) {
133
0
        *error_r = t_strdup_printf(
134
0
          "Decimal value has too many digits after radix point: %s",
135
0
          str_value);
136
0
        return FALSE;
137
0
      }
138
0
      radix /= 10;
139
0
      p++;
140
0
    }
141
0
  }
142
143
0
  if (*p != '\0') {
144
0
    *error_r = t_strdup_printf(
145
0
      "Invalid decimal point value: %s", str_value);
146
0
    return FALSE;
147
0
  }
148
149
0
  *value_r = value * sign;
150
0
  return TRUE;
151
0
}
152
153
static bool
154
ext_spamvirustest_settings_check(void *_set, bool virustest,
155
         pool_t pool ATTR_UNUSED, const char **error_r)
156
0
{
157
0
  struct ext_spamvirustest_settings *set = _set;
158
0
  const char *ext_name = (virustest ? "virustest" : "spamtest");
159
0
  const char *error;
160
161
0
  if (*set->status_header == '\0')
162
0
    return TRUE;
163
164
0
  if (*set->status_type == '\0' ||
165
0
      strcmp(set->status_type, "score") == 0)
166
0
    set->parsed.status_type = EXT_SPAMVIRUSTEST_STATUS_TYPE_SCORE;
167
0
  else if (strcmp(set->status_type, "strlen") == 0)
168
0
    set->parsed.status_type = EXT_SPAMVIRUSTEST_STATUS_TYPE_STRLEN;
169
0
  else if (strcmp(set->status_type, "text") == 0)
170
0
    set->parsed.status_type = EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT;
171
0
  else {
172
0
    *error_r = t_strdup_printf("Invalid status type '%s'",
173
0
             set->status_type);
174
0
    return FALSE;
175
0
  }
176
177
0
  if (set->parsed.status_type != EXT_SPAMVIRUSTEST_STATUS_TYPE_TEXT) {
178
0
    if (*set->score_max_header != '\0' &&
179
0
        *set->score_max_value != '\0') {
180
0
      *error_r = t_strdup_printf(
181
0
        "sieve_%s_score_max_header and sieve_%s_score_max_value "
182
0
        "cannot both be configured",
183
0
        ext_name, ext_name);
184
0
      return FALSE;
185
0
    }
186
0
    if (*set->score_max_header == '\0' &&
187
0
        *set->score_max_value == '\0') {
188
0
      *error_r = t_strdup_printf(
189
0
        "None of sieve_%s_score_max_header or "
190
0
        "sieve_%s_score_max_value is configured",
191
0
        ext_name, ext_name);
192
0
      return FALSE;
193
0
    }
194
0
    if (*set->score_max_value != '\0' &&
195
0
        !ext_spamvirustest_parse_decimal_value(
196
0
      set->score_max_value, &set->parsed.score_max_value,
197
0
      &error)) {
198
0
      *error_r = t_strdup_printf(
199
0
        "Invalid max score value specification "
200
0
        "'%s': %s", set->score_max_value, error);
201
0
      return FALSE;
202
0
    }
203
0
  } else if (array_is_created(&set->text_value)) {
204
0
    const char *const *tvalues;
205
0
    unsigned int tvalues_count, i;
206
0
    unsigned int tv_index_max = (virustest ? 5 : 10);
207
208
0
    tvalues = array_get(&set->text_value, &tvalues_count);
209
0
    i_assert(tvalues_count % 2 == 0);
210
0
    for (i = 0; i < tvalues_count; i += 2) {
211
0
      unsigned int tv_index;
212
213
0
      if (str_to_uint(tvalues[i], &tv_index) < 0) {
214
0
        *error_r = t_strdup_printf(
215
0
          "Invalid text value index '%s'",
216
0
          tvalues[i]);
217
0
        return FALSE;
218
0
      }
219
0
      if (tv_index > tv_index_max) {
220
0
        *error_r = t_strdup_printf(
221
0
          "Text value index out of range "
222
0
          "(%u > %u)", tv_index, tv_index_max);
223
0
        return FALSE;
224
0
      }
225
0
      set->parsed.text_values[tv_index] = tvalues[i + 1]; 
226
0
    }
227
0
    set->parsed.score_max_value = 1;
228
0
  }
229
230
0
  return TRUE;
231
0
}
232
233
static bool
234
ext_spamtest_settings_check(void *_set, pool_t pool ATTR_UNUSED,
235
          const char **error_r)
236
0
{
237
0
  return ext_spamvirustest_settings_check(_set, FALSE, pool, error_r);
238
0
}
239
240
static bool
241
ext_virustest_settings_check(void *_set, pool_t pool ATTR_UNUSED,
242
           const char **error_r)
243
0
{
244
0
  return ext_spamvirustest_settings_check(_set, TRUE, pool, error_r);
245
0
}
246
/* </settings checks> */
247