/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 1999-2021 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include void mu_i_sv_code (struct mu_sieve_machine *mach, sieve_op_t op) { if (mach->changeloc) { mach->changeloc = 0; mu_i_sv_code (mach, (sieve_op_t) _mu_i_sv_instr_locus); mu_i_sv_code (mach, (sieve_op_t) mu_i_sv_id_num (mach, mach->locus.beg.mu_file)); mu_i_sv_code (mach, (sieve_op_t) mach->locus.beg.mu_line); mu_i_sv_code (mach, (sieve_op_t) mach->locus.beg.mu_col); mu_i_sv_code (mach, (sieve_op_t) mu_i_sv_id_num (mach, mach->locus.end.mu_file)); mu_i_sv_code (mach, (sieve_op_t) mach->locus.end.mu_line); mu_i_sv_code (mach, (sieve_op_t) mach->locus.end.mu_col); } if (mach->pc >= mach->progsize) { mu_i_sv_2nrealloc (mach, (void**) &mach->prog, &mach->progsize, sizeof mach->prog[0]); } mach->prog[mach->pc++] = op; } int mu_i_sv_locus (struct mu_sieve_machine *mach, struct mu_locus_range *lr) { if (!mu_locus_point_eq (&mach->locus.beg, &lr->beg)) { mach->changeloc = 1; mu_locus_range_copy (&mach->locus, lr); } return 0; } mu_sieve_tag_def_t * find_tag (mu_sieve_tag_group_t *taglist, char *tagname, mu_sieve_tag_checker_t *checker) { *checker = NULL; if (!taglist) return NULL; for (; taglist->tags; taglist++) { mu_sieve_tag_def_t *def; for (def = taglist->tags; def->name; def++) if (strcmp (def->name, tagname) == 0) { *checker = taglist->checker; return def; } } return NULL; } struct check_arg { struct mu_sieve_machine *mach; struct mu_sieve_node *node; }; static int _run_checker (void *item, void *data) { struct check_arg *arg = data; mu_sieve_machine_t mach = arg->mach; struct mu_sieve_node *node = arg->node; mu_sieve_tag_checker_t checker = item; int rc; mach->comparator = node->v.command.comparator; mach->argstart = node->v.command.argstart; mach->argcount = node->v.command.argcount; mach->tagcount = node->v.command.tagcount; mach->identifier = node->v.command.reg->name; rc = checker (arg->mach); /* checker is allowed to alter these values */ node->v.command.comparator = mach->comparator; node->v.command.argcount = mach->argcount; node->v.command.tagcount = mach->tagcount; mach->argstart = 0; mach->argcount = 0; mach->tagcount = 0; mach->identifier = NULL; return rc ? MU_ERR_USER0 : 0; } void mu_i_sv_lint_command (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { size_t i; mu_sieve_registry_t *reg = node->v.command.reg; mu_sieve_value_t *start = mach->valspace + node->v.command.argstart; mu_list_t chk_list = NULL; mu_sieve_data_type *exp_arg; int opt_args = 0; int rc, err = 0; static mu_sieve_data_type empty[] = { SVT_VOID }; if (!reg) return; exp_arg = reg->v.command.req_args ? reg->v.command.req_args : empty; /* Pass 1: tag consolidation and argument checking */ for (i = 0; i < node->v.command.argcount; i++) { mu_sieve_value_t *val = start + i; if (val->type == SVT_TAG) { mu_sieve_tag_checker_t cf; mu_sieve_tag_def_t *tag = find_tag (reg->v.command.tags, val->v.string, &cf); if (!tag) { mu_diag_at_locus_range (MU_LOG_ERROR, &val->locus, _("invalid tag name `%s' for `%s'"), val->v.string, reg->name); mu_i_sv_error (mach); err = 1; break; } node->v.command.tagcount++; if (tag->argtype == SVT_VOID) { val->type = SVT_VOID; val->tag = val->v.string; val->v.string = NULL; } else { if (i + 1 == node->v.command.argcount) { /* FIXME: more exact locus */ mu_diag_at_locus_range (MU_LOG_ERROR, &mach->locus, _("required argument for tag %s is missing"), tag->name); mu_i_sv_error (mach); err = 1; break; } val[1].tag = val->v.string; *val = val[1]; memmove (val + 1, val + 2, (node->v.command.argcount - i - 2) * sizeof (val[0])); mach->valcount--; node->v.command.argcount--; if (val->type != tag->argtype) { mu_diag_at_locus_range (MU_LOG_ERROR, &val->locus, _("type mismatch in argument to " "tag `%s'"), tag->name); mu_diag_at_locus_range (MU_LOG_ERROR, &val->locus, _("expected %s but passed %s"), mu_sieve_type_str (tag->argtype), mu_sieve_type_str (val->type)); mu_i_sv_error (mach); err = 1; break; } } if (cf) { if (!chk_list && (rc = mu_list_create (&chk_list))) { mu_diag_at_locus_range (MU_LOG_ERROR, &mach->locus, _("cannot create check list: %s"), mu_strerror (rc)); mu_i_sv_error (mach); err = 1; break; } if (mu_list_locate (chk_list, cf, NULL) == MU_ERR_NOENT) { rc = mu_list_append (chk_list, cf); if (rc) { mu_diag_at_locus_range (MU_LOG_ERROR, &mach->locus, "mu_list_append: %s", mu_strerror (rc)); mu_i_sv_error (mach); err = 1; break; } } } } else { if (*exp_arg == SVT_VOID) { if (reg->v.command.opt_args) { exp_arg = reg->v.command.opt_args; opt_args = 1; } else { mu_diag_at_locus_range (MU_LOG_ERROR, &mach->locus, _("too many arguments in call to `%s'"), reg->name); mu_i_sv_error (mach); err = 1; break; } } if (*exp_arg != val->type) { if (*exp_arg == SVT_STRING_LIST && val->type == SVT_STRING) /* compatible types */; else { mu_diag_at_locus_range (MU_LOG_ERROR, &val->locus, _("type mismatch in argument %lu to `%s'"), (unsigned long) (exp_arg - reg->v.command.req_args + 1), reg->name); mu_diag_at_locus_range (MU_LOG_ERROR, &val->locus, _("expected %s but passed %s"), mu_sieve_type_str (*exp_arg), mu_sieve_type_str (val->type)); mu_i_sv_error (mach); err = 1; break; } } exp_arg++; } } if (!err && !opt_args && *exp_arg != SVT_VOID) { mu_diag_at_locus_range (MU_LOG_ERROR, &mach->locus, _("too few arguments in call to `%s'"), reg->name); mu_i_sv_error (mach); err = 1; } if (err) { mu_list_destroy (&chk_list); return; } if (node->v.command.tagcount) { /* Pass 2: Move tags to the end of the list */ for (i = 1; i < node->v.command.argcount; i++) { size_t j; mu_sieve_value_t tmp = start[i]; for (j = i; j > 0; j--) { if (!tmp.tag && start[j - 1].tag) start[j] = start[j - 1]; else break; } start[j] = tmp; } } node->v.command.argcount -= node->v.command.tagcount; if (chk_list) { struct check_arg chk_arg; chk_arg.mach = mach; chk_arg.node = node; err = mu_list_foreach (chk_list, _run_checker, &chk_arg); } } static void sv_code_command (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { mu_i_sv_code (mach, (sieve_op_t) node->v.command.reg->v.command.handler); mu_i_sv_code (mach, (sieve_op_t) node->v.command.argstart); mu_i_sv_code (mach, (sieve_op_t) node->v.command.argcount); mu_i_sv_code (mach, (sieve_op_t) node->v.command.tagcount); mu_i_sv_code (mach, (sieve_op_t) (char*) node->v.command.reg->name); mu_i_sv_code (mach, (sieve_op_t) node->v.command.comparator); } void mu_i_sv_code_action (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { mu_i_sv_code (mach, (sieve_op_t) _mu_i_sv_instr_action); sv_code_command (mach, node); } void mu_i_sv_code_test (struct mu_sieve_machine *mach, struct mu_sieve_node *node) { mu_i_sv_code (mach, (sieve_op_t) _mu_i_sv_instr_test); sv_code_command (mach, node); }