1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2  */
3 
4 #include "lib.h"
5 
6 #include "sieve.h"
7 #include "sieve-code.h"
8 #include "sieve-commands.h"
9 #include "sieve-binary.h"
10 #include "sieve-generator.h"
11 #include "sieve-interpreter.h"
12 #include "sieve-dump.h"
13 
14 #include "testsuite-common.h"
15 #include "testsuite-substitutions.h"
16 
17 /*
18  * Forward declarations
19  */
20 
21 void testsuite_opr_substitution_emit
22 	(struct sieve_binary_block *sblock, const struct testsuite_substitution *tsub,
23 		const char *param);
24 
25 /*
26  * Testsuite substitutions
27  */
28 
29 /* FIXME: make this extendible */
30 
31 enum {
32 	TESTSUITE_SUBSTITUTION_FILE,
33 };
34 
35 static const struct testsuite_substitution_def testsuite_file_substitution;
36 
37 static const struct testsuite_substitution_def *substitutions[] = {
38 	&testsuite_file_substitution,
39 };
40 
41 static const unsigned int substitutions_count = N_ELEMENTS(substitutions);
42 
43 static inline const struct testsuite_substitution_def *
testsuite_substitution_get(unsigned int code)44 testsuite_substitution_get
45 (unsigned int code)
46 {
47 	if ( code >= substitutions_count )
48 		return NULL;
49 
50 	return substitutions[code];
51 }
52 
testsuite_substitution_create(struct sieve_ast * ast,const char * identifier)53 static const struct testsuite_substitution *testsuite_substitution_create
54 (struct sieve_ast *ast, const char *identifier)
55 {
56 	unsigned int i;
57 
58 	for ( i = 0; i < substitutions_count; i++ ) {
59 		if ( strcasecmp(substitutions[i]->obj_def.identifier, identifier) == 0 ) {
60 			const struct testsuite_substitution_def *tsub_def = substitutions[i];
61 			struct testsuite_substitution *tsub;
62 
63 			tsub = p_new(sieve_ast_pool(ast), struct testsuite_substitution, 1);
64 			tsub->object.def = &tsub_def->obj_def;
65 			tsub->object.ext = testsuite_ext;
66 			tsub->def = tsub_def;
67 
68 			return tsub;
69 		}
70 	}
71 
72 	return NULL;
73 }
74 
75 /*
76  * Substitution argument
77  */
78 
79 static bool arg_testsuite_substitution_generate
80 	(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
81 		struct sieve_command *context);
82 
83 struct _testsuite_substitution_context {
84 	const struct testsuite_substitution *tsub;
85 	const char *param;
86 };
87 
88 const struct sieve_argument_def testsuite_substitution_argument = {
89 	.identifier = "@testsuite-substitution",
90 	.generate = arg_testsuite_substitution_generate
91 };
92 
testsuite_substitution_argument_create(struct sieve_validator * valdtr ATTR_UNUSED,struct sieve_ast * ast,unsigned int source_line,const char * substitution,const char * param)93 struct sieve_ast_argument *testsuite_substitution_argument_create
94 (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast *ast,
95 	unsigned int source_line, const char *substitution, const char *param)
96 {
97 	const struct testsuite_substitution *tsub;
98 	struct _testsuite_substitution_context *tsctx;
99 	struct sieve_ast_argument *arg;
100 	pool_t pool;
101 
102 	tsub = testsuite_substitution_create(ast, substitution);
103 	if ( tsub == NULL )
104 		return NULL;
105 
106 	arg = sieve_ast_argument_create(ast, source_line);
107 	arg->type = SAAT_STRING;
108 
109 	pool = sieve_ast_pool(ast);
110 	tsctx = p_new(pool, struct _testsuite_substitution_context, 1);
111 	tsctx->tsub = tsub;
112 	tsctx->param = p_strdup(pool, param);
113 
114 	arg->argument = sieve_argument_create
115 		(ast, &testsuite_substitution_argument, testsuite_ext, 0);
116 	arg->argument->data = (void *) tsctx;
117 
118 	return arg;
119 }
120 
arg_testsuite_substitution_generate(const struct sieve_codegen_env * cgenv,struct sieve_ast_argument * arg,struct sieve_command * context ATTR_UNUSED)121 static bool arg_testsuite_substitution_generate
122 (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
123 	struct sieve_command *context ATTR_UNUSED)
124 {
125 	struct _testsuite_substitution_context *tsctx =
126 		(struct _testsuite_substitution_context *) arg->argument->data;
127 
128 	testsuite_opr_substitution_emit(cgenv->sblock, tsctx->tsub, tsctx->param);
129 
130 	return TRUE;
131 }
132 
133 /*
134  * Substitution operand
135  */
136 
137 static bool opr_substitution_dump
138 	(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd,
139 		sieve_size_t *address);
140 static int opr_substitution_read_value
141 	(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
142 		sieve_size_t *address, string_t **str);
143 
144 const struct sieve_opr_string_interface testsuite_substitution_interface = {
145 	opr_substitution_dump,
146 	opr_substitution_read_value
147 };
148 
149 const struct sieve_operand_def testsuite_substitution_operand = {
150 	.name = "test-substitution",
151 	.ext_def = &testsuite_extension,
152 	.code = TESTSUITE_OPERAND_SUBSTITUTION,
153 	.class = &string_class,
154 	.interface = &testsuite_substitution_interface
155 };
156 
testsuite_opr_substitution_emit(struct sieve_binary_block * sblock,const struct testsuite_substitution * tsub,const char * param)157 void testsuite_opr_substitution_emit
158 (struct sieve_binary_block *sblock, const struct testsuite_substitution *tsub,
159 	const char *param)
160 {
161 	/* Default variable storage */
162 	(void) sieve_operand_emit
163 		(sblock, testsuite_ext, &testsuite_substitution_operand);
164 	(void) sieve_binary_emit_unsigned(sblock, tsub->object.def->code);
165 	(void) sieve_binary_emit_cstring(sblock, param);
166 }
167 
opr_substitution_dump(const struct sieve_dumptime_env * denv,const struct sieve_operand * oprnd,sieve_size_t * address)168 static bool opr_substitution_dump
169 (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd,
170 	sieve_size_t *address)
171 {
172 	unsigned int code = 0;
173 	const struct testsuite_substitution_def *tsub;
174 	string_t *param;
175 
176 	if ( !sieve_binary_read_unsigned(denv->sblock, address, &code) )
177 		return FALSE;
178 
179 	tsub = testsuite_substitution_get(code);
180 	if ( tsub == NULL )
181 		return FALSE;
182 
183 	if ( !sieve_binary_read_string(denv->sblock, address, &param) )
184 		return FALSE;
185 
186 	if ( oprnd->field_name != NULL )
187 		sieve_code_dumpf(denv, "%s: TEST_SUBS %%{%s:%s}",
188 			oprnd->field_name, tsub->obj_def.identifier, str_c(param));
189 	else
190 		sieve_code_dumpf(denv, "TEST_SUBS %%{%s:%s}",
191 			tsub->obj_def.identifier, str_c(param));
192 	return TRUE;
193 }
194 
opr_substitution_read_value(const struct sieve_runtime_env * renv,const struct sieve_operand * oprnd ATTR_UNUSED,sieve_size_t * address,string_t ** str_r)195 static int opr_substitution_read_value
196 (const struct sieve_runtime_env *renv,
197 	const struct sieve_operand *oprnd ATTR_UNUSED, sieve_size_t *address,
198 	string_t **str_r)
199 {
200 	const struct testsuite_substitution_def *tsub;
201 	unsigned int code = 0;
202 	string_t *param;
203 
204 	if ( !sieve_binary_read_unsigned(renv->sblock, address, &code) )
205 		return SIEVE_EXEC_BIN_CORRUPT;
206 
207 	tsub = testsuite_substitution_get(code);
208 	if ( tsub == NULL )
209 		return SIEVE_EXEC_FAILURE;
210 
211 	/* Parameter str can be NULL if we are requested to only skip and not
212 	 * actually read the argument.
213 	 */
214 	if ( str_r == NULL ) {
215 		if ( !sieve_binary_read_string(renv->sblock, address, NULL) )
216 			return SIEVE_EXEC_BIN_CORRUPT;
217 
218 		return SIEVE_EXEC_OK;
219 	}
220 
221 	if ( !sieve_binary_read_string(renv->sblock, address, &param) )
222 		return SIEVE_EXEC_BIN_CORRUPT;
223 
224 	if ( !tsub->get_value(str_c(param), str_r) )
225 		return SIEVE_EXEC_FAILURE;
226 
227 	return SIEVE_EXEC_OK;
228 }
229 
230 /*
231  * Testsuite substitution definitions
232  */
233 
234 static bool testsuite_file_substitution_get_value
235 	(const char *param, string_t **result);
236 
237 static const struct testsuite_substitution_def
238 testsuite_file_substitution = {
239 	SIEVE_OBJECT("file",
240 		&testsuite_substitution_operand,
241 		TESTSUITE_SUBSTITUTION_FILE),
242 	.get_value = testsuite_file_substitution_get_value
243 };
244 
testsuite_file_substitution_get_value(const char * param,string_t ** result)245 static bool testsuite_file_substitution_get_value
246 (const char *param, string_t **result)
247 {
248 	*result = t_str_new(256);
249 
250 	str_printfa(*result, "[FILE: %s]", param);
251 	return TRUE;
252 }
253 
254