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