diff options
Diffstat (limited to '')
-rw-r--r-- | src/plugins_types.c | 1043 |
1 files changed, 1043 insertions, 0 deletions
diff --git a/src/plugins_types.c b/src/plugins_types.c new file mode 100644 index 0000000..cb4b896 --- /dev/null +++ b/src/plugins_types.c @@ -0,0 +1,1043 @@ +/** + * @file plugins_types.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief Built-in types plugins and interface for user types plugins. + * + * Copyright (c) 2019 - 2022 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ + +#define _GNU_SOURCE /* asprintf, strdup */ + +#include "plugins_types.h" + +#include <assert.h> +#include <ctype.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "common.h" +#include "compat.h" +#include "context.h" +#include "dict.h" +#include "path.h" +#include "schema_compile.h" +#include "set.h" +#include "tree.h" +#include "tree_data.h" +#include "tree_data_internal.h" +#include "tree_schema.h" +#include "tree_schema_internal.h" +#include "xml.h" +#include "xpath.h" + +/** + * @brief Find import prefix in imports. + */ +static const struct lys_module * +ly_schema_resolve_prefix(const struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len, const void *prefix_data) +{ + const struct lysp_module *prefix_mod = prefix_data; + struct lys_module *m = NULL; + LY_ARRAY_COUNT_TYPE u; + const char *local_prefix; + + local_prefix = prefix_mod->is_submod ? ((struct lysp_submodule *)prefix_mod)->prefix : prefix_mod->mod->prefix; + if (!prefix_len || !ly_strncmp(local_prefix, prefix, prefix_len)) { + /* it is the prefix of the module itself */ + m = prefix_mod->mod; + } + + /* search in imports */ + if (!m) { + LY_ARRAY_FOR(prefix_mod->imports, u) { + if (!ly_strncmp(prefix_mod->imports[u].prefix, prefix, prefix_len)) { + m = prefix_mod->imports[u].module; + break; + } + } + } + + return m; +} + +/** + * @brief Find resolved module for a prefix in prefix - module pairs. + */ +static const struct lys_module * +ly_schema_resolved_resolve_prefix(const struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len, + const void *prefix_data) +{ + const struct lysc_prefix *prefixes = prefix_data; + LY_ARRAY_COUNT_TYPE u; + + LY_ARRAY_FOR(prefixes, u) { + if ((!prefixes[u].prefix && !prefix_len) || (prefixes[u].prefix && !ly_strncmp(prefixes[u].prefix, prefix, prefix_len))) { + return prefixes[u].mod; + } + } + + return NULL; +} + +/** + * @brief Find XML namespace prefix in XML namespaces, which are then mapped to modules. + */ +static const struct lys_module * +ly_xml_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len, const void *prefix_data) +{ + const struct lys_module *mod; + const struct lyxml_ns *ns; + const struct ly_set *ns_set = prefix_data; + + ns = lyxml_ns_get(ns_set, prefix, prefix_len); + if (!ns) { + return NULL; + } + + mod = ly_ctx_get_module_implemented_ns(ctx, ns->uri); + if (!mod) { + /* for YIN extension prefix resolution */ + mod = ly_ctx_get_module_latest_ns(ctx, ns->uri); + } + return mod; +} + +/** + * @brief Find module name. + */ +static const struct lys_module * +ly_json_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len, const void *UNUSED(prefix_data)) +{ + return ly_ctx_get_module_implemented2(ctx, prefix, prefix_len); +} + +const struct lys_module * +ly_resolve_prefix(const struct ly_ctx *ctx, const void *prefix, size_t prefix_len, LY_VALUE_FORMAT format, + const void *prefix_data) +{ + const struct lys_module *mod = NULL; + + LY_CHECK_ARG_RET(ctx, prefix, prefix_len, NULL); + + switch (format) { + case LY_VALUE_SCHEMA: + mod = ly_schema_resolve_prefix(ctx, prefix, prefix_len, prefix_data); + break; + case LY_VALUE_SCHEMA_RESOLVED: + mod = ly_schema_resolved_resolve_prefix(ctx, prefix, prefix_len, prefix_data); + break; + case LY_VALUE_XML: + case LY_VALUE_STR_NS: + mod = ly_xml_resolve_prefix(ctx, prefix, prefix_len, prefix_data); + break; + case LY_VALUE_CANON: + case LY_VALUE_JSON: + case LY_VALUE_LYB: + mod = ly_json_resolve_prefix(ctx, prefix, prefix_len, prefix_data); + break; + } + + return mod; +} + +LIBYANG_API_DEF const struct lys_module * +lyplg_type_identity_module(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *prefix, + size_t prefix_len, LY_VALUE_FORMAT format, const void *prefix_data) +{ + if (prefix_len) { + return ly_resolve_prefix(ctx, prefix, prefix_len, format, prefix_data); + } else { + switch (format) { + case LY_VALUE_SCHEMA: + /* use local module */ + return ly_schema_resolve_prefix(ctx, prefix, prefix_len, prefix_data); + case LY_VALUE_SCHEMA_RESOLVED: + /* use local module */ + return ly_schema_resolved_resolve_prefix(ctx, prefix, prefix_len, prefix_data); + case LY_VALUE_CANON: + case LY_VALUE_JSON: + case LY_VALUE_LYB: + case LY_VALUE_STR_NS: + /* use context node module (as specified) */ + return ctx_node ? ctx_node->module : NULL; + case LY_VALUE_XML: + /* use the default namespace */ + return ly_xml_resolve_prefix(ctx, NULL, 0, prefix_data); + } + } + + return NULL; +} + +/** + * @brief Find module in import prefixes. + */ +static const char * +ly_schema_get_prefix(const struct lys_module *mod, void *prefix_data) +{ + const struct lysp_module *pmod = prefix_data; + LY_ARRAY_COUNT_TYPE u; + + if (pmod->mod == mod) { + if (pmod->is_submod) { + return ((struct lysp_submodule *)pmod)->prefix; + } else { + return pmod->mod->prefix; + } + } + + LY_ARRAY_FOR(pmod->imports, u) { + if (pmod->imports[u].module == mod) { + /* match */ + return pmod->imports[u].prefix; + } + } + + return NULL; +} + +/** + * @brief Find prefix in prefix - module pairs. + */ +static const char * +ly_schema_resolved_get_prefix(const struct lys_module *mod, void *prefix_data) +{ + struct lysc_prefix *prefixes = prefix_data; + LY_ARRAY_COUNT_TYPE u; + + LY_ARRAY_FOR(prefixes, u) { + if (prefixes[u].mod == mod) { + return prefixes[u].prefix; + } + } + + return NULL; +} + +/** + * @brief Simply return module local prefix. Also, store the module in a set. + */ +static const char * +ly_xml_get_prefix(const struct lys_module *mod, void *prefix_data) +{ + struct ly_set *ns_list = prefix_data; + + LY_CHECK_RET(ly_set_add(ns_list, (void *)mod, 0, NULL), NULL); + return mod->prefix; +} + +/** + * @brief Simply return module name. + */ +static const char * +ly_json_get_prefix(const struct lys_module *mod, void *UNUSED(prefix_data)) +{ + return mod->name; +} + +const char * +ly_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data) +{ + const char *prefix = NULL; + + switch (format) { + case LY_VALUE_SCHEMA: + prefix = ly_schema_get_prefix(mod, prefix_data); + break; + case LY_VALUE_SCHEMA_RESOLVED: + prefix = ly_schema_resolved_get_prefix(mod, prefix_data); + break; + case LY_VALUE_XML: + case LY_VALUE_STR_NS: + prefix = ly_xml_get_prefix(mod, prefix_data); + break; + case LY_VALUE_CANON: + case LY_VALUE_JSON: + case LY_VALUE_LYB: + prefix = ly_json_get_prefix(mod, prefix_data); + break; + } + + return prefix; +} + +LIBYANG_API_DEF const char * +lyplg_type_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data) +{ + return ly_get_prefix(mod, format, prefix_data); +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_compare_simple(const struct lyd_value *val1, const struct lyd_value *val2) +{ + if (val1->realtype != val2->realtype) { + return LY_ENOT; + } + + if (val1->_canonical == val2->_canonical) { + return LY_SUCCESS; + } + + return LY_ENOT; +} + +LIBYANG_API_DEF const void * +lyplg_type_print_simple(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT UNUSED(format), + void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len) +{ + if (dynamic) { + *dynamic = 0; + } + if (value_len) { + *value_len = ly_strlen(value->_canonical); + } + return value->_canonical; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_dup_simple(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup) +{ + memset(dup, 0, sizeof *dup); + LY_CHECK_RET(lydict_insert(ctx, original->_canonical, 0, &dup->_canonical)); + memcpy(dup->fixed_mem, original->fixed_mem, sizeof dup->fixed_mem); + dup->realtype = original->realtype; + return LY_SUCCESS; +} + +LIBYANG_API_DEF void +lyplg_type_free_simple(const struct ly_ctx *ctx, struct lyd_value *value) +{ + lydict_remove(ctx, value->_canonical); + value->_canonical = NULL; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value, size_t value_len, + int64_t *ret, struct ly_err_item **err) +{ + LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL); + + *err = NULL; + + /* consume leading whitespaces */ + for ( ; value_len && isspace(*value); ++value, --value_len) {} + + if (!value || !value_len || !value[0]) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid type %s empty value.", datatype); + } + + switch (ly_parse_int(value, value_len, min, max, base, ret)) { + case LY_EDENIED: + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, + "Value \"%.*s\" is out of type %s min/max bounds.", (int)value_len, value, datatype); + case LY_SUCCESS: + return LY_SUCCESS; + default: + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, + "Invalid type %s value \"%.*s\".", datatype, (int)value_len, value); + } +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_parse_uint(const char *datatype, int base, uint64_t max, const char *value, size_t value_len, uint64_t *ret, + struct ly_err_item **err) +{ + LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL); + + *err = NULL; + + /* consume leading whitespaces */ + for ( ; value_len && isspace(*value); ++value, --value_len) {} + + if (!value || !value_len || !value[0]) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid type %s empty value.", datatype); + } + + *err = NULL; + switch (ly_parse_uint(value, value_len, max, base, ret)) { + case LY_EDENIED: + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, + "Value \"%.*s\" is out of type %s min/max bounds.", (int)value_len, value, datatype); + case LY_SUCCESS: + return LY_SUCCESS; + default: + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, + "Invalid type %s value \"%.*s\".", datatype, (int)value_len, value); + } +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_parse_dec64(uint8_t fraction_digits, const char *value, size_t value_len, int64_t *ret, struct ly_err_item **err) +{ + LY_ERR ret_val; + char *valcopy = NULL; + size_t fraction = 0, size, len = 0, trailing_zeros; + int64_t d; + + *err = NULL; + + /* consume leading whitespaces */ + for ( ; value_len && isspace(*value); ++value, --value_len) {} + + /* parse value */ + if (!value_len) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty decimal64 value."); + } else if (!isdigit(value[len]) && (value[len] != '-') && (value[len] != '+')) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid %zu. character of decimal64 value \"%.*s\".", + len + 1, (int)value_len, value); + } + + if ((value[len] == '-') || (value[len] == '+')) { + ++len; + } + + while (len < value_len && isdigit(value[len])) { + ++len; + } + + trailing_zeros = 0; + if ((len < value_len) && ((value[len] != '.') || !isdigit(value[len + 1]))) { + goto decimal; + } + fraction = len; + ++len; + while (len < value_len && isdigit(value[len])) { + if (value[len] == '0') { + ++trailing_zeros; + } else { + trailing_zeros = 0; + } + ++len; + } + len = len - trailing_zeros; + +decimal: + if (fraction && (len - 1 - fraction > fraction_digits)) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, + "Value \"%.*s\" of decimal64 type exceeds defined number (%u) of fraction digits.", + (int)len, value, fraction_digits); + } + if (fraction) { + size = len + (fraction_digits - (len - 1 - fraction)); + } else { + size = len + fraction_digits + 1; + } + + if (len + trailing_zeros < value_len) { + /* consume trailing whitespaces to check that there is nothing after it */ + uint64_t u; + + for (u = len + trailing_zeros; u < value_len && isspace(value[u]); ++u) {} + if (u != value_len) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, + "Invalid %" PRIu64 ". character of decimal64 value \"%.*s\".", u + 1, (int)value_len, value); + } + } + + /* prepare value string without decimal point to easily parse using standard functions */ + valcopy = malloc(size * sizeof *valcopy); + if (!valcopy) { + return ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG); + } + + valcopy[size - 1] = '\0'; + if (fraction) { + memcpy(&valcopy[0], &value[0], fraction); + memcpy(&valcopy[fraction], &value[fraction + 1], len - 1 - (fraction)); + /* add trailing zero characters */ + memset(&valcopy[len - 1], '0', fraction_digits - (len - 1 - fraction)); + } else { + memcpy(&valcopy[0], &value[0], len); + /* add trailing zero characters */ + memset(&valcopy[len], '0', fraction_digits); + } + + ret_val = lyplg_type_parse_int("decimal64", LY_BASE_DEC, INT64_C(-9223372036854775807) - INT64_C(1), + INT64_C(9223372036854775807), valcopy, size - 1, &d, err); + if (!ret_val && ret) { + *ret = d; + } + free(valcopy); + + return ret_val; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_validate_patterns(struct lysc_pattern **patterns, const char *str, size_t str_len, struct ly_err_item **err) +{ + int rc, match_opts; + LY_ARRAY_COUNT_TYPE u; + pcre2_match_data *match_data = NULL; + + LY_CHECK_ARG_RET(NULL, str, err, LY_EINVAL); + + *err = NULL; + + LY_ARRAY_FOR(patterns, u) { + /* match_data needs to be allocated each time because of possible multi-threaded evaluation */ + match_data = pcre2_match_data_create_from_pattern(patterns[u]->code, NULL); + if (!match_data) { + return ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG); + } + + match_opts = PCRE2_ANCHORED; +#ifdef PCRE2_ENDANCHORED + /* PCRE2_ENDANCHORED was added in PCRE2 version 10.30 */ + match_opts |= PCRE2_ENDANCHORED; +#endif + rc = pcre2_match(patterns[u]->code, (PCRE2_SPTR)str, str_len, 0, match_opts, match_data, NULL); + pcre2_match_data_free(match_data); + + if ((rc != PCRE2_ERROR_NOMATCH) && (rc < 0)) { + PCRE2_UCHAR pcre2_errmsg[LY_PCRE2_MSG_LIMIT] = {0}; + + pcre2_get_error_message(rc, pcre2_errmsg, LY_PCRE2_MSG_LIMIT); + + return ly_err_new(err, LY_ESYS, 0, NULL, NULL, "%s", (const char *)pcre2_errmsg); + } else if (((rc == PCRE2_ERROR_NOMATCH) && !patterns[u]->inverted) || + ((rc != PCRE2_ERROR_NOMATCH) && patterns[u]->inverted)) { + char *eapptag = patterns[u]->eapptag ? strdup(patterns[u]->eapptag) : NULL; + + if (patterns[u]->emsg) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", patterns[u]->emsg); + } else { + const char *inverted = patterns[u]->inverted ? "inverted " : ""; + + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, + LY_ERRMSG_NOPATTERN, (int)str_len, str, inverted, patterns[u]->expr); + } + } + } + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval, + size_t strval_len, struct ly_err_item **err) +{ + LY_ARRAY_COUNT_TYPE u; + ly_bool is_length; /* length or range */ + + *err = NULL; + is_length = (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? 1 : 0; + + LY_ARRAY_FOR(range->parts, u) { + if (basetype < LY_TYPE_DEC64) { + /* unsigned */ + if ((uint64_t)value < range->parts[u].min_u64) { + char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL; + + if (range->emsg) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg); + } else { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, + is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval); + } + } else if ((uint64_t)value <= range->parts[u].max_u64) { + /* inside the range */ + return LY_SUCCESS; + } else if (u == LY_ARRAY_COUNT(range->parts) - 1) { + /* we have the last range part, so the value is out of bounds */ + char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL; + + if (range->emsg) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg); + } else { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, + is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval); + } + } + } else { + /* signed */ + if (value < range->parts[u].min_64) { + char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL; + + if (range->emsg) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg); + } else { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval); + } + } else if (value <= range->parts[u].max_64) { + /* inside the range */ + return LY_SUCCESS; + } else if (u == LY_ARRAY_COUNT(range->parts) - 1) { + /* we have the last range part, so the value is out of bounds */ + char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL; + + if (range->emsg) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg); + } else { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval); + } + } + } + } + + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_prefix_data_new(const struct ly_ctx *ctx, const void *value, size_t value_len, LY_VALUE_FORMAT format, + const void *prefix_data, LY_VALUE_FORMAT *format_p, void **prefix_data_p) +{ + LY_CHECK_ARG_RET(ctx, value, format_p, prefix_data_p, LY_EINVAL); + + *prefix_data_p = NULL; + return ly_store_prefix_data(ctx, value, value_len, format, prefix_data, format_p, (void **)prefix_data_p); +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_prefix_data_dup(const struct ly_ctx *ctx, LY_VALUE_FORMAT format, const void *orig, void **dup) +{ + LY_CHECK_ARG_RET(NULL, dup, LY_EINVAL); + + *dup = NULL; + if (!orig) { + return LY_SUCCESS; + } + + return ly_dup_prefix_data(ctx, format, orig, (void **)dup); +} + +LIBYANG_API_DEF void +lyplg_type_prefix_data_free(LY_VALUE_FORMAT format, void *prefix_data) +{ + ly_free_prefix_data(format, prefix_data); +} + +static int +type_get_hints_base(uint32_t hints) +{ + /* set allowed base */ + switch (hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM)) { + case LYD_VALHINT_DECNUM: + return LY_BASE_DEC; + case LYD_VALHINT_OCTNUM: + return LY_BASE_OCT; + case LYD_VALHINT_HEXNUM: + return LY_BASE_HEX; + default: + /* generic base - decimal by default, hexa if prexed by 0x/0X and octal otherwise if prefixed by 0 */ + return 0; + } +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_DATA_TYPE type, int *base, + struct ly_err_item **err) +{ + LY_CHECK_ARG_RET(NULL, value || !value_len, err, LY_EINVAL); + + *err = NULL; + if (!value) { + value = ""; + } + + switch (type) { + case LY_TYPE_UINT8: + case LY_TYPE_UINT16: + case LY_TYPE_UINT32: + case LY_TYPE_INT8: + case LY_TYPE_INT16: + case LY_TYPE_INT32: + LY_CHECK_ARG_RET(NULL, base, LY_EINVAL); + + if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM))) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-number-encoded %s value \"%.*s\".", + lys_datatype2str(type), (int)value_len, value); + } + *base = type_get_hints_base(hints); + break; + case LY_TYPE_UINT64: + case LY_TYPE_INT64: + LY_CHECK_ARG_RET(NULL, base, LY_EINVAL); + + if (!(hints & LYD_VALHINT_NUM64)) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-num64-encoded %s value \"%.*s\".", + lys_datatype2str(type), (int)value_len, value); + } + *base = type_get_hints_base(hints); + break; + case LY_TYPE_STRING: + case LY_TYPE_DEC64: + case LY_TYPE_ENUM: + case LY_TYPE_BITS: + case LY_TYPE_BINARY: + case LY_TYPE_IDENT: + case LY_TYPE_INST: + if (!(hints & LYD_VALHINT_STRING)) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-string-encoded %s value \"%.*s\".", + lys_datatype2str(type), (int)value_len, value); + } + break; + case LY_TYPE_BOOL: + if (!(hints & LYD_VALHINT_BOOLEAN)) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-boolean-encoded %s value \"%.*s\".", + lys_datatype2str(type), (int)value_len, value); + } + break; + case LY_TYPE_EMPTY: + if (!(hints & LYD_VALHINT_EMPTY)) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-empty-encoded %s value \"%.*s\".", + lys_datatype2str(type), (int)value_len, value); + } + break; + case LY_TYPE_UNKNOWN: + case LY_TYPE_LEAFREF: + case LY_TYPE_UNION: + LOGINT_RET(NULL); + } + + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_check_status(const struct lysc_node *ctx_node, uint16_t val_flags, LY_VALUE_FORMAT format, void *prefix_data, + const char *val_name, struct ly_err_item **err) +{ + LY_ERR ret; + const struct lys_module *mod2; + uint16_t flg1, flg2; + + if (format != LY_VALUE_SCHEMA) { + /* nothing/unable to check */ + return LY_SUCCESS; + } + + mod2 = ((struct lysp_module *)prefix_data)->mod; + + if (mod2 == ctx_node->module) { + /* use flags of the context node since the definition is local */ + flg1 = (ctx_node->flags & LYS_STATUS_MASK) ? (ctx_node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR; + } else { + /* definition is foreign (deviation, refine), always current */ + flg1 = LYS_STATUS_CURR; + } + flg2 = (val_flags & LYS_STATUS_MASK) ? (val_flags & LYS_STATUS_MASK) : LYS_STATUS_CURR; + + if ((flg1 < flg2) && (ctx_node->module == mod2)) { + ret = ly_err_new(err, LY_EVALID, LYVE_REFERENCE, NULL, NULL, + "A %s definition \"%s\" is not allowed to reference %s value \"%s\".", + flg1 == LYS_STATUS_CURR ? "current" : "deprecated", ctx_node->name, + flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", val_name); + return ret; + } + + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_lypath_check_status(const struct lysc_node *ctx_node, const struct ly_path *path, LY_VALUE_FORMAT format, + void *prefix_data, struct ly_err_item **err) +{ + LY_ERR ret; + LY_ARRAY_COUNT_TYPE u; + const struct lys_module *val_mod; + const struct lysc_node *node; + uint16_t flg1, flg2; + + if (format != LY_VALUE_SCHEMA) { + /* nothing to check */ + return LY_SUCCESS; + } + + val_mod = ((struct lysp_module *)prefix_data)->mod; + if (val_mod == ctx_node->module) { + /* use flags of the context node since the definition is local */ + flg1 = (ctx_node->flags & LYS_STATUS_MASK) ? (ctx_node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR; + } else { + /* definition is foreign (deviation, refine), always current */ + flg1 = LYS_STATUS_CURR; + } + + LY_ARRAY_FOR(path, u) { + node = path[u].node; + + flg2 = (node->flags & LYS_STATUS_MASK) ? (node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR; + if ((flg1 < flg2) && (val_mod == node->module)) { + ret = ly_err_new(err, LY_EVALID, LYVE_REFERENCE, NULL, NULL, + "A %s definition \"%s\" is not allowed to reference %s value \"%s\".", + flg1 == LYS_STATUS_CURR ? "current" : "deprecated", ctx_node->name, + flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", node->name); + return ret; + } + } + + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, size_t value_len, uint32_t options, + LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *ctx_node, struct lys_glob_unres *unres, + struct ly_path **path, struct ly_err_item **err) +{ + LY_ERR ret = LY_SUCCESS; + struct lyxp_expr *exp = NULL; + uint32_t prefix_opt = 0; + + LY_CHECK_ARG_RET(ctx, ctx, value, ctx_node, path, err, LY_EINVAL); + + *path = NULL; + *err = NULL; + + switch (format) { + case LY_VALUE_SCHEMA: + case LY_VALUE_SCHEMA_RESOLVED: + case LY_VALUE_XML: + prefix_opt = LY_PATH_PREFIX_MANDATORY; + break; + case LY_VALUE_CANON: + case LY_VALUE_LYB: + case LY_VALUE_JSON: + case LY_VALUE_STR_NS: + prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT; + break; + } + + /* parse the value */ + ret = ly_path_parse(ctx, ctx_node, value, value_len, 0, LY_PATH_BEGIN_ABSOLUTE, prefix_opt, LY_PATH_PRED_SIMPLE, &exp); + if (ret) { + ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, + "Invalid instance-identifier \"%.*s\" value - syntax error.", (int)value_len, value); + goto cleanup; + } + + if (options & LYPLG_TYPE_STORE_IMPLEMENT) { + /* implement all prefixes */ + LY_CHECK_GOTO(ret = lys_compile_expr_implement(ctx, exp, format, prefix_data, 1, unres, NULL), cleanup); + } + + /* resolve it on schema tree */ + ret = ly_path_compile(ctx, NULL, ctx_node, NULL, exp, (ctx_node->flags & LYS_IS_OUTPUT) ? + LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, 1, format, prefix_data, path); + if (ret) { + ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL, + "Invalid instance-identifier \"%.*s\" value - semantic error.", (int)value_len, value); + goto cleanup; + } + +cleanup: + lyxp_expr_free(ctx, exp); + if (ret) { + ly_path_free(ctx, *path); + *path = NULL; + } + + return ret; +} + +LIBYANG_API_DEF void +lyplg_type_lypath_free(const struct ly_ctx *ctx, struct ly_path *path) +{ + ly_path_free(ctx, path); +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_make_implemented(struct lys_module *mod, const char **features, struct lys_glob_unres *unres) +{ + if (mod->implemented) { + return LY_SUCCESS; + } + + LY_CHECK_RET(lys_implement(mod, features, unres)); + LY_CHECK_RET(lys_compile(mod, &unres->ds_unres)); + + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_identity_isderived(const struct lysc_ident *base, const struct lysc_ident *der) +{ + LY_ARRAY_COUNT_TYPE u; + + assert(base->module->ctx == der->module->ctx); + + LY_ARRAY_FOR(base->derived, u) { + if (der == base->derived[u]) { + return LY_SUCCESS; + } + if (!lyplg_type_identity_isderived(base->derived[u], der)) { + return LY_SUCCESS; + } + } + return LY_ENOTFOUND; +} + +/** + * @brief Try to generate a path to the leafref target with its value to enable the use of hash-based search. + * + * @param[in] path Leafref path. + * @param[in] ctx_node Leafref context node. + * @param[in] format Format of @p path. + * @param[in] prefix_data Prefix data of @p path. + * @param[in] target_val Leafref target value. + * @param[out] target_path Generated path with the target value. + * @return LY_SUCCESS on success. + * @return LY_ENOT if no matching target exists. + * @return LY_ERR on error. + */ +static LY_ERR +lyplg_type_resolve_leafref_get_target_path(const struct lyxp_expr *path, const struct lysc_node *ctx_node, + LY_VALUE_FORMAT format, void *prefix_data, const char *target_val, struct lyxp_expr **target_path) +{ + LY_ERR rc = LY_SUCCESS; + uint8_t oper; + struct ly_path *p = NULL; + char *str_path = NULL, quot; + int len; + ly_bool list_key = 0; + + *target_path = NULL; + + /* compile, has already been so it must succeed */ + oper = (ctx_node->flags & LYS_IS_OUTPUT) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT; + if (ly_path_compile_leafref(ctx_node->module->ctx, ctx_node, NULL, path, oper, LY_PATH_TARGET_MANY, format, + prefix_data, &p)) { + /* the target was found before but is disabled so it was removed */ + return LY_ENOT; + } + + /* check whether we can search for a list instance with a specific key value */ + if (lysc_is_key(p[LY_ARRAY_COUNT(p) - 1].node)) { + if ((LY_ARRAY_COUNT(p) >= 2) && (p[LY_ARRAY_COUNT(p) - 2].node->nodetype == LYS_LIST)) { + if ((path->tokens[path->used - 1] == LYXP_TOKEN_NAMETEST) && + (path->tokens[path->used - 2] == LYXP_TOKEN_OPER_PATH) && + (path->tokens[path->used - 3] == LYXP_TOKEN_NAMETEST)) { + list_key = 1; + } /* else again, should be possible but does not make sense */ + } /* else allowed despite not making sense */ + } + + if (list_key) { + /* get the length of the orig expression without the last "/" and the key node */ + len = path->tok_pos[path->used - 3] + path->tok_len[path->used - 3]; + + /* generate the string path evaluated using hashes */ + quot = strchr(target_val, '\'') ? '\"' : '\''; + if (asprintf(&str_path, "%.*s[%s=%c%s%c]/%s", len, path->expr, path->expr + path->tok_pos[path->used - 1], + quot, target_val, quot, path->expr + path->tok_pos[path->used - 1]) == -1) { + LOGMEM(ctx_node->module->ctx); + rc = LY_EMEM; + goto cleanup; + } + + } else { + /* leaf will not be found using hashes, but generate the path just to unify it */ + assert(p[LY_ARRAY_COUNT(p) - 1].node->nodetype & LYD_NODE_TERM); + + /* generate the string path evaluated using hashes */ + quot = strchr(target_val, '\'') ? '\"' : '\''; + if (asprintf(&str_path, "%s[.=%c%s%c]", path->expr, quot, target_val, quot) == -1) { + LOGMEM(ctx_node->module->ctx); + rc = LY_EMEM; + goto cleanup; + } + } + + /* parse into an expression */ + LY_CHECK_GOTO(lyxp_expr_parse(ctx_node->module->ctx, str_path, 0, 1, target_path), cleanup); + +cleanup: + ly_path_free(ctx_node->module->ctx, p); + free(str_path); + return rc; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_resolve_leafref(const struct lysc_type_leafref *lref, const struct lyd_node *node, struct lyd_value *value, + const struct lyd_node *tree, struct lyd_node **target, char **errmsg) +{ + LY_ERR rc = LY_SUCCESS; + struct lyxp_expr *target_path = NULL; + struct lyxp_set set = {0}; + const char *val_str, *xp_err_msg; + uint32_t i; + int r; + + LY_CHECK_ARG_RET(NULL, lref, node, value, errmsg, LY_EINVAL); + + if (target) { + *target = NULL; + } + + /* get the canonical value */ + val_str = lyd_value_get_canonical(LYD_CTX(node), value); + + if (!strchr(val_str, '\"') || !strchr(val_str, '\'')) { + /* get the path with the value */ + r = lyplg_type_resolve_leafref_get_target_path(lref->path, node->schema, LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, + val_str, &target_path); + if (r == LY_ENOT) { + goto cleanup; + } else if (r) { + rc = r; + goto cleanup; + } + } /* else value with both ' and ", XPath does not support that */ + + /* find the target data instance(s) */ + rc = lyxp_eval(LYD_CTX(node), target_path ? target_path : lref->path, node->schema->module, + LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, node, node, tree, NULL, &set, LYXP_IGNORE_WHEN); + if (rc) { + if (ly_errcode(LYD_CTX(node)) == rc) { + xp_err_msg = ly_errmsg(LYD_CTX(node)); + } else { + xp_err_msg = NULL; + } + + if (xp_err_msg) { + r = asprintf(errmsg, "Invalid leafref value \"%s\" - XPath evaluation error (%s).", val_str, xp_err_msg); + } else { + r = asprintf(errmsg, "Invalid leafref value \"%s\" - XPath evaluation error.", val_str); + } + if (r == -1) { + *errmsg = NULL; + rc = LY_EMEM; + } + goto cleanup; + } + + /* check the result */ + if (target_path) { + /* no or exact match(es) */ + i = 0; + } else { + /* check whether any matches */ + for (i = 0; i < set.used; ++i) { + if (set.val.nodes[i].type != LYXP_NODE_ELEM) { + continue; + } + + if (!lref->plugin->compare(&((struct lyd_node_term *)set.val.nodes[i].node)->value, value)) { + break; + } + } + } + + if (i == set.used) { + /* no match found */ + rc = LY_ENOTFOUND; + if (asprintf(errmsg, LY_ERRMSG_NOLREF_VAL, val_str, lref->path->expr) == -1) { + *errmsg = NULL; + rc = LY_EMEM; + } + goto cleanup; + } + if (target) { + *target = set.val.nodes[i].node; + } + +cleanup: + lyxp_expr_free(LYD_CTX(node), target_path); + lyxp_set_free_content(&set); + return rc; +} |