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