1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2  */
3 
4 #include "lib.h"
5 #include "str.h"
6 #include "string.h"
7 #include "ostream.h"
8 #include "hash.h"
9 #include "mail-storage.h"
10 #include "env-util.h"
11 #include "unlink-directory.h"
12 
13 #include "mail-raw.h"
14 
15 #include "sieve-common.h"
16 #include "sieve-code.h"
17 #include "sieve-message.h"
18 #include "sieve-commands.h"
19 #include "sieve-extensions.h"
20 #include "sieve-binary.h"
21 #include "sieve-validator.h"
22 #include "sieve-generator.h"
23 #include "sieve-interpreter.h"
24 #include "sieve-result.h"
25 #include "sieve-dump.h"
26 
27 #include "testsuite-common.h"
28 #include "testsuite-settings.h"
29 #include "testsuite-objects.h"
30 #include "testsuite-log.h"
31 #include "testsuite-script.h"
32 #include "testsuite-binary.h"
33 #include "testsuite-result.h"
34 #include "testsuite-smtp.h"
35 
36 #include <string.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <time.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 
43 /*
44  * Global data
45  */
46 
47 struct sieve_instance *testsuite_sieve_instance = NULL;
48 char *testsuite_test_path = NULL;
49 
50 /* Test context */
51 
52 static string_t *test_name;
53 static sieve_size_t test_block_end;
54 static unsigned int test_index;
55 static unsigned int test_failures;
56 
57 /* Extension */
58 
59 const struct sieve_extension *testsuite_ext;
60 
61 /*
62  * Validator context
63  */
64 
testsuite_validator_context_initialize(struct sieve_validator * valdtr)65 bool testsuite_validator_context_initialize(struct sieve_validator *valdtr)
66 {
67 	pool_t pool = sieve_validator_pool(valdtr);
68 	struct testsuite_validator_context *ctx =
69 		p_new(pool, struct testsuite_validator_context, 1);
70 
71 	/* Setup object registry */
72 	ctx->object_registrations =
73 		sieve_validator_object_registry_create(valdtr);
74 	testsuite_register_core_objects(ctx);
75 
76 	sieve_validator_extension_set_context(valdtr, testsuite_ext, ctx);
77 
78 	return TRUE;
79 }
80 
81 struct testsuite_validator_context *
testsuite_validator_context_get(struct sieve_validator * valdtr)82 testsuite_validator_context_get(struct sieve_validator *valdtr)
83 {
84 	return (struct testsuite_validator_context *)
85 		sieve_validator_extension_get_context(valdtr, testsuite_ext);
86 }
87 
88 /*
89  * Generator context
90  */
91 
testsuite_generator_context_initialize(struct sieve_generator * gentr,const struct sieve_extension * this_ext)92 bool testsuite_generator_context_initialize(
93 	struct sieve_generator *gentr, const struct sieve_extension *this_ext)
94 {
95 	pool_t pool = sieve_generator_pool(gentr);
96 	struct sieve_binary_block *sblock = sieve_generator_get_block(gentr);
97 	struct testsuite_generator_context *ctx =
98 		p_new(pool, struct testsuite_generator_context, 1);
99 
100 	/* Setup exit jumplist */
101 	ctx->exit_jumps = sieve_jumplist_create(pool, sblock);
102 
103 	sieve_generator_extension_set_context(gentr, this_ext, ctx);
104 
105 	return TRUE;
106 }
107 
108 /*
109  * Interpreter context
110  */
111 
112 static void
testsuite_interpreter_free(const struct sieve_extension * ext ATTR_UNUSED,struct sieve_interpreter * interp ATTR_UNUSED,void * context)113 testsuite_interpreter_free(const struct sieve_extension *ext ATTR_UNUSED,
114 			   struct sieve_interpreter *interp ATTR_UNUSED,
115 			   void *context)
116 {
117 	struct testsuite_interpreter_context *ctx =
118 		(struct testsuite_interpreter_context *)context;
119 
120 	sieve_binary_unref(&ctx->compiled_script);
121 }
122 
123 const struct sieve_interpreter_extension testsuite_interpreter_ext = {
124 	.ext_def = &testsuite_extension,
125 	.free = testsuite_interpreter_free,
126 };
127 
testsuite_interpreter_context_initialize(struct sieve_interpreter * interp,const struct sieve_extension * this_ext)128 bool testsuite_interpreter_context_initialize(
129 	struct sieve_interpreter *interp, const struct sieve_extension *this_ext)
130 {
131 	pool_t pool = sieve_interpreter_pool(interp);
132 	struct testsuite_interpreter_context *ctx =
133 		p_new(pool, struct testsuite_interpreter_context, 1);
134 
135 	sieve_interpreter_extension_register(interp, this_ext,
136 					     &testsuite_interpreter_ext, ctx);
137 	return TRUE;
138 }
139 
140 struct testsuite_interpreter_context *
testsuite_interpreter_context_get(struct sieve_interpreter * interp,const struct sieve_extension * this_ext)141 testsuite_interpreter_context_get(struct sieve_interpreter *interp,
142 				  const struct sieve_extension *this_ext)
143 {
144 	struct testsuite_interpreter_context *ctx =
145 		sieve_interpreter_extension_get_context(interp, this_ext);
146 
147 	return ctx;
148 }
149 
150 /*
151  * Test context
152  */
153 
testsuite_test_context_init(void)154 static void testsuite_test_context_init(void)
155 {
156 	test_name = str_new(default_pool, 128);
157 	test_block_end = 0;
158 	test_index = 0;
159 	test_failures = 0;
160 }
161 
testsuite_test_start(const struct sieve_runtime_env * renv,string_t * name,sieve_size_t block_end)162 int testsuite_test_start(const struct sieve_runtime_env *renv,
163 			 string_t *name, sieve_size_t block_end)
164 {
165 	if (test_block_end != 0) {
166 		sieve_runtime_trace_error(renv, "already inside test block");
167 		return SIEVE_EXEC_BIN_CORRUPT;
168 	}
169 	str_truncate(test_name, 0);
170 	str_append_str(test_name, name);
171 
172 	test_block_end = block_end;
173 	test_index++;
174 
175 	return SIEVE_EXEC_OK;
176 }
177 
testsuite_test_fail(const struct sieve_runtime_env * renv,string_t * reason)178 int testsuite_test_fail(const struct sieve_runtime_env *renv,
179 			string_t *reason)
180 {
181 	return testsuite_test_fail_cstr(renv, str_c(reason));
182 }
183 
testsuite_test_failf(const struct sieve_runtime_env * renv,const char * fmt,...)184 int testsuite_test_failf(const struct sieve_runtime_env *renv,
185 			 const char *fmt, ...)
186 {
187 	va_list args;
188 	int ret;
189 
190 	va_start(args, fmt);
191 	ret = testsuite_test_fail_cstr(renv, t_strdup_vprintf(fmt, args));
192 	va_end(args);
193 
194 	return ret;
195 }
196 
testsuite_test_fail_cstr(const struct sieve_runtime_env * renv,const char * reason)197 int testsuite_test_fail_cstr(const struct sieve_runtime_env *renv,
198 			     const char *reason)
199 {
200 	sieve_size_t end = test_block_end;
201 
202 	if (str_len(test_name) == 0) {
203 		if (reason == NULL || *reason == '\0')
204 			printf("%2d: Test FAILED\n", test_index);
205 		else
206 			printf("%2d: Test FAILED: %s\n", test_index, reason);
207 	} else {
208 		if (reason == NULL || *reason == '\0') {
209 			printf("%2d: Test '%s' FAILED\n",
210 			       test_index, str_c(test_name));
211 		} else {
212 			printf("%2d: Test '%s' FAILED: %s\n",
213 			       test_index, str_c(test_name), reason);
214 		}
215 	}
216 
217 	str_truncate(test_name, 0);
218 	test_block_end = 0;
219 
220 	test_failures++;
221 
222 	return sieve_interpreter_program_jump_to(renv->interp, end, FALSE);
223 }
224 
testsuite_testcase_fail(const char * reason)225 void testsuite_testcase_fail(const char *reason)
226 {
227 	if (reason == NULL || *reason == '\0')
228 		printf("XX: Test CASE FAILED\n");
229 	else
230 		printf("XX: Test CASE FAILED: %s\n", reason);
231 
232 	test_failures++;
233 }
234 
testsuite_test_succeed(const struct sieve_runtime_env * renv,sieve_size_t * address,string_t * reason)235 int testsuite_test_succeed(const struct sieve_runtime_env *renv,
236 			   sieve_size_t *address, string_t *reason)
237 {
238 	sieve_size_t end = test_block_end;
239 	int ret;
240 
241 	if (str_len(test_name) == 0) {
242 		if (reason == NULL || str_len(reason) == 0)
243 			printf("%2d: Test SUCCEEDED\n", test_index);
244 		else {
245 			printf("%2d: Test SUCCEEDED: %s\n",
246 			       test_index, str_c(reason));
247 		}
248 	} else {
249 		if (reason == NULL || str_len(reason) == 0) {
250 			printf("%2d: Test '%s' SUCCEEDED\n",
251 			       test_index, str_c(test_name));
252 		} else {
253 			printf("%2d: Test '%s' SUCCEEDED: %s\n", test_index,
254 				str_c(test_name), str_c(reason));
255 		}
256 	}
257 
258 	str_truncate(test_name, 0);
259 	test_block_end = 0;
260 
261 	if (*address > end) {
262 		sieve_runtime_trace_error(
263 			renv, "invalid test block end offset");
264 		return SIEVE_EXEC_BIN_CORRUPT;
265 	} else if (*address < end) {
266 		ret = sieve_interpreter_program_jump_to(
267 			renv->interp, end, FALSE);
268 		if (ret <= 0)
269 			return ret;
270 	}
271 
272 	return SIEVE_EXEC_OK;
273 }
274 
testsuite_test_context_deinit(void)275 static void testsuite_test_context_deinit(void)
276 {
277 	str_free(&test_name);
278 }
279 
testsuite_testcase_result(bool expect_failure)280 bool testsuite_testcase_result(bool expect_failure)
281 {
282 	if (expect_failure) {
283 		if (test_failures < test_index) {
284 			printf("\nFAIL: Only %d of %d tests failed "
285 			       "(all expected to fail).\n\n",
286 			       test_failures, test_index);
287 			return FALSE;
288 		}
289 
290 		printf("\nPASS: %d tests failed (expected to fail).\n\n",
291 		       (test_index == 0 ? 1 : test_index));
292 		return TRUE;
293 	}
294 
295 	if (test_failures > 0) {
296 		printf("\nFAIL: %d of %d tests failed.\n\n",
297 		       test_failures, test_index);
298 		return FALSE;
299 	}
300 
301 	printf("\nPASS: %d tests succeeded.\n\n", test_index);
302 	return TRUE;
303 }
304 
305 /*
306  * Testsuite temporary directory
307  */
308 
309 static char *testsuite_tmp_dir;
310 
testsuite_tmp_dir_init(void)311 static void testsuite_tmp_dir_init(void)
312 {
313 	testsuite_tmp_dir = i_strdup_printf("/tmp/dsieve-testsuite.%s.%s",
314 					    dec2str(time(NULL)),
315 					    dec2str(getpid()));
316 
317 	if (mkdir(testsuite_tmp_dir, 0700) < 0) {
318 		i_fatal("failed to create temporary directory '%s': %m.",
319 			testsuite_tmp_dir);
320 	}
321 }
322 
testsuite_tmp_dir_deinit(void)323 static void testsuite_tmp_dir_deinit(void)
324 {
325 	const char *error;
326 
327 	if (unlink_directory(testsuite_tmp_dir,
328 			     UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0)
329 		i_warning("failed to remove temporary directory '%s': %s.",
330 			  testsuite_tmp_dir, error);
331 
332 	i_free(testsuite_tmp_dir);
333 }
334 
testsuite_tmp_dir_get(void)335 const char *testsuite_tmp_dir_get(void)
336 {
337 	return testsuite_tmp_dir;
338 }
339 
340 /*
341  * Main testsuite init/deinit
342  */
343 
testsuite_init(struct sieve_instance * svinst,const char * test_path,bool log_stdout)344 void testsuite_init(struct sieve_instance *svinst, const char *test_path,
345 		    bool log_stdout)
346 {
347 	testsuite_sieve_instance = svinst;
348 
349 	testsuite_test_context_init();
350 	testsuite_log_init(log_stdout);
351 	testsuite_tmp_dir_init();
352 
353 	testsuite_script_init();
354 	testsuite_binary_init();
355 	testsuite_smtp_init();
356 
357 	testsuite_ext =
358 		sieve_extension_register(svinst, &testsuite_extension, TRUE);
359 
360 	testsuite_test_path = i_strdup(test_path);
361 }
362 
testsuite_deinit(void)363 void testsuite_deinit(void)
364 {
365 	i_free(testsuite_test_path);
366 
367 	testsuite_smtp_deinit();
368 	testsuite_binary_deinit();
369 	testsuite_script_deinit();
370 
371 	testsuite_tmp_dir_deinit();
372 	testsuite_log_deinit();
373 	testsuite_test_context_deinit();
374 }
375