1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2  */
3 
4 #include "lib.h"
5 
6 #include "sieve-common.h"
7 #include "sieve-code.h"
8 #include "sieve-commands.h"
9 #include "sieve-validator.h"
10 #include "sieve-generator.h"
11 #include "sieve-binary.h"
12 #include "sieve-interpreter.h"
13 #include "sieve-dump.h"
14 
15 #include "sieve-ext-variables.h"
16 
17 #include "ext-include-common.h"
18 #include "ext-include-binary.h"
19 #include "ext-include-variables.h"
20 
21 /*
22  * Commands
23  */
24 
25 static bool cmd_global_validate
26   (struct sieve_validator *valdtr, struct sieve_command *cmd);
27 static bool cmd_global_generate
28 (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd);
29 
30 const struct sieve_command_def cmd_global = {
31   .identifier = "global",
32   .type = SCT_COMMAND,
33 	.positional_args = 1,
34 	.subtests = 0,
35 	.block_allowed = FALSE,
36 	.block_required = FALSE,
37   .validate = cmd_global_validate,
38   .generate = cmd_global_generate,
39 };
40 
41 /* DEPRICATED:
42  */
43 
44 /* Import command
45  *
46  * Syntax
47  *   import
48  */
49 const struct sieve_command_def cmd_import = {
50 	.identifier = "import",
51 	.type = SCT_COMMAND,
52 	.positional_args = 1,
53 	.subtests = 0,
54 	.block_allowed = FALSE,
55 	.block_required = FALSE,
56 	.validate = cmd_global_validate,
57 	.generate = cmd_global_generate,
58 };
59 
60 /* Export command
61  *
62  * Syntax
63  *   export
64  */
65 const struct sieve_command_def cmd_export = {
66 	.identifier = "export",
67 	.type = SCT_COMMAND,
68 	.positional_args = 1,
69 	.subtests = 0,
70 	.block_allowed = FALSE,
71 	.block_required = FALSE,
72 	.validate = cmd_global_validate,
73 	.generate = cmd_global_generate,
74 };
75 
76 /*
77  * Operations
78  */
79 
80 static bool opc_global_dump
81 	(const struct sieve_dumptime_env *denv, sieve_size_t *address);
82 static int opc_global_execute
83 	(const struct sieve_runtime_env *renv, sieve_size_t *address);
84 
85 /* Global operation */
86 
87 const struct sieve_operation_def global_operation = {
88 	.mnemonic = "GLOBAL",
89 	.ext_def = &include_extension,
90 	.code = EXT_INCLUDE_OPERATION_GLOBAL,
91 	.dump = opc_global_dump,
92 	.execute = opc_global_execute
93 };
94 
95 /*
96  * Validation
97  */
98 
_create_variable_argument(struct sieve_command * cmd,struct sieve_variable * var)99 static inline struct sieve_argument *_create_variable_argument
100 (struct sieve_command *cmd, struct sieve_variable *var)
101 {
102 	struct sieve_argument *argument = sieve_argument_create
103 		(cmd->ast_node->ast, NULL, cmd->ext, 0);
104 
105 	argument->data = (void *) var;
106 
107 	return argument;
108 }
109 
cmd_global_validate(struct sieve_validator * valdtr,struct sieve_command * cmd)110 static bool cmd_global_validate
111 (struct sieve_validator *valdtr, struct sieve_command *cmd)
112 {
113 	const struct sieve_extension *this_ext = cmd->ext;
114 	struct sieve_ast_argument *arg = cmd->first_positional;
115 	struct sieve_command *prev = sieve_command_prev(cmd);
116 
117 	/* DEPRECATED: Check valid command placement */
118 	if ( !sieve_command_is(cmd, cmd_global) ) {
119 		if ( !sieve_command_is_toplevel(cmd) ||
120 			( !sieve_command_is_first(cmd) && prev != NULL &&
121 				!sieve_command_is(prev, cmd_require) &&
122 				!sieve_command_is(prev, cmd_import) &&
123 				!sieve_command_is(prev, cmd_export) ) ) {
124 			sieve_command_validate_error(valdtr, cmd,
125 				"the DEPRECATED %s command can only be placed at top level "
126 				"at the beginning of the file after any require or "
127 				"import/export commands",
128 				sieve_command_identifier(cmd));
129 			return FALSE;
130 		}
131 	}
132 
133 	/* Check for use of variables extension */
134 	if ( !ext_include_validator_have_variables(this_ext, valdtr) ) {
135 		sieve_command_validate_error(valdtr, cmd,
136 			"%s command requires that variables extension is active",
137 			sieve_command_identifier(cmd));
138 		return FALSE;
139 	}
140 
141 	/* Register global variable */
142 	if ( sieve_ast_argument_type(arg) == SAAT_STRING ) {
143 		/* Single string */
144 		const char *identifier = sieve_ast_argument_strc(arg);
145 		struct sieve_variable *var;
146 
147 		if ( (var=ext_include_variable_import_global
148 			(valdtr, cmd, identifier)) == NULL )
149 			return FALSE;
150 
151 		arg->argument = _create_variable_argument(cmd, var);
152 
153 	} else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) {
154 		/* String list */
155 		struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg);
156 
157 		while ( stritem != NULL ) {
158 			const char *identifier = sieve_ast_argument_strc(stritem);
159 			struct sieve_variable *var;
160 
161 			if ( (var=ext_include_variable_import_global
162 				(valdtr, cmd, identifier)) == NULL )
163 				return FALSE;
164 
165 			stritem->argument = _create_variable_argument(cmd, var);
166 
167 			stritem = sieve_ast_strlist_next(stritem);
168 		}
169 	} else {
170 		/* Something else */
171 		sieve_argument_validate_error(valdtr, arg,
172 			"the %s command accepts a single string or string list argument, "
173 			"but %s was found", sieve_command_identifier(cmd),
174 			sieve_ast_argument_name(arg));
175 		return FALSE;
176 	}
177 
178 	/* Join global commands with predecessors if possible */
179 	if ( sieve_commands_equal(prev, cmd) ) {
180 		/* Join this command's string list with the previous one */
181 		prev->first_positional = sieve_ast_stringlist_join
182 			(prev->first_positional, cmd->first_positional);
183 
184 		if ( prev->first_positional == NULL ) {
185 			/* Not going to happen unless MAXINT stringlist items are specified */
186 			sieve_command_validate_error(valdtr, cmd,
187 				"compiler reached AST limit (script too complex)");
188 			return FALSE;
189 		}
190 
191 		/* Detach this command node */
192 		sieve_ast_node_detach(cmd->ast_node);
193 	}
194 
195 	return TRUE;
196 }
197 
198 /*
199  * Code generation
200  */
201 
cmd_global_generate(const struct sieve_codegen_env * cgenv,struct sieve_command * cmd)202 static bool cmd_global_generate
203 (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
204 {
205 	struct sieve_ast_argument *arg = cmd->first_positional;
206 
207 	sieve_operation_emit(cgenv->sblock, cmd->ext, &global_operation);
208 
209 	if ( sieve_ast_argument_type(arg) == SAAT_STRING ) {
210 		/* Single string */
211 		struct sieve_variable *var = (struct sieve_variable *) arg->argument->data;
212 
213 		(void)sieve_binary_emit_unsigned(cgenv->sblock, 1);
214 		(void)sieve_binary_emit_unsigned(cgenv->sblock, var->index);
215 
216 	} else if ( sieve_ast_argument_type(arg) == SAAT_STRING_LIST ) {
217 		/* String list */
218 		struct sieve_ast_argument *stritem = sieve_ast_strlist_first(arg);
219 
220 		(void)sieve_binary_emit_unsigned
221 			(cgenv->sblock, sieve_ast_strlist_count(arg));
222 
223 		while ( stritem != NULL ) {
224 			struct sieve_variable *var =
225 				(struct sieve_variable *) stritem->argument->data;
226 
227 			(void)sieve_binary_emit_unsigned(cgenv->sblock, var->index);
228 
229 			stritem = sieve_ast_strlist_next(stritem);
230 		}
231 	} else {
232 		i_unreached();
233 	}
234 
235 	return TRUE;
236 }
237 
238 /*
239  * Code dump
240  */
241 
opc_global_dump(const struct sieve_dumptime_env * denv,sieve_size_t * address)242 static bool opc_global_dump
243 (const struct sieve_dumptime_env *denv, sieve_size_t *address)
244 {
245 	const struct sieve_extension *this_ext = denv->oprtn->ext;
246 	unsigned int count, i, var_count;
247 	struct sieve_variable_scope_binary *global_vars;
248 	struct sieve_variable_scope *global_scope;
249 	struct sieve_variable * const *vars;
250 
251 	if ( !sieve_binary_read_unsigned(denv->sblock, address, &count) )
252 		return FALSE;
253 
254 	sieve_code_dumpf(denv, "GLOBAL (count: %u):", count);
255 
256 	global_vars = ext_include_binary_get_global_scope(this_ext, denv->sbin);
257 	global_scope = sieve_variable_scope_binary_get(global_vars);
258 	vars = sieve_variable_scope_get_variables(global_scope, &var_count);
259 
260 	sieve_code_descend(denv);
261 
262 	for ( i = 0; i < count; i++ ) {
263 		unsigned int index;
264 
265 		sieve_code_mark(denv);
266 		if ( !sieve_binary_read_unsigned(denv->sblock, address, &index) ||
267 			index >= var_count )
268 			return FALSE;
269 
270 		sieve_code_dumpf(denv, "%d: VAR[%d]: '%s'", i, index, vars[index]->identifier);
271 	}
272 
273 	return TRUE;
274 }
275 
276 /*
277  * Execution
278  */
279 
opc_global_execute(const struct sieve_runtime_env * renv,sieve_size_t * address)280 static int opc_global_execute
281 (const struct sieve_runtime_env *renv, sieve_size_t *address)
282 {
283 	const struct sieve_extension *this_ext = renv->oprtn->ext;
284 	struct sieve_variable_scope_binary *global_vars;
285 	struct sieve_variable_scope *global_scope;
286 	struct sieve_variable_storage *storage;
287 	struct sieve_variable * const *vars;
288 	unsigned int var_count, count, i;
289 
290 	if ( !sieve_binary_read_unsigned(renv->sblock, address, &count) ) {
291 		sieve_runtime_trace_error(renv,
292 			"global: count operand invalid");
293 		return SIEVE_EXEC_BIN_CORRUPT;
294 	}
295 
296 	global_vars = ext_include_binary_get_global_scope(this_ext, renv->sbin);
297 	global_scope = sieve_variable_scope_binary_get(global_vars);
298 	vars = sieve_variable_scope_get_variables(global_scope, &var_count);
299 	storage = ext_include_interpreter_get_global_variables
300 		(this_ext, renv->interp);
301 
302 	for ( i = 0; i < count; i++ ) {
303 		unsigned int index;
304 
305 		if ( !sieve_binary_read_unsigned(renv->sblock, address, &index) ) {
306 			sieve_runtime_trace_error(renv,
307 				"global: variable index operand invalid");
308 			return SIEVE_EXEC_BIN_CORRUPT;
309 		}
310 
311 		if ( index >= var_count ) {
312 			sieve_runtime_trace_error(renv,
313 				"global: variable index %u is invalid in global storage (> %u)",
314 				index, var_count);
315 			return SIEVE_EXEC_BIN_CORRUPT;
316 		}
317 
318 		sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
319 			"global: exporting variable '%s' [gvid: %u, vid: %u]",
320 			vars[index]->identifier, i, index);
321 
322 		/* Make sure variable is initialized (export) */
323 		(void)sieve_variable_get_modifiable(storage, index, NULL);
324 	}
325 
326 	return SIEVE_EXEC_OK;
327 }
328 
329 
330