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