/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "string.h" #include "ostream.h" #include "hash.h" #include "mail-storage.h" #include "sieve.h" #include "sieve-code.h" #include "sieve-commands.h" #include "sieve-extensions.h" #include "sieve-validator.h" #include "sieve-generator.h" #include "sieve-binary.h" #include "sieve-dump.h" #include "testsuite-common.h" #include "testsuite-objects.h" #include "testsuite-message.h" /* * Testsuite core objects */ enum testsuite_object_code { TESTSUITE_OBJECT_MESSAGE, TESTSUITE_OBJECT_ENVELOPE }; const struct testsuite_object_def *testsuite_core_objects[] = { &message_testsuite_object, &envelope_testsuite_object }; const unsigned int testsuite_core_objects_count = N_ELEMENTS(testsuite_core_objects); /* * Testsuite object registry */ static inline struct sieve_validator_object_registry *_get_object_registry (struct sieve_validator *valdtr) { struct testsuite_validator_context *ctx = testsuite_validator_context_get(valdtr); return ctx->object_registrations; } void testsuite_object_register (struct sieve_validator *valdtr, const struct sieve_extension *ext, const struct testsuite_object_def *tobj_def) { struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); sieve_validator_object_registry_add(regs, ext, &tobj_def->obj_def); } static const struct testsuite_object *testsuite_object_create (struct sieve_validator *valdtr, struct sieve_command *cmd, const char *identifier) { struct sieve_validator_object_registry *regs = _get_object_registry(valdtr); struct sieve_object object; struct testsuite_object *tobj; if ( !sieve_validator_object_registry_find(regs, identifier, &object) ) return NULL; tobj = p_new(sieve_command_pool(cmd), struct testsuite_object, 1); tobj->object = object; tobj->def = (const struct testsuite_object_def *) object.def; return tobj; } void testsuite_register_core_objects (struct testsuite_validator_context *ctx) { struct sieve_validator_object_registry *regs = ctx->object_registrations; unsigned int i; /* Register core testsuite objects */ for ( i = 0; i < testsuite_core_objects_count; i++ ) { const struct testsuite_object_def *tobj_def = testsuite_core_objects[i]; sieve_validator_object_registry_add (regs, testsuite_ext, &tobj_def->obj_def); } } /* * Testsuite object code */ const struct sieve_operand_class sieve_testsuite_object_operand_class = { "testsuite object" }; static const struct sieve_extension_objects core_testsuite_objects = SIEVE_EXT_DEFINE_OBJECTS(testsuite_core_objects); const struct sieve_operand_def testsuite_object_operand = { .name = "testsuite-object", .ext_def = &testsuite_extension, .code = TESTSUITE_OPERAND_OBJECT, .class = &sieve_testsuite_object_operand_class, .interface = &core_testsuite_objects }; static void testsuite_object_emit (struct sieve_binary_block *sblock, const struct testsuite_object *tobj, int member_id) { sieve_opr_object_emit(sblock, tobj->object.ext, tobj->object.def); if ( tobj->def != NULL && tobj->def->get_member_id != NULL ) { (void) sieve_binary_emit_byte(sblock, (unsigned char) member_id); } } bool testsuite_object_read (struct sieve_binary_block *sblock, sieve_size_t *address, struct testsuite_object *tobj) { struct sieve_operand oprnd; if ( !sieve_operand_read(sblock, address, NULL, &oprnd) ) return FALSE; if ( !sieve_opr_object_read_data (sblock, &oprnd, &sieve_testsuite_object_operand_class, address, &tobj->object) ) return FALSE; tobj->def = (const struct testsuite_object_def *) tobj->object.def; i_assert(tobj->def != NULL); return TRUE; } bool testsuite_object_read_member (struct sieve_binary_block *sblock, sieve_size_t *address, struct testsuite_object *tobj, int *member_id_r) { if ( !testsuite_object_read(sblock, address, tobj) ) return FALSE; *member_id_r = -1; if ( tobj->def->get_member_id != NULL ) { if ( !sieve_binary_read_code(sblock, address, member_id_r) ) return FALSE; } return TRUE; } const char *testsuite_object_member_name (const struct testsuite_object *object, int member_id) { const struct testsuite_object_def *obj_def = object->def; const char *member = NULL; if ( obj_def->get_member_id != NULL ) { if ( obj_def->get_member_name != NULL ) member = obj_def->get_member_name(member_id); } else return obj_def->obj_def.identifier; if ( member == NULL ) return t_strdup_printf("%s.%d", obj_def->obj_def.identifier, member_id); return t_strdup_printf("%s.%s", obj_def->obj_def.identifier, member); } bool testsuite_object_dump (const struct sieve_dumptime_env *denv, sieve_size_t *address) { struct testsuite_object object; int member_id; sieve_code_mark(denv); if ( !testsuite_object_read_member (denv->sblock, address, &object, &member_id) ) return FALSE; sieve_code_dumpf(denv, "%s: %s", sieve_testsuite_object_operand_class.name, testsuite_object_member_name(&object, member_id)); return TRUE; } /* * Testsuite object argument */ static bool arg_testsuite_object_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd); const struct sieve_argument_def testsuite_object_argument = { .identifier = "testsuite-object", .generate = arg_testsuite_object_generate }; struct testsuite_object_argctx { const struct testsuite_object *object; int member; }; bool testsuite_object_argument_activate (struct sieve_validator *valdtr, struct sieve_ast_argument *arg, struct sieve_command *cmd) { const char *objname = sieve_ast_argument_strc(arg); const struct testsuite_object *tobj; int member_id; const char *member; struct testsuite_object_argctx *ctx; /* Parse the object specifier */ member = strchr(objname, '.'); if ( member != NULL ) { objname = t_strdup_until(objname, member); member++; } /* Find the object */ tobj = testsuite_object_create(valdtr, cmd, objname); if ( tobj == NULL ) { sieve_argument_validate_error(valdtr, arg, "unknown testsuite object '%s'", objname); return FALSE; } /* Find the object member */ member_id = -1; if ( member != NULL ) { if ( tobj->def == NULL || tobj->def->get_member_id == NULL || (member_id=tobj->def->get_member_id(member)) == -1 ) { sieve_argument_validate_error(valdtr, arg, "member '%s' does not exist for testsuite object '%s'", member, objname); return FALSE; } } /* Assign argument context */ ctx = p_new(sieve_command_pool(cmd), struct testsuite_object_argctx, 1); ctx->object = tobj; ctx->member = member_id; arg->argument = sieve_argument_create (arg->ast, &testsuite_object_argument, testsuite_ext, 0); arg->argument->data = (void *) ctx; return TRUE; } static bool arg_testsuite_object_generate (const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg, struct sieve_command *cmd ATTR_UNUSED) { struct testsuite_object_argctx *ctx = (struct testsuite_object_argctx *) arg->argument->data; testsuite_object_emit(cgenv->sblock, ctx->object, ctx->member); return TRUE; } /* * Testsuite core object implementation */ static bool tsto_message_set_member (const struct sieve_runtime_env *renv, int id, string_t *value); static int tsto_envelope_get_member_id(const char *identifier); static const char *tsto_envelope_get_member_name(int id); static bool tsto_envelope_set_member (const struct sieve_runtime_env *renv, int id, string_t *value); const struct testsuite_object_def message_testsuite_object = { SIEVE_OBJECT("message", &testsuite_object_operand, TESTSUITE_OBJECT_MESSAGE), .set_member = tsto_message_set_member }; const struct testsuite_object_def envelope_testsuite_object = { SIEVE_OBJECT("envelope", &testsuite_object_operand, TESTSUITE_OBJECT_ENVELOPE), .get_member_id = tsto_envelope_get_member_id, .get_member_name = tsto_envelope_get_member_name, .set_member = tsto_envelope_set_member }; enum testsuite_object_envelope_field { TESTSUITE_OBJECT_ENVELOPE_FROM, TESTSUITE_OBJECT_ENVELOPE_TO, TESTSUITE_OBJECT_ENVELOPE_ORIG_TO, TESTSUITE_OBJECT_ENVELOPE_AUTH_USER }; static bool tsto_message_set_member (const struct sieve_runtime_env *renv, int id, string_t *value) { if ( id != -1 ) return FALSE; testsuite_message_set_string(renv, value); return TRUE; } static int tsto_envelope_get_member_id(const char *identifier) { if ( strcasecmp(identifier, "from") == 0 ) return TESTSUITE_OBJECT_ENVELOPE_FROM; if ( strcasecmp(identifier, "to") == 0 ) return TESTSUITE_OBJECT_ENVELOPE_TO; if ( strcasecmp(identifier, "orig_to") == 0 ) return TESTSUITE_OBJECT_ENVELOPE_ORIG_TO; if ( strcasecmp(identifier, "auth") == 0 ) return TESTSUITE_OBJECT_ENVELOPE_AUTH_USER; return -1; } static const char *tsto_envelope_get_member_name(int id) { switch ( id ) { case TESTSUITE_OBJECT_ENVELOPE_FROM: return "from"; case TESTSUITE_OBJECT_ENVELOPE_TO: return "to"; case TESTSUITE_OBJECT_ENVELOPE_ORIG_TO: return "orig_to"; case TESTSUITE_OBJECT_ENVELOPE_AUTH_USER: return "auth"; } return NULL; } static bool tsto_envelope_set_member (const struct sieve_runtime_env *renv, int id, string_t *value) { switch ( id ) { case TESTSUITE_OBJECT_ENVELOPE_FROM: testsuite_envelope_set_sender(renv, str_c(value)); return TRUE; case TESTSUITE_OBJECT_ENVELOPE_TO: testsuite_envelope_set_recipient(renv, str_c(value)); return TRUE; case TESTSUITE_OBJECT_ENVELOPE_ORIG_TO: testsuite_envelope_set_orig_recipient(renv, str_c(value)); return TRUE; case TESTSUITE_OBJECT_ENVELOPE_AUTH_USER: testsuite_envelope_set_auth_user(renv, str_c(value)); return TRUE; } return FALSE; }