/src/pigeonhole/src/lib-sieve/tst-address.c
Line | Count | Source |
1 | | /* Copyright (c) Pigeonhole authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "str-sanitize.h" |
5 | | |
6 | | #include "sieve-common.h" |
7 | | #include "sieve-commands.h" |
8 | | #include "sieve-stringlist.h" |
9 | | #include "sieve-code.h" |
10 | | #include "sieve-comparators.h" |
11 | | #include "sieve-match-types.h" |
12 | | #include "sieve-message.h" |
13 | | #include "sieve-address.h" |
14 | | #include "sieve-address-parts.h" |
15 | | #include "sieve-validator.h" |
16 | | #include "sieve-generator.h" |
17 | | #include "sieve-interpreter.h" |
18 | | #include "sieve-dump.h" |
19 | | #include "sieve-match.h" |
20 | | |
21 | | #include <stdio.h> |
22 | | |
23 | | /* |
24 | | * Address test |
25 | | * |
26 | | * Syntax: |
27 | | * address [ADDRESS-PART] [COMPARATOR] [MATCH-TYPE] |
28 | | * <header-list: string-list> <key-list: string-list> |
29 | | */ |
30 | | |
31 | | static bool |
32 | | tst_address_registered(struct sieve_validator *valdtr, |
33 | | const struct sieve_extension *ext, |
34 | | struct sieve_command_registration *cmd_reg); |
35 | | static bool |
36 | | tst_address_validate(struct sieve_validator *valdtr, struct sieve_command *tst); |
37 | | static bool |
38 | | tst_address_generate(const struct sieve_codegen_env *cgenv, |
39 | | struct sieve_command *ctx); |
40 | | |
41 | | const struct sieve_command_def tst_address = { |
42 | | .identifier = "address", |
43 | | .type = SCT_TEST, |
44 | | .positional_args = 2, |
45 | | .subtests = 0, |
46 | | .block_allowed = FALSE, |
47 | | .block_required = FALSE, |
48 | | .registered = tst_address_registered, |
49 | | .validate = tst_address_validate, |
50 | | .generate = tst_address_generate |
51 | | }; |
52 | | |
53 | | /* |
54 | | * Address operation |
55 | | */ |
56 | | |
57 | | static bool |
58 | | tst_address_operation_dump(const struct sieve_dumptime_env *denv, |
59 | | sieve_size_t *address); |
60 | | static int |
61 | | tst_address_operation_execute(const struct sieve_runtime_env *renv, |
62 | | sieve_size_t *address); |
63 | | |
64 | | const struct sieve_operation_def tst_address_operation = { |
65 | | .mnemonic = "ADDRESS", |
66 | | .code = SIEVE_OPERATION_ADDRESS, |
67 | | .dump = tst_address_operation_dump, |
68 | | .execute = tst_address_operation_execute |
69 | | }; |
70 | | |
71 | | /* |
72 | | * Test registration |
73 | | */ |
74 | | |
75 | | static bool |
76 | | tst_address_registered(struct sieve_validator *valdtr, |
77 | | const struct sieve_extension *ext ATTR_UNUSED, |
78 | | struct sieve_command_registration *cmd_reg) |
79 | 0 | { |
80 | | /* The order of these is not significant */ |
81 | 0 | sieve_comparators_link_tag(valdtr, cmd_reg, |
82 | 0 | SIEVE_MATCH_OPT_COMPARATOR); |
83 | 0 | sieve_match_types_link_tags(valdtr, cmd_reg, |
84 | 0 | SIEVE_MATCH_OPT_MATCH_TYPE); |
85 | 0 | sieve_address_parts_link_tags(valdtr, cmd_reg, |
86 | 0 | SIEVE_AM_OPT_ADDRESS_PART); |
87 | 0 | return TRUE; |
88 | 0 | } |
89 | | |
90 | | /* |
91 | | * Validation |
92 | | */ |
93 | | |
94 | | /* List of valid headers: |
95 | | Implementations MUST restrict the address test to headers that contain |
96 | | addresses, but MUST include at least From, To, Cc, Bcc, Sender, |
97 | | Resent-From, and Resent-To, and it SHOULD include any other header that |
98 | | utilizes an "address-list" structured header body. |
99 | | |
100 | | This list explicitly does not contain the envelope-to and return-path |
101 | | headers. The envelope test must be used to test against these addresses. |
102 | | |
103 | | FIXME: this restriction is somewhat odd. Sieve list advises to allow any |
104 | | other header as long as its content matches the address-list grammar. |
105 | | */ |
106 | | static const char *const _allowed_headers[] = { |
107 | | /* Required */ |
108 | | "from", "to", "cc", "bcc", "sender", "resent-from", "resent-to", |
109 | | |
110 | | /* Additional (RFC 822 / RFC 2822) */ |
111 | | "reply-to", "resent-reply-to", "resent-sender", "resent-cc", |
112 | | "resent-bcc", |
113 | | |
114 | | /* Non-standard (RFC 2076, draft-palme-mailext-headers-08.txt) */ |
115 | | "for-approval", "for-handling", "for-comment", "apparently-to", |
116 | | "errors-to", "delivered-to", "return-receipt-to", "x-admin", |
117 | | "read-receipt-to", "x-confirm-reading-to", "return-receipt-requested", |
118 | | "registered-mail-reply-requested-by", "mail-followup-to", |
119 | | "mail-reply-to", "abuse-reports-to", "x-complaints-to", |
120 | | "x-report-abuse-to", |
121 | | |
122 | | /* Undocumented */ |
123 | | "x-beenthere", "x-original-from", "x-original-to", |
124 | | |
125 | | NULL |
126 | | }; |
127 | | |
128 | | static int |
129 | | _header_is_allowed(void *context ATTR_UNUSED, struct sieve_ast_argument *arg) |
130 | 0 | { |
131 | 0 | if (sieve_argument_is_string_literal(arg)) { |
132 | 0 | const char *header = sieve_ast_strlist_strc(arg); |
133 | |
|
134 | 0 | const char *const *hdsp = _allowed_headers; |
135 | 0 | while (*hdsp != NULL) { |
136 | 0 | if (strcasecmp(*hdsp, header) == 0) |
137 | 0 | return 1; |
138 | 0 | hdsp++; |
139 | 0 | } |
140 | 0 | return 0; |
141 | 0 | } |
142 | 0 | return 1; |
143 | 0 | } |
144 | | |
145 | | static bool |
146 | | tst_address_validate(struct sieve_validator *valdtr, struct sieve_command *tst) |
147 | 0 | { |
148 | 0 | struct sieve_ast_argument *arg = tst->first_positional; |
149 | 0 | struct sieve_ast_argument *header; |
150 | 0 | struct sieve_comparator cmp_default = |
151 | 0 | SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); |
152 | 0 | struct sieve_match_type mcht_default = |
153 | 0 | SIEVE_MATCH_TYPE_DEFAULT(is_match_type); |
154 | |
|
155 | 0 | if (!sieve_validate_positional_argument(valdtr, tst, arg, "header list", |
156 | 0 | 1, SAAT_STRING_LIST)) |
157 | 0 | return FALSE; |
158 | | |
159 | 0 | if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) |
160 | 0 | return FALSE; |
161 | | |
162 | 0 | if (!sieve_command_verify_headers_argument(valdtr, arg)) |
163 | 0 | return FALSE; |
164 | | |
165 | | /* Check if supplied header names are allowed |
166 | | FIXME: verify dynamic header names at runtime |
167 | | */ |
168 | 0 | header = arg; |
169 | 0 | if (sieve_ast_stringlist_map(&header, NULL, _header_is_allowed) <= 0) { |
170 | 0 | i_assert(header != NULL); |
171 | 0 | sieve_argument_validate_error( |
172 | 0 | valdtr, header, |
173 | 0 | "specified header '%s' is not allowed for the address test", |
174 | 0 | str_sanitize(sieve_ast_strlist_strc(header), 64)); |
175 | 0 | return FALSE; |
176 | 0 | } |
177 | | |
178 | | /* Check key list */ |
179 | | |
180 | 0 | arg = sieve_ast_argument_next(arg); |
181 | |
|
182 | 0 | if (!sieve_validate_positional_argument(valdtr, tst, arg, "key list", 2, |
183 | 0 | SAAT_STRING_LIST)) |
184 | 0 | return FALSE; |
185 | | |
186 | 0 | if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE)) |
187 | 0 | return FALSE; |
188 | | |
189 | | /* Validate the key argument to a specified match type */ |
190 | 0 | return sieve_match_type_validate(valdtr, tst, arg, |
191 | 0 | &mcht_default, &cmp_default); |
192 | 0 | } |
193 | | |
194 | | /* |
195 | | * Code generation |
196 | | */ |
197 | | |
198 | | static bool |
199 | | tst_address_generate(const struct sieve_codegen_env *cgenv, |
200 | | struct sieve_command *tst) |
201 | 0 | { |
202 | 0 | sieve_operation_emit(cgenv->sblock, NULL, &tst_address_operation); |
203 | | |
204 | | /* Generate arguments */ |
205 | 0 | return sieve_generate_arguments(cgenv, tst, NULL); |
206 | 0 | } |
207 | | |
208 | | /* |
209 | | * Code dump |
210 | | */ |
211 | | |
212 | | static bool |
213 | | tst_address_operation_dump(const struct sieve_dumptime_env *denv, |
214 | | sieve_size_t *address) |
215 | 0 | { |
216 | 0 | sieve_code_dumpf(denv, "ADDRESS"); |
217 | 0 | sieve_code_descend(denv); |
218 | | |
219 | | /* Handle any optional arguments */ |
220 | 0 | if (sieve_message_opr_optional_dump(denv, address, NULL) != 0) |
221 | 0 | return FALSE; |
222 | | |
223 | 0 | return (sieve_opr_stringlist_dump(denv, address, "header list") && |
224 | 0 | sieve_opr_stringlist_dump(denv, address, "key list")); |
225 | 0 | } |
226 | | |
227 | | /* |
228 | | * Code execution |
229 | | */ |
230 | | |
231 | | static int |
232 | | tst_address_operation_execute(const struct sieve_runtime_env *renv, |
233 | | sieve_size_t *address) |
234 | 0 | { |
235 | 0 | struct sieve_comparator cmp = |
236 | 0 | SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); |
237 | 0 | struct sieve_match_type mcht = |
238 | 0 | SIEVE_MATCH_TYPE_DEFAULT(is_match_type); |
239 | 0 | struct sieve_address_part addrp = |
240 | 0 | SIEVE_ADDRESS_PART_DEFAULT(all_address_part); |
241 | 0 | struct sieve_stringlist *hdr_list, *hdr_value_list, *value_list, |
242 | 0 | *key_list; |
243 | 0 | struct sieve_address_list *addr_list; |
244 | 0 | ARRAY_TYPE(sieve_message_override) svmos; |
245 | 0 | int match, ret; |
246 | | |
247 | | /* Read optional operands */ |
248 | 0 | i_zero(&svmos); |
249 | 0 | if (sieve_message_opr_optional_read(renv, address, NULL, &ret, |
250 | 0 | &addrp, &mcht, &cmp, &svmos) < 0) |
251 | 0 | return ret; |
252 | | |
253 | | /* Read header-list */ |
254 | 0 | if ((ret = sieve_opr_stringlist_read(renv, address, "header-list", |
255 | 0 | &hdr_list)) <= 0) |
256 | 0 | return ret; |
257 | | |
258 | | /* Read key-list */ |
259 | 0 | if ((ret = sieve_opr_stringlist_read(renv, address, "key-list", |
260 | 0 | &key_list)) <= 0) |
261 | 0 | return ret; |
262 | | |
263 | 0 | sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "address test"); |
264 | | |
265 | | /* Get header */ |
266 | 0 | sieve_runtime_trace_descend(renv); |
267 | 0 | if ((ret = sieve_message_get_header_fields( |
268 | 0 | renv, hdr_list, &svmos, FALSE, &hdr_value_list)) <= 0) |
269 | 0 | return ret; |
270 | 0 | sieve_runtime_trace_ascend(renv); |
271 | | |
272 | | /* Create value stringlist */ |
273 | 0 | addr_list = sieve_header_address_list_create(renv, hdr_value_list); |
274 | 0 | value_list = sieve_address_part_stringlist_create( |
275 | 0 | renv, &addrp, addr_list); |
276 | | |
277 | | /* Perform match */ |
278 | 0 | if ((match = sieve_match(renv, &mcht, &cmp, |
279 | 0 | value_list, key_list, &ret)) < 0) |
280 | 0 | return ret; |
281 | | |
282 | | /* Set test result for subsequent conditional jump */ |
283 | 0 | sieve_interpreter_set_test_result(renv->interp, match > 0); |
284 | 0 | return SIEVE_EXEC_OK; |
285 | 0 | } |