1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2  */
3 
4 #include "lib.h"
5 #include "str-sanitize.h"
6 
7 #include "sieve-common.h"
8 #include "sieve-script.h"
9 #include "sieve-storage.h"
10 #include "sieve-ast.h"
11 #include "sieve-code.h"
12 #include "sieve-extensions.h"
13 #include "sieve-commands.h"
14 #include "sieve-validator.h"
15 #include "sieve-binary.h"
16 #include "sieve-generator.h"
17 #include "sieve-interpreter.h"
18 #include "sieve-dump.h"
19 
20 #include "ext-include-common.h"
21 #include "ext-include-binary.h"
22 
23 /*
24  * Include command
25  *
26  * Syntax:
27  *   include [LOCATION] [":once"] [":optional"] <value: string>
28  *
29  * [LOCATION]:
30  *   ":personal" / ":global"
31  */
32 
33 static bool cmd_include_registered
34 	(struct sieve_validator *valdtr, const struct sieve_extension *ext,
35 		struct sieve_command_registration *cmd_reg);
36 static bool cmd_include_pre_validate
37 	(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd);
38 static bool cmd_include_validate
39 	(struct sieve_validator *valdtr, struct sieve_command *cmd);
40 static bool cmd_include_generate
41 	(const struct sieve_codegen_env *cgenv, struct sieve_command *ctx);
42 
43 const struct sieve_command_def cmd_include = {
44 	.identifier = "include",
45 	.type = SCT_COMMAND,
46 	.positional_args = 1,
47 	.subtests = 0,
48 	.block_allowed = FALSE,
49 	.block_required = FALSE,
50 	.registered = cmd_include_registered,
51 	.pre_validate = cmd_include_pre_validate,
52 	.validate = cmd_include_validate,
53 	.generate = cmd_include_generate
54 };
55 
56 /*
57  * Include operation
58  */
59 
60 static bool opc_include_dump
61 	(const struct sieve_dumptime_env *denv, sieve_size_t *address);
62 static int opc_include_execute
63 	(const struct sieve_runtime_env *renv, sieve_size_t *address);
64 
65 const struct sieve_operation_def include_operation = {
66 	.mnemonic = "include",
67 	.ext_def = &include_extension,
68 	.code = EXT_INCLUDE_OPERATION_INCLUDE,
69 	.dump = opc_include_dump,
70 	.execute = opc_include_execute
71 };
72 
73 /*
74  * Context structures
75  */
76 
77 struct cmd_include_context_data {
78 	enum ext_include_script_location location;
79 
80 	struct sieve_script *script;
81 	enum ext_include_flags flags;
82 
83 	bool location_assigned:1;
84 };
85 
86 /*
87  * Tagged arguments
88  */
89 
90 static bool cmd_include_validate_location_tag
91 	(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
92 		struct sieve_command *cmd);
93 
94 static const struct sieve_argument_def include_personal_tag = {
95 	.identifier = "personal",
96 	.validate = cmd_include_validate_location_tag
97 };
98 
99 static const struct sieve_argument_def include_global_tag = {
100 	.identifier = "global",
101 	.validate = cmd_include_validate_location_tag
102 };
103 
104 static bool cmd_include_validate_boolean_tag
105 	(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
106 		struct sieve_command *cmd);
107 
108 static const struct sieve_argument_def include_once_tag = {
109 	.identifier = "once",
110 	.validate = cmd_include_validate_boolean_tag
111 };
112 
113 static const struct sieve_argument_def include_optional_tag = {
114 	.identifier = "optional",
115 	.validate = cmd_include_validate_boolean_tag
116 };
117 
118 /*
119  * Tag validation
120  */
121 
cmd_include_validate_location_tag(struct sieve_validator * valdtr,struct sieve_ast_argument ** arg,struct sieve_command * cmd)122 static bool cmd_include_validate_location_tag
123 (struct sieve_validator *valdtr,	struct sieve_ast_argument **arg,
124 	struct sieve_command *cmd)
125 {
126 	struct cmd_include_context_data *ctx_data =
127 		(struct cmd_include_context_data *) cmd->data;
128 
129 	if ( ctx_data->location_assigned) {
130 		sieve_argument_validate_error(valdtr, *arg,
131 			"include: cannot use location tags ':personal' and ':global' "
132 			"multiple times");
133 		return FALSE;
134 	}
135 
136 	if ( sieve_argument_is(*arg, include_personal_tag) )
137 		ctx_data->location = EXT_INCLUDE_LOCATION_PERSONAL;
138 	else if ( sieve_argument_is(*arg, include_global_tag) )
139 		ctx_data->location = EXT_INCLUDE_LOCATION_GLOBAL;
140 	else
141 		return FALSE;
142 
143 	ctx_data->location_assigned = TRUE;
144 
145 	/* Delete this tag (for now) */
146 	*arg = sieve_ast_arguments_detach(*arg, 1);
147 
148 	return TRUE;
149 }
150 
cmd_include_validate_boolean_tag(struct sieve_validator * valdtr ATTR_UNUSED,struct sieve_ast_argument ** arg,struct sieve_command * cmd)151 static bool cmd_include_validate_boolean_tag
152 (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg,
153 	struct sieve_command *cmd)
154 {
155 	struct cmd_include_context_data *ctx_data =
156 		(struct cmd_include_context_data *) cmd->data;
157 
158 	if ( sieve_argument_is(*arg, include_once_tag) )
159 		ctx_data->flags |= EXT_INCLUDE_FLAG_ONCE;
160 	else
161 		ctx_data->flags |= EXT_INCLUDE_FLAG_OPTIONAL;
162 
163 	/* Delete this tag (for now) */
164 	*arg = sieve_ast_arguments_detach(*arg, 1);
165 
166 	return TRUE;
167 }
168 
169 /*
170  * Command registration
171  */
172 
cmd_include_registered(struct sieve_validator * valdtr,const struct sieve_extension * ext,struct sieve_command_registration * cmd_reg)173 static bool cmd_include_registered
174 (struct sieve_validator *valdtr, const struct sieve_extension *ext,
175 	struct sieve_command_registration *cmd_reg)
176 {
177 	sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_personal_tag, 0);
178 	sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_global_tag, 0);
179 	sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_once_tag, 0);
180 	sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_optional_tag, 0);
181 
182 	return TRUE;
183 }
184 
185 /*
186  * Command validation
187  */
188 
cmd_include_pre_validate(struct sieve_validator * valdtr ATTR_UNUSED,struct sieve_command * cmd)189 static bool cmd_include_pre_validate
190 (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd)
191 {
192 	struct cmd_include_context_data *ctx_data;
193 
194 	/* Assign context */
195 	ctx_data = p_new(sieve_command_pool(cmd), struct cmd_include_context_data, 1);
196 	ctx_data->location = EXT_INCLUDE_LOCATION_PERSONAL;
197 	cmd->data = ctx_data;
198 
199 	return TRUE;
200 }
201 
cmd_include_validate(struct sieve_validator * valdtr,struct sieve_command * cmd)202 static bool cmd_include_validate
203 (struct sieve_validator *valdtr, struct sieve_command *cmd)
204 {
205 	const struct sieve_extension *this_ext = cmd->ext;
206 	struct sieve_ast_argument *arg = cmd->first_positional;
207 	struct cmd_include_context_data *ctx_data =
208 		(struct cmd_include_context_data *) cmd->data;
209 	struct sieve_storage *storage;
210 	struct sieve_script *script;
211 	const char *script_name;
212 	enum sieve_error error = SIEVE_ERROR_NONE;
213 	int ret;
214 
215 	/* Check argument */
216 	if ( !sieve_validate_positional_argument
217 		(valdtr, cmd, arg, "value", 1, SAAT_STRING) ) {
218 		return FALSE;
219 	}
220 
221 	if ( !sieve_validator_argument_activate(valdtr, cmd, arg, FALSE) )
222 		return FALSE;
223 
224 	/*
225 	 * Variables are not allowed.
226 	 */
227 	if ( !sieve_argument_is_string_literal(arg) ) {
228 		sieve_argument_validate_error(valdtr, arg,
229 			"the include command requires a constant string for its value argument");
230 		return FALSE;
231 	}
232 
233 	/* Find the script */
234 
235 	script_name = sieve_ast_argument_strc(arg);
236 
237 	if ( !sieve_script_name_is_valid(script_name) ) {
238  		sieve_argument_validate_error(valdtr, arg,
239 			"include: invalid script name '%s'",
240 			str_sanitize(script_name, 80));
241 		return FALSE;
242 	}
243 
244 	storage = ext_include_get_script_storage
245 		(this_ext, ctx_data->location, script_name,	&error);
246 	if ( storage == NULL ) {
247 		// FIXME: handle ':optional' in this case
248 		if (error == SIEVE_ERROR_NOT_FOUND) {
249 			sieve_argument_validate_error(valdtr, arg,
250 				"include: %s location for included script `%s' is unavailable "
251 				"(contact system administrator for more information)",
252 				ext_include_script_location_name(ctx_data->location),
253 				str_sanitize(script_name, 80));
254 		} else {
255 			sieve_argument_validate_error(valdtr, arg,
256 				"include: failed to access %s location for included script `%s' "
257 				"(contact system administrator for more information)",
258 				ext_include_script_location_name(ctx_data->location),
259 				str_sanitize(script_name, 80));
260 		}
261 		return FALSE;
262 	}
263 
264 	/* Create script object */
265 	script = sieve_storage_get_script
266 		(storage, script_name, &error);
267 	if ( script == NULL )
268 		return FALSE;
269 
270 	ret = sieve_script_open(script, &error);
271 	if ( ret < 0 ) {
272 		if ( error != SIEVE_ERROR_NOT_FOUND ) {
273 			sieve_argument_validate_error(valdtr, arg,
274 				"failed to access included %s script '%s': %s",
275 				ext_include_script_location_name(ctx_data->location),
276 				str_sanitize(script_name, 80),
277 				sieve_script_get_last_error_lcase(script));
278 			sieve_script_unref(&script);
279 			return FALSE;
280 
281 		/* Not found */
282 		} else {
283 			enum sieve_compile_flags cpflags =
284 				sieve_validator_compile_flags(valdtr);
285 
286 			if ( (ctx_data->flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ) {
287 				/* :optional */
288 
289 			} else if ( (cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 ) {
290 				/* Script is being uploaded */
291 				sieve_argument_validate_warning(valdtr, arg,
292 					"included %s script '%s' does not exist (ignored during upload)",
293 					ext_include_script_location_name(ctx_data->location),
294 					str_sanitize(script_name, 80));
295 				ctx_data->flags |= EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD;
296 
297 			} else {
298 				/* Should have existed */
299 				sieve_argument_validate_error(valdtr, arg,
300 					"included %s script '%s' does not exist",
301 					ext_include_script_location_name(ctx_data->location),
302 					str_sanitize(script_name, 80));
303 				sieve_script_unref(&script);
304 				return FALSE;
305 			}
306 		}
307 	}
308 
309 	ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script);
310 	ctx_data->script = script;
311 
312 	(void)sieve_ast_arguments_detach(arg, 1);
313 	return TRUE;
314 }
315 
316 /*
317  * Code Generation
318  */
319 
cmd_include_generate(const struct sieve_codegen_env * cgenv,struct sieve_command * cmd)320 static bool cmd_include_generate
321 (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
322 {
323 	struct cmd_include_context_data *ctx_data =
324 		(struct cmd_include_context_data *) cmd->data;
325 	const struct ext_include_script_info *included;
326 	int ret;
327 
328 	/* Compile (if necessary) and include the script into the binary.
329 	 * This yields the id of the binary block containing the compiled byte code.
330 	 */
331 	if ( (ret=ext_include_generate_include
332 		(cgenv, cmd, ctx_data->location, ctx_data->flags, ctx_data->script,
333 			&included)) < 0 )
334  		return FALSE;
335 
336 	if ( ret > 0 ) {
337 	 	(void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation);
338 		(void)sieve_binary_emit_unsigned(cgenv->sblock, included->id);
339 		(void)sieve_binary_emit_byte(cgenv->sblock, ctx_data->flags);
340 	}
341 
342 	return TRUE;
343 }
344 
345 /*
346  * Code dump
347  */
348 
opc_include_dump(const struct sieve_dumptime_env * denv,sieve_size_t * address)349 static bool opc_include_dump
350 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
351 {
352 	const struct ext_include_script_info *included;
353 	struct ext_include_binary_context *binctx;
354 	unsigned int include_id, flags;
355 
356 	sieve_code_dumpf(denv, "INCLUDE:");
357 
358 	sieve_code_mark(denv);
359 	if ( !sieve_binary_read_unsigned(denv->sblock, address, &include_id) )
360 		return FALSE;
361 
362 	if ( !sieve_binary_read_byte(denv->sblock, address, &flags) )
363 		return FALSE;
364 
365 	binctx = ext_include_binary_get_context(denv->oprtn->ext, denv->sbin);
366 	included = ext_include_binary_script_get_included(binctx, include_id);
367 	if ( included == NULL )
368 		return FALSE;
369 
370 	sieve_code_descend(denv);
371 	sieve_code_dumpf(denv, "script: `%s' from %s %s%s[ID: %d, BLOCK: %d]",
372 		sieve_script_name(included->script), sieve_script_location(included->script),
373 		((flags & EXT_INCLUDE_FLAG_ONCE) != 0 ? "(once) " : ""),
374 		((flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ? "(optional) " : ""),
375 		include_id, sieve_binary_block_get_id(included->block));
376 
377 	return TRUE;
378 }
379 
380 /*
381  * Execution
382  */
383 
opc_include_execute(const struct sieve_runtime_env * renv,sieve_size_t * address)384 static int opc_include_execute
385 (const struct sieve_runtime_env *renv, sieve_size_t *address)
386 {
387 	unsigned int include_id, flags;
388 
389 	if ( !sieve_binary_read_unsigned(renv->sblock, address, &include_id) ) {
390 		sieve_runtime_trace_error(renv, "invalid include-id operand");
391 		return SIEVE_EXEC_BIN_CORRUPT;
392 	}
393 
394 	if ( !sieve_binary_read_unsigned(renv->sblock, address, &flags) ) {
395 		sieve_runtime_trace_error(renv, "invalid flags operand");
396 		return SIEVE_EXEC_BIN_CORRUPT;
397 	}
398 
399 	return ext_include_execute_include
400 		(renv, include_id, (enum ext_include_flags)flags);
401 }
402 
403 
404 
405 
406 
407