1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2 */
3
4 #include "sieve-common.h"
5 #include "sieve-commands.h"
6 #include "sieve-stringlist.h"
7 #include "sieve-code.h"
8 #include "sieve-comparators.h"
9 #include "sieve-match-types.h"
10 #include "sieve-validator.h"
11 #include "sieve-generator.h"
12 #include "sieve-interpreter.h"
13 #include "sieve-dump.h"
14 #include "sieve-match.h"
15
16 #include "ext-variables-common.h"
17
18 /*
19 * String test
20 *
21 * Syntax:
22 * string [COMPARATOR] [MATCH-TYPE]
23 * <source: string-list> <key-list: string-list>
24 */
25
26 static bool tst_string_registered
27 (struct sieve_validator *valdtr, const struct sieve_extension *ext,
28 struct sieve_command_registration *cmd_reg);
29 static bool tst_string_validate
30 (struct sieve_validator *valdtr, struct sieve_command *tst);
31 static bool tst_string_generate
32 (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
33
34 const struct sieve_command_def tst_string = {
35 .identifier = "string",
36 .type = SCT_TEST,
37 .positional_args = 2,
38 .subtests = 0,
39 .block_allowed = FALSE,
40 .block_required = FALSE,
41 .registered = tst_string_registered,
42 .validate = tst_string_validate,
43 .generate = tst_string_generate
44 };
45
46 /*
47 * String operation
48 */
49
50 static bool tst_string_operation_dump
51 (const struct sieve_dumptime_env *denv, sieve_size_t *address);
52 static int tst_string_operation_execute
53 (const struct sieve_runtime_env *renv, sieve_size_t *address);
54
55 const struct sieve_operation_def tst_string_operation = {
56 .mnemonic = "STRING",
57 .ext_def = &variables_extension,
58 .code = EXT_VARIABLES_OPERATION_STRING,
59 .dump = tst_string_operation_dump,
60 .execute = tst_string_operation_execute
61 };
62
63 /*
64 * Optional arguments
65 */
66
67 enum tst_string_optional {
68 OPT_END,
69 OPT_COMPARATOR,
70 OPT_MATCH_TYPE
71 };
72
73 /*
74 * Test registration
75 */
76
tst_string_registered(struct sieve_validator * valdtr,const struct sieve_extension * ext ATTR_UNUSED,struct sieve_command_registration * cmd_reg)77 static bool tst_string_registered
78 (struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED,
79 struct sieve_command_registration *cmd_reg)
80 {
81 /* The order of these is not significant */
82 sieve_comparators_link_tag(valdtr, cmd_reg, OPT_COMPARATOR);
83 sieve_match_types_link_tags(valdtr, cmd_reg, OPT_MATCH_TYPE);
84
85 return TRUE;
86 }
87
88 /*
89 * Test validation
90 */
91
tst_string_validate(struct sieve_validator * valdtr,struct sieve_command * tst)92 static bool tst_string_validate
93 (struct sieve_validator *valdtr, struct sieve_command *tst)
94 {
95 struct sieve_ast_argument *arg = tst->first_positional;
96 const struct sieve_match_type mcht_default =
97 SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
98 const struct sieve_comparator cmp_default =
99 SIEVE_COMPARATOR_DEFAULT(i_octet_comparator);
100
101 if ( !sieve_validate_positional_argument
102 (valdtr, tst, arg, "source", 1, SAAT_STRING_LIST) ) {
103 return FALSE;
104 }
105
106 if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
107 return FALSE;
108
109 arg = sieve_ast_argument_next(arg);
110
111 if ( !sieve_validate_positional_argument
112 (valdtr, tst, arg, "key list", 2, SAAT_STRING_LIST) ) {
113 return FALSE;
114 }
115
116 if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
117 return FALSE;
118
119 /* Validate the key argument to a specified match type */
120 return sieve_match_type_validate
121 (valdtr, tst, arg, &mcht_default, &cmp_default);
122 }
123
124 /*
125 * Test generation
126 */
127
tst_string_generate(const struct sieve_codegen_env * cgenv,struct sieve_command * cmd)128 static bool tst_string_generate
129 (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
130 {
131 sieve_operation_emit(cgenv->sblock, cmd->ext, &tst_string_operation);
132
133 /* Generate arguments */
134 if ( !sieve_generate_arguments(cgenv, cmd, NULL) )
135 return FALSE;
136
137 return TRUE;
138 }
139
140 /*
141 * Code dump
142 */
143
tst_string_operation_dump(const struct sieve_dumptime_env * denv,sieve_size_t * address)144 static bool tst_string_operation_dump
145 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
146 {
147 sieve_code_dumpf(denv, "STRING-TEST");
148 sieve_code_descend(denv);
149
150 /* Optional operands */
151 if ( sieve_match_opr_optional_dump(denv, address, NULL) != 0 )
152 return FALSE;
153
154 return
155 sieve_opr_stringlist_dump(denv, address, "source") &&
156 sieve_opr_stringlist_dump(denv, address, "key list");
157 }
158
159 /*
160 * Code execution
161 */
162
163 static int tst_string_stringlist_next_item
164 (struct sieve_stringlist *_strlist, string_t **str_r);
165 static void tst_string_stringlist_reset
166 (struct sieve_stringlist *_strlist);
167 static int tst_string_stringlist_get_length
168 (struct sieve_stringlist *_strlist);
169
170 struct tst_string_stringlist {
171 struct sieve_stringlist strlist;
172
173 struct sieve_stringlist *value_list;
174 };
175
tst_string_stringlist_create(const struct sieve_runtime_env * renv,struct sieve_stringlist * value_list)176 static struct sieve_stringlist *tst_string_stringlist_create
177 (const struct sieve_runtime_env *renv, struct sieve_stringlist *value_list)
178 {
179 struct tst_string_stringlist *strlist;
180
181 strlist = t_new(struct tst_string_stringlist, 1);
182 strlist->strlist.runenv = renv;
183 strlist->strlist.exec_status = SIEVE_EXEC_OK;
184 strlist->strlist.next_item = tst_string_stringlist_next_item;
185 strlist->strlist.reset = tst_string_stringlist_reset;
186 strlist->strlist.get_length = tst_string_stringlist_get_length;
187 strlist->value_list = value_list;
188
189 return &strlist->strlist;
190 }
191
tst_string_stringlist_next_item(struct sieve_stringlist * _strlist,string_t ** str_r)192 static int tst_string_stringlist_next_item
193 (struct sieve_stringlist *_strlist, string_t **str_r)
194 {
195 struct tst_string_stringlist *strlist =
196 (struct tst_string_stringlist *)_strlist;
197
198 return sieve_stringlist_next_item(strlist->value_list, str_r);
199 }
200
tst_string_stringlist_reset(struct sieve_stringlist * _strlist)201 static void tst_string_stringlist_reset
202 (struct sieve_stringlist *_strlist)
203 {
204 struct tst_string_stringlist *strlist =
205 (struct tst_string_stringlist *)_strlist;
206
207 sieve_stringlist_reset(strlist->value_list);
208 }
209
tst_string_stringlist_get_length(struct sieve_stringlist * _strlist)210 static int tst_string_stringlist_get_length
211 (struct sieve_stringlist *_strlist)
212 {
213 struct tst_string_stringlist *strlist =
214 (struct tst_string_stringlist *)_strlist;
215 string_t *item;
216 int length = 0;
217 int ret;
218
219 while ( (ret=sieve_stringlist_next_item(strlist->value_list, &item)) > 0 ) {
220 if ( str_len(item) > 0 )
221 length++;
222 }
223
224 return ( ret < 0 ? -1 : length );
225 }
226
tst_string_operation_execute(const struct sieve_runtime_env * renv,sieve_size_t * address)227 static int tst_string_operation_execute
228 (const struct sieve_runtime_env *renv, sieve_size_t *address)
229 {
230 struct sieve_match_type mcht =
231 SIEVE_MATCH_TYPE_DEFAULT(is_match_type);
232 struct sieve_comparator cmp =
233 SIEVE_COMPARATOR_DEFAULT(i_octet_comparator);
234 struct sieve_stringlist *source, *value_list, *key_list;
235 int match, ret;
236
237 /*
238 * Read operands
239 */
240
241 /* Handle match-type and comparator operands */
242 if ( sieve_match_opr_optional_read
243 (renv, address, NULL, &ret, &cmp, &mcht) < 0 )
244 return ret;
245
246 /* Read source */
247 if ( (ret=sieve_opr_stringlist_read(renv, address, "source", &source)) <= 0 )
248 return ret;
249
250 /* Read key-list */
251 if ( (ret=sieve_opr_stringlist_read(renv, address, "key-list", &key_list))
252 <= 0 )
253 return ret;
254
255 /*
256 * Perform operation
257 */
258
259 sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "string test");
260
261 /* Create wrapper string list wich does not count empty string items */
262 value_list = tst_string_stringlist_create(renv, source);
263
264 /* Perform match */
265 if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) < 0 )
266 return ret;
267
268 /* Set test result for subsequent conditional jump */
269 sieve_interpreter_set_test_result(renv->interp, match > 0);
270 return SIEVE_EXEC_OK;
271 }
272