1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2  */
3 
4 #include "lib.h"
5 #include "str.h"
6 
7 #include "sieve-common.h"
8 #include "sieve-error.h"
9 #include "sieve-script.h"
10 #include "sieve-storage.h"
11 #include "sieve-binary.h"
12 #include "sieve-generator.h"
13 #include "sieve-interpreter.h"
14 #include "sieve-dump.h"
15 
16 #include "sieve-ext-variables.h"
17 
18 #include "ext-include-common.h"
19 #include "ext-include-limits.h"
20 #include "ext-include-variables.h"
21 #include "ext-include-binary.h"
22 
23 /*
24  * Forward declarations
25  */
26 
27 static bool ext_include_binary_pre_save
28 	(const struct sieve_extension *ext, struct sieve_binary *sbin,
29 		void *context, enum sieve_error *error_r);
30 static bool ext_include_binary_open
31 	(const struct sieve_extension *ext, struct sieve_binary *sbin,
32 		void *context);
33 static bool ext_include_binary_up_to_date
34 	(const struct sieve_extension *ext, struct sieve_binary *sbin,
35 		void *context, enum sieve_compile_flags cpflags);
36 static void ext_include_binary_free
37 	(const struct sieve_extension *ext, struct sieve_binary *sbin,
38 		void *context);
39 
40 /*
41  * Binary include extension
42  */
43 
44 const struct sieve_binary_extension include_binary_ext = {
45 	.extension = &include_extension,
46 	.binary_pre_save = ext_include_binary_pre_save,
47 	.binary_open = ext_include_binary_open,
48 	.binary_free = ext_include_binary_free,
49 	.binary_up_to_date = ext_include_binary_up_to_date
50 };
51 
52 /*
53  * Binary context management
54  */
55 
56 struct ext_include_binary_context {
57 	struct sieve_binary *binary;
58 	struct sieve_binary_block *dependency_block;
59 
60 	HASH_TABLE(struct sieve_script *,
61 		   struct ext_include_script_info *) included_scripts;
62 	ARRAY(struct ext_include_script_info *) include_index;
63 
64 	struct sieve_variable_scope_binary *global_vars;
65 
66 	bool outdated:1;
67 };
68 
ext_include_binary_create_context(const struct sieve_extension * this_ext,struct sieve_binary * sbin)69 static struct ext_include_binary_context *ext_include_binary_create_context
70 (const struct sieve_extension *this_ext, struct sieve_binary *sbin)
71 {
72 	pool_t pool = sieve_binary_pool(sbin);
73 
74 	struct ext_include_binary_context *ctx =
75 		p_new(pool, struct ext_include_binary_context, 1);
76 
77 	ctx->binary = sbin;
78 	hash_table_create(&ctx->included_scripts, pool, 0,
79 		sieve_script_hash, sieve_script_cmp);
80 	p_array_init(&ctx->include_index, pool, 128);
81 
82 	sieve_binary_extension_set(sbin, this_ext, &include_binary_ext, ctx);
83 
84 	return ctx;
85 }
86 
ext_include_binary_get_context(const struct sieve_extension * this_ext,struct sieve_binary * sbin)87 struct ext_include_binary_context *ext_include_binary_get_context
88 (const struct sieve_extension *this_ext, struct sieve_binary *sbin)
89 {
90 	struct ext_include_binary_context *ctx = (struct ext_include_binary_context *)
91 		sieve_binary_extension_get_context(sbin, this_ext);
92 
93 	if ( ctx == NULL )
94 		ctx = ext_include_binary_create_context(this_ext, sbin);
95 
96 	return ctx;
97 }
98 
ext_include_binary_init(const struct sieve_extension * this_ext,struct sieve_binary * sbin,struct sieve_ast * ast)99 struct ext_include_binary_context *ext_include_binary_init
100 (const struct sieve_extension *this_ext, struct sieve_binary *sbin,
101 	struct sieve_ast *ast)
102 {
103 	struct ext_include_ast_context *ast_ctx =
104 		ext_include_get_ast_context(this_ext, ast);
105 	struct ext_include_binary_context *ctx;
106 
107 	/* Get/create our context from the binary we are working on */
108 	ctx = ext_include_binary_get_context(this_ext, sbin);
109 
110 	/* Create dependency block */
111 	if ( ctx->dependency_block == 0 )
112 		ctx->dependency_block =
113 			sieve_binary_extension_create_block(sbin, this_ext);
114 
115 	if ( ctx->global_vars == NULL ) {
116 		ctx->global_vars =
117 			sieve_variable_scope_binary_create(ast_ctx->global_vars);
118 		sieve_variable_scope_binary_ref(ctx->global_vars);
119 	}
120 
121 	return ctx;
122 }
123 
124 /*
125  * Script inclusion
126  */
127 
ext_include_binary_script_include(struct ext_include_binary_context * binctx,enum ext_include_script_location location,enum ext_include_flags flags,struct sieve_script * script,struct sieve_binary_block * inc_block)128 struct ext_include_script_info *ext_include_binary_script_include
129 (struct ext_include_binary_context *binctx,
130 	enum ext_include_script_location location, enum ext_include_flags flags,
131 	struct sieve_script *script,	struct sieve_binary_block *inc_block)
132 {
133 	pool_t pool = sieve_binary_pool(binctx->binary);
134 	struct ext_include_script_info *incscript;
135 
136 	incscript = p_new(pool, struct ext_include_script_info, 1);
137 	incscript->id = array_count(&binctx->include_index)+1;
138 	incscript->location = location;
139 	incscript->flags = flags;
140 	incscript->script = script;
141 	incscript->block = inc_block;
142 
143 	/* Unreferenced on binary_free */
144 	sieve_script_ref(script);
145 
146 	hash_table_insert(binctx->included_scripts, script, incscript);
147 	array_append(&binctx->include_index, &incscript, 1);
148 
149 	return incscript;
150 }
151 
ext_include_binary_script_get_include_info(struct ext_include_binary_context * binctx,struct sieve_script * script)152 struct ext_include_script_info *ext_include_binary_script_get_include_info
153 (struct ext_include_binary_context *binctx, struct sieve_script *script)
154 {
155 	struct ext_include_script_info *incscript =
156 		hash_table_lookup(binctx->included_scripts, script);
157 
158 	return incscript;
159 }
160 
ext_include_binary_script_get_included(struct ext_include_binary_context * binctx,unsigned int include_id)161 const struct ext_include_script_info *ext_include_binary_script_get_included
162 (struct ext_include_binary_context *binctx, unsigned int include_id)
163 {
164 	if ( include_id > 0 &&
165 		(include_id - 1) < array_count(&binctx->include_index) ) {
166 		struct ext_include_script_info *const *sinfo =
167 			array_idx(&binctx->include_index, include_id - 1);
168 
169 		return *sinfo;
170 	}
171 
172 	return NULL;
173 }
174 
ext_include_binary_script_get(struct ext_include_binary_context * binctx,struct sieve_script * script)175 const struct ext_include_script_info *ext_include_binary_script_get
176 (struct ext_include_binary_context *binctx, struct sieve_script *script)
177 {
178 	return hash_table_lookup(binctx->included_scripts, script);
179 }
180 
ext_include_binary_script_get_count(struct ext_include_binary_context * binctx)181 unsigned int ext_include_binary_script_get_count
182 (struct ext_include_binary_context *binctx)
183 {
184 	return array_count(&binctx->include_index);
185 }
186 
187 /*
188  * Variables
189  */
190 
ext_include_binary_get_global_scope(const struct sieve_extension * this_ext,struct sieve_binary * sbin)191 struct sieve_variable_scope_binary *ext_include_binary_get_global_scope
192 (const struct sieve_extension *this_ext, struct sieve_binary *sbin)
193 {
194 	struct ext_include_binary_context *binctx =
195 		ext_include_binary_get_context(this_ext, sbin);
196 
197 	return binctx->global_vars;
198 }
199 
200 /*
201  * Binary extension
202  */
203 
ext_include_binary_pre_save(const struct sieve_extension * ext ATTR_UNUSED,struct sieve_binary * sbin ATTR_UNUSED,void * context,enum sieve_error * error_r)204 static bool ext_include_binary_pre_save
205 (const struct sieve_extension *ext ATTR_UNUSED,
206 	struct sieve_binary *sbin ATTR_UNUSED, void *context,
207 	enum sieve_error *error_r)
208 {
209 	struct ext_include_binary_context *binctx =
210 		(struct ext_include_binary_context *) context;
211 	struct ext_include_script_info *const *scripts;
212 	struct sieve_binary_block *sblock = binctx->dependency_block;
213 	unsigned int script_count, i;
214 	bool result = TRUE;
215 
216 	sieve_binary_block_clear(sblock);
217 
218 	scripts = array_get(&binctx->include_index, &script_count);
219 
220 	sieve_binary_emit_unsigned(sblock, script_count);
221 
222 	for ( i = 0; i < script_count; i++ ) {
223 		struct ext_include_script_info *incscript = scripts[i];
224 
225 		if ( incscript->block != NULL ) {
226 			sieve_binary_emit_unsigned
227 				(sblock, sieve_binary_block_get_id(incscript->block));
228 		} else {
229 			sieve_binary_emit_unsigned(sblock, 0);
230 		}
231 		sieve_binary_emit_byte(sblock, incscript->location);
232 		sieve_binary_emit_cstring(sblock, sieve_script_name(incscript->script));
233 		sieve_binary_emit_byte(sblock, incscript->flags);
234 		sieve_script_binary_write_metadata(incscript->script, sblock);
235 	}
236 
237 	result = ext_include_variables_save(sblock, binctx->global_vars, error_r);
238 
239 	return result;
240 }
241 
ext_include_binary_open(const struct sieve_extension * ext,struct sieve_binary * sbin,void * context)242 static bool ext_include_binary_open
243 (const struct sieve_extension *ext, struct sieve_binary *sbin, void *context)
244 {
245 	struct sieve_instance *svinst = ext->svinst;
246 	struct ext_include_context *ext_ctx =
247 		(struct ext_include_context *)ext->context;
248 	struct ext_include_binary_context *binctx =
249 		(struct ext_include_binary_context *) context;
250 	struct sieve_binary_block *sblock;
251 	unsigned int depcount, i, block_id;
252 	sieve_size_t offset;
253 
254 	sblock = sieve_binary_extension_get_block(sbin, ext);
255 	block_id = sieve_binary_block_get_id(sblock);
256 
257 	offset = 0;
258 
259 	if ( !sieve_binary_read_unsigned(sblock, &offset, &depcount) ) {
260 		e_error(svinst->event,
261 			"include: failed to read include count "
262 			"for dependency block %d of binary %s", block_id,
263 			sieve_binary_path(sbin));
264 		return FALSE;
265 	}
266 
267 	/* Check include limit */
268 	if ( depcount > ext_ctx->max_includes ) {
269 		e_error(svinst->event,
270 			"include: binary %s includes too many scripts (%u > %u)",
271 			sieve_binary_path(sbin), depcount, ext_ctx->max_includes);
272 		return FALSE;
273 	}
274 
275 	/* Read dependencies */
276 	for ( i = 0; i < depcount; i++ ) {
277 		unsigned int inc_block_id;
278 		struct sieve_binary_block *inc_block = NULL;
279 		unsigned int location, flags;
280 		string_t *script_name;
281 		struct sieve_storage *storage;
282 		struct sieve_script *script;
283 		enum sieve_error error;
284 		int ret;
285 
286 		if (
287 			!sieve_binary_read_unsigned(sblock, &offset, &inc_block_id) ||
288 			!sieve_binary_read_byte(sblock, &offset, &location) ||
289 			!sieve_binary_read_string(sblock, &offset, &script_name) ||
290 			!sieve_binary_read_byte(sblock, &offset, &flags) ) {
291 			/* Binary is corrupt, recompile */
292 			e_error(svinst->event,
293 				"include: failed to read included script "
294 				"from dependency block %d of binary %s",
295 				block_id, sieve_binary_path(sbin));
296 			return FALSE;
297 		}
298 
299 		if ( inc_block_id != 0 &&
300 			(inc_block=sieve_binary_block_get(sbin, inc_block_id)) == NULL ) {
301 			e_error(svinst->event,
302 				"include: failed to find block %d for included script "
303 				"from dependency block %d of binary %s",
304 				inc_block_id, block_id,
305 				sieve_binary_path(sbin));
306 			return FALSE;
307 		}
308 
309 		if ( location >= EXT_INCLUDE_LOCATION_INVALID ) {
310 			/* Binary is corrupt, recompile */
311 			e_error(svinst->event,
312 				"include: dependency block %d of binary %s "
313 				"uses invalid script location (id %d)",
314 				block_id, sieve_binary_path(sbin), location);
315 			return FALSE;
316 		}
317 
318 		/* Can we find the script dependency ? */
319 		storage = ext_include_get_script_storage
320 			(ext, location, str_c(script_name), &error);
321 		if ( storage == NULL ) {
322 			/* No, recompile */
323 			// FIXME: handle ':optional' in this case
324 			return FALSE;
325 		}
326 
327 		/* Can we open the script dependency ? */
328 		script = sieve_storage_get_script
329 			(storage, str_c(script_name), &error);
330 		if ( script == NULL ) {
331 			/* No, recompile */
332 			return FALSE;
333 		}
334 		if ( sieve_script_open(script, &error) < 0 ) {
335 			if ( error != SIEVE_ERROR_NOT_FOUND ) {
336 				/* No, recompile */
337 				return FALSE;
338 			}
339 
340 			if ( (flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0 ) {
341 				/* Not supposed to be missing, recompile */
342 				if ( svinst->debug ) {
343 					e_debug(svinst->event, "include: "
344 						"script '%s' included in binary %s is missing, "
345 						"so recompile",
346 						str_c(script_name),
347 						sieve_binary_path(sbin));
348 				}
349 				return FALSE;
350 			}
351 
352 		} else if (inc_block == NULL) {
353 			/* Script exists, but it is missing from the binary, recompile no matter
354 			 * what.
355 			 */
356 			if ( svinst->debug ) {
357 				e_debug(svinst->event, "include: "
358 					"script '%s' is missing in binary %s, but is now available, "
359 					"so recompile", str_c(script_name), sieve_binary_path(sbin));
360 			}
361 			sieve_script_unref(&script);
362 			return FALSE;
363 		}
364 
365 		/* Can we read script metadata ? */
366 		if ( (ret=sieve_script_binary_read_metadata
367 			(script, sblock, &offset))	< 0 ) {
368 			/* Binary is corrupt, recompile */
369 			e_error(svinst->event, "include: "
370 				"dependency block %d of binary %s "
371 				"contains invalid script metadata for script %s",
372 				block_id, sieve_binary_path(sbin),
373 				sieve_script_location(script));
374 			sieve_script_unref(&script);
375 			return FALSE;
376 		}
377 
378 		if ( ret == 0 )
379 			binctx->outdated = TRUE;
380 
381 		(void)ext_include_binary_script_include
382 			(binctx, location, flags, script, inc_block);
383 
384 		sieve_script_unref(&script);
385 	}
386 
387 	if ( !ext_include_variables_load
388 		(ext, sblock, &offset, &binctx->global_vars) )
389 		return FALSE;
390 
391 	return TRUE;
392 }
393 
ext_include_binary_up_to_date(const struct sieve_extension * ext ATTR_UNUSED,struct sieve_binary * sbin ATTR_UNUSED,void * context,enum sieve_compile_flags cpflags ATTR_UNUSED)394 static bool ext_include_binary_up_to_date
395 (const struct sieve_extension *ext ATTR_UNUSED,
396 	struct sieve_binary *sbin ATTR_UNUSED, void *context,
397 	enum sieve_compile_flags cpflags ATTR_UNUSED)
398 {
399 	struct ext_include_binary_context *binctx =
400 		(struct ext_include_binary_context *) context;
401 
402 	return !binctx->outdated;
403 }
404 
ext_include_binary_free(const struct sieve_extension * ext ATTR_UNUSED,struct sieve_binary * sbin ATTR_UNUSED,void * context)405 static void ext_include_binary_free
406 (const struct sieve_extension *ext ATTR_UNUSED,
407 	struct sieve_binary *sbin ATTR_UNUSED, void *context)
408 {
409 	struct ext_include_binary_context *binctx =
410 		(struct ext_include_binary_context *) context;
411 	struct hash_iterate_context *hctx;
412 	struct sieve_script *script;
413 	struct ext_include_script_info *incscript;
414 
415 	/* Release references to all included script objects */
416 	hctx = hash_table_iterate_init(binctx->included_scripts);
417 	while ( hash_table_iterate
418 		(hctx, binctx->included_scripts, &script, &incscript) )
419 		sieve_script_unref(&incscript->script);
420 	hash_table_iterate_deinit(&hctx);
421 
422 	hash_table_destroy(&binctx->included_scripts);
423 
424 	if ( binctx->global_vars != NULL )
425 		sieve_variable_scope_binary_unref(&binctx->global_vars);
426 }
427 
428 /*
429  * Dumping the binary
430  */
431 
ext_include_binary_dump(const struct sieve_extension * ext,struct sieve_dumptime_env * denv)432 bool ext_include_binary_dump
433 (const struct sieve_extension *ext, struct sieve_dumptime_env *denv)
434 {
435 	struct sieve_binary *sbin = denv->sbin;
436 	struct ext_include_binary_context *binctx =
437 		ext_include_binary_get_context(ext, sbin);
438 	struct hash_iterate_context *hctx;
439 	struct sieve_script *script;
440 	struct ext_include_script_info *incscript;
441 
442 	if ( !ext_include_variables_dump(denv, binctx->global_vars) )
443 		return FALSE;
444 
445 	hctx = hash_table_iterate_init(binctx->included_scripts);
446 	while ( hash_table_iterate
447 		(hctx, binctx->included_scripts, &script, &incscript) ) {
448 
449 		if ( incscript->block == NULL ) {
450 			sieve_binary_dump_sectionf(denv, "Included %s script '%s' (MISSING)",
451 				ext_include_script_location_name(incscript->location),
452 				sieve_script_name(incscript->script));
453 
454 		} else {
455 			unsigned int block_id = sieve_binary_block_get_id(incscript->block);
456 
457 			sieve_binary_dump_sectionf(denv, "Included %s script '%s' (block: %d)",
458 				ext_include_script_location_name(incscript->location),
459 				sieve_script_name(incscript->script), block_id);
460 
461 			denv->sblock = incscript->block;
462 			denv->cdumper = sieve_code_dumper_create(denv);
463 
464 			if ( denv->cdumper == NULL )
465 				return FALSE;
466 
467 			sieve_code_dumper_run(denv->cdumper);
468 			sieve_code_dumper_free(&(denv->cdumper));
469 		}
470 	}
471 	hash_table_iterate_deinit(&hctx);
472 
473 	return TRUE;
474 }
475 
ext_include_code_dump(const struct sieve_extension * ext,const struct sieve_dumptime_env * denv,sieve_size_t * address ATTR_UNUSED)476 bool ext_include_code_dump
477 (const struct sieve_extension *ext, const struct sieve_dumptime_env *denv,
478 	sieve_size_t *address ATTR_UNUSED)
479 {
480 	struct sieve_binary *sbin = denv->sbin;
481 	struct ext_include_binary_context *binctx =
482 		ext_include_binary_get_context(ext, sbin);
483 	struct ext_include_context *ectx = ext_include_get_context(ext);
484 
485 	sieve_ext_variables_dump_set_scope
486 		(ectx->var_ext, denv, ext,
487 			sieve_variable_scope_binary_get(binctx->global_vars));
488 
489 	return TRUE;
490 }
491 
492 
493