/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 | | |