From 5a4b309e6ffe5f9af8073c94dfeb95c88bd8483c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 23 May 2020 17:50:24 +0200 Subject: [PATCH] Bug hunting: Add 'buffer overflow' check. Detect CVE-2019-19334 --- lib/exprengine.cpp | 87 +- lib/library.cpp | 11 + lib/library.h | 1 + test/bug-hunting/cve/CVE-2019-19334/cmd.txt | 1 + .../cve/CVE-2019-19334/expected.txt | 3 + test/bug-hunting/cve/CVE-2019-19334/parser.c | 3941 +++++++++++++++++ 6 files changed, 4039 insertions(+), 5 deletions(-) create mode 100644 test/bug-hunting/cve/CVE-2019-19334/cmd.txt create mode 100644 test/bug-hunting/cve/CVE-2019-19334/expected.txt create mode 100644 test/bug-hunting/cve/CVE-2019-19334/parser.c diff --git a/lib/exprengine.cpp b/lib/exprengine.cpp index cc102f8f1..47f838bac 100644 --- a/lib/exprengine.cpp +++ b/lib/exprengine.cpp @@ -1854,14 +1854,30 @@ static void execute(const Token *start, const Token *end, Data &data, int recurs const Token *thenStart = tok->linkAt(1)->next(); const Token *thenEnd = thenStart->link(); - execute(thenStart->next(), end, thenData, recursion); + const Token *exceptionToken = nullptr; + std::string exceptionMessage; + auto exec = [&](const Token *tok1, const Token *tok2, Data& data) { + try { + execute(tok1, tok2, data, recursion); + } catch (VerifyException &e) { + if (!exceptionToken || (e.tok && precedes(e.tok, exceptionToken))) { + exceptionToken = e.tok; + exceptionMessage = e.what; + } + } + }; + + exec(thenStart->next(), end, thenData); if (Token::simpleMatch(thenEnd, "} else {")) { const Token *elseStart = thenEnd->tokAt(2); - execute(elseStart->next(), end, elseData, recursion); + exec(elseStart->next(), end, elseData); } else { - execute(thenEnd, end, elseData, recursion); + exec(thenEnd, end, elseData); } + + if (exceptionToken) + throw VerifyException(exceptionToken, exceptionMessage); return; } @@ -1871,6 +1887,18 @@ static void execute(const Token *start, const Token *end, Data &data, int recurs const Token *bodyEnd = bodyStart->link(); const Token *defaultStart = nullptr; Data defaultData(data); + const Token *exceptionToken = nullptr; + std::string exceptionMessage; + auto exec = [&](const Token *tok1, const Token *tok2, Data& data) { + try { + execute(tok1, tok2, data, recursion); + } catch (VerifyException &e) { + if (!exceptionToken || (e.tok && precedes(e.tok, exceptionToken))) { + exceptionToken = e.tok; + exceptionMessage = e.what; + } + } + }; for (const Token *tok2 = bodyStart->next(); tok2 != bodyEnd; tok2 = tok2->next()) { if (tok2->str() == "{") tok2 = tok2->link(); @@ -1880,11 +1908,16 @@ static void execute(const Token *start, const Token *end, Data &data, int recurs Data caseData(data); caseData.addConstraint(condValue, caseValue, true); defaultData.addConstraint(condValue, caseValue, false); - execute(tok2->tokAt(2), end, caseData); + exec(tok2->tokAt(2), end, caseData); + } else if (Token::Match(tok2, "case %name% :") && !Token::Match(tok2->tokAt(3), ";| case")) { + Data caseData(data); + exec(tok2->tokAt(2), end, caseData); } else if (Token::simpleMatch(tok2, "default :")) defaultStart = tok2; } - execute(defaultStart ? defaultStart : bodyEnd, end, defaultData); + exec(defaultStart ? defaultStart : bodyEnd, end, defaultData); + if (exceptionToken) + throw VerifyException(exceptionToken, exceptionMessage); return; } @@ -2145,6 +2178,49 @@ static float getKnownFloatValue(const Token *tok, float def) void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings) { + std::function bufferOverflow = [=](const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { + if (!Token::simpleMatch(tok->astParent(), ",")) + return; + + if (!tok->valueType() || tok->valueType()->pointer != 1 || tok->valueType()->type != ::ValueType::Type::CHAR) + return; + + int argnr = (tok == tok->astParent()->astOperand1()) ? 0 : 1; + const Token *ftok = tok->astParent(); + while (Token::simpleMatch(ftok, ",")) { + ++argnr; + ftok = ftok->astParent(); + } + ftok = ftok ? ftok->previous() : nullptr; + if (!Token::Match(ftok, "%name% (")) + return; + + int overflowArgument = 0; + + if (const Library::Function *func = settings->library.getFunction(ftok)) { + for (auto argNrChecks: func->argumentChecks) { + int nr = argNrChecks.first; + const Library::ArgumentChecks &checks = argNrChecks.second; + for (const Library::ArgumentChecks::MinSize &minsize: checks.minsizes) { + if (minsize.type == Library::ArgumentChecks::MinSize::STRLEN && minsize.arg == argnr) + overflowArgument = nr; + } + } + } + + if (!overflowArgument) + return; + + dataBase->addError(tok->linenr()); + std::list callstack{tok}; + ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::error, "bughuntingBufferOverflow", "Buffer read/write, when calling '" + ftok->str() + "' it cannot be determined that " + std::to_string(overflowArgument) + getOrdinalText(overflowArgument) + " argument is not overflowed", CWE(120), false); + if (value.type == ExprEngine::ValueType::BailoutValue) + errmsg.incomplete = true; + else + errmsg.function = dataBase->currentFunction; + errorLogger->reportErr(errmsg); + }; + std::function divByZero = [=](const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (!tok->astParent() || !std::strchr("/%", tok->astParent()->str()[0])) return; @@ -2435,6 +2511,7 @@ void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer, }; std::vector callbacks; + callbacks.push_back(bufferOverflow); callbacks.push_back(divByZero); callbacks.push_back(checkFunctionCall); callbacks.push_back(checkAssignment); diff --git a/lib/library.cpp b/lib/library.cpp index cbcd17b19..283963f0a 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -1308,6 +1308,17 @@ std::vector Library::unknownReturnValues(const Token *ftok) con return (it == mUnknownReturnValues.end()) ? std::vector() : it->second; } +const Library::Function *Library::getFunction(const Token *ftok) const +{ + if (isNotLibraryFunction(ftok)) + return nullptr; + const std::map::const_iterator it1 = functions.find(getFunctionName(ftok)); + if (it1 == functions.cend()) + return nullptr; + return &it1->second; +} + + bool Library::hasminsize(const Token *ftok) const { if (isNotLibraryFunction(ftok)) diff --git a/lib/library.h b/lib/library.h index 842e509cf..bf5ade214 100644 --- a/lib/library.h +++ b/lib/library.h @@ -311,6 +311,7 @@ public: Function() : use(false), leakignore(false), isconst(false), ispure(false), useretval(false), ignore(false), formatstr(false), formatstr_scan(false), formatstr_secure(false) {} }; + const Function *getFunction(const Token *ftok) const; std::map functions; bool isUse(const std::string& functionName) const; bool isLeakIgnore(const std::string& functionName) const; diff --git a/test/bug-hunting/cve/CVE-2019-19334/cmd.txt b/test/bug-hunting/cve/CVE-2019-19334/cmd.txt new file mode 100644 index 000000000..e993abff1 --- /dev/null +++ b/test/bug-hunting/cve/CVE-2019-19334/cmd.txt @@ -0,0 +1 @@ +-DLY_CHECK_ERR_RETURN(A,B,C)= diff --git a/test/bug-hunting/cve/CVE-2019-19334/expected.txt b/test/bug-hunting/cve/CVE-2019-19334/expected.txt new file mode 100644 index 000000000..8343bfd91 --- /dev/null +++ b/test/bug-hunting/cve/CVE-2019-19334/expected.txt @@ -0,0 +1,3 @@ +parser.c:1024:bughuntingBufferOverflow +parser.c:1026:bughuntingBufferOverflow + diff --git a/test/bug-hunting/cve/CVE-2019-19334/parser.c b/test/bug-hunting/cve/CVE-2019-19334/parser.c new file mode 100644 index 000000000..e5d7ebbd6 --- /dev/null +++ b/test/bug-hunting/cve/CVE-2019-19334/parser.c @@ -0,0 +1,3941 @@ +/** + * @file parser.c + * @author Radek Krejci + * @brief common libyang parsers routines implementations + * + * Copyright (c) 2015-2017 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "context.h" +#include "libyang.h" +#include "parser.h" +#include "resolve.h" +#include "tree_internal.h" +#include "parser_yang.h" +#include "xpath.h" + +#define LYP_URANGE_LEN 19 + +static char *lyp_ublock2urange[][2] = { + {"BasicLatin", "[\\x{0000}-\\x{007F}]"}, + {"Latin-1Supplement", "[\\x{0080}-\\x{00FF}]"}, + {"LatinExtended-A", "[\\x{0100}-\\x{017F}]"}, + {"LatinExtended-B", "[\\x{0180}-\\x{024F}]"}, + {"IPAExtensions", "[\\x{0250}-\\x{02AF}]"}, + {"SpacingModifierLetters", "[\\x{02B0}-\\x{02FF}]"}, + {"CombiningDiacriticalMarks", "[\\x{0300}-\\x{036F}]"}, + {"Greek", "[\\x{0370}-\\x{03FF}]"}, + {"Cyrillic", "[\\x{0400}-\\x{04FF}]"}, + {"Armenian", "[\\x{0530}-\\x{058F}]"}, + {"Hebrew", "[\\x{0590}-\\x{05FF}]"}, + {"Arabic", "[\\x{0600}-\\x{06FF}]"}, + {"Syriac", "[\\x{0700}-\\x{074F}]"}, + {"Thaana", "[\\x{0780}-\\x{07BF}]"}, + {"Devanagari", "[\\x{0900}-\\x{097F}]"}, + {"Bengali", "[\\x{0980}-\\x{09FF}]"}, + {"Gurmukhi", "[\\x{0A00}-\\x{0A7F}]"}, + {"Gujarati", "[\\x{0A80}-\\x{0AFF}]"}, + {"Oriya", "[\\x{0B00}-\\x{0B7F}]"}, + {"Tamil", "[\\x{0B80}-\\x{0BFF}]"}, + {"Telugu", "[\\x{0C00}-\\x{0C7F}]"}, + {"Kannada", "[\\x{0C80}-\\x{0CFF}]"}, + {"Malayalam", "[\\x{0D00}-\\x{0D7F}]"}, + {"Sinhala", "[\\x{0D80}-\\x{0DFF}]"}, + {"Thai", "[\\x{0E00}-\\x{0E7F}]"}, + {"Lao", "[\\x{0E80}-\\x{0EFF}]"}, + {"Tibetan", "[\\x{0F00}-\\x{0FFF}]"}, + {"Myanmar", "[\\x{1000}-\\x{109F}]"}, + {"Georgian", "[\\x{10A0}-\\x{10FF}]"}, + {"HangulJamo", "[\\x{1100}-\\x{11FF}]"}, + {"Ethiopic", "[\\x{1200}-\\x{137F}]"}, + {"Cherokee", "[\\x{13A0}-\\x{13FF}]"}, + {"UnifiedCanadianAboriginalSyllabics", "[\\x{1400}-\\x{167F}]"}, + {"Ogham", "[\\x{1680}-\\x{169F}]"}, + {"Runic", "[\\x{16A0}-\\x{16FF}]"}, + {"Khmer", "[\\x{1780}-\\x{17FF}]"}, + {"Mongolian", "[\\x{1800}-\\x{18AF}]"}, + {"LatinExtendedAdditional", "[\\x{1E00}-\\x{1EFF}]"}, + {"GreekExtended", "[\\x{1F00}-\\x{1FFF}]"}, + {"GeneralPunctuation", "[\\x{2000}-\\x{206F}]"}, + {"SuperscriptsandSubscripts", "[\\x{2070}-\\x{209F}]"}, + {"CurrencySymbols", "[\\x{20A0}-\\x{20CF}]"}, + {"CombiningMarksforSymbols", "[\\x{20D0}-\\x{20FF}]"}, + {"LetterlikeSymbols", "[\\x{2100}-\\x{214F}]"}, + {"NumberForms", "[\\x{2150}-\\x{218F}]"}, + {"Arrows", "[\\x{2190}-\\x{21FF}]"}, + {"MathematicalOperators", "[\\x{2200}-\\x{22FF}]"}, + {"MiscellaneousTechnical", "[\\x{2300}-\\x{23FF}]"}, + {"ControlPictures", "[\\x{2400}-\\x{243F}]"}, + {"OpticalCharacterRecognition", "[\\x{2440}-\\x{245F}]"}, + {"EnclosedAlphanumerics", "[\\x{2460}-\\x{24FF}]"}, + {"BoxDrawing", "[\\x{2500}-\\x{257F}]"}, + {"BlockElements", "[\\x{2580}-\\x{259F}]"}, + {"GeometricShapes", "[\\x{25A0}-\\x{25FF}]"}, + {"MiscellaneousSymbols", "[\\x{2600}-\\x{26FF}]"}, + {"Dingbats", "[\\x{2700}-\\x{27BF}]"}, + {"BraillePatterns", "[\\x{2800}-\\x{28FF}]"}, + {"CJKRadicalsSupplement", "[\\x{2E80}-\\x{2EFF}]"}, + {"KangxiRadicals", "[\\x{2F00}-\\x{2FDF}]"}, + {"IdeographicDescriptionCharacters", "[\\x{2FF0}-\\x{2FFF}]"}, + {"CJKSymbolsandPunctuation", "[\\x{3000}-\\x{303F}]"}, + {"Hiragana", "[\\x{3040}-\\x{309F}]"}, + {"Katakana", "[\\x{30A0}-\\x{30FF}]"}, + {"Bopomofo", "[\\x{3100}-\\x{312F}]"}, + {"HangulCompatibilityJamo", "[\\x{3130}-\\x{318F}]"}, + {"Kanbun", "[\\x{3190}-\\x{319F}]"}, + {"BopomofoExtended", "[\\x{31A0}-\\x{31BF}]"}, + {"EnclosedCJKLettersandMonths", "[\\x{3200}-\\x{32FF}]"}, + {"CJKCompatibility", "[\\x{3300}-\\x{33FF}]"}, + {"CJKUnifiedIdeographsExtensionA", "[\\x{3400}-\\x{4DB5}]"}, + {"CJKUnifiedIdeographs", "[\\x{4E00}-\\x{9FFF}]"}, + {"YiSyllables", "[\\x{A000}-\\x{A48F}]"}, + {"YiRadicals", "[\\x{A490}-\\x{A4CF}]"}, + {"HangulSyllables", "[\\x{AC00}-\\x{D7A3}]"}, + {"PrivateUse", "[\\x{E000}-\\x{F8FF}]"}, + {"CJKCompatibilityIdeographs", "[\\x{F900}-\\x{FAFF}]"}, + {"AlphabeticPresentationForms", "[\\x{FB00}-\\x{FB4F}]"}, + {"ArabicPresentationForms-A", "[\\x{FB50}-\\x{FDFF}]"}, + {"CombiningHalfMarks", "[\\x{FE20}-\\x{FE2F}]"}, + {"CJKCompatibilityForms", "[\\x{FE30}-\\x{FE4F}]"}, + {"SmallFormVariants", "[\\x{FE50}-\\x{FE6F}]"}, + {"ArabicPresentationForms-B", "[\\x{FE70}-\\x{FEFE}]"}, + {"HalfwidthandFullwidthForms", "[\\x{FF00}-\\x{FFEF}]"}, + {NULL, NULL} +}; + +const char *ly_stmt_str[] = { + [LY_STMT_UNKNOWN] = "", + [LY_STMT_ARGUMENT] = "argument", + [LY_STMT_BASE] = "base", + [LY_STMT_BELONGSTO] = "belongs-to", + [LY_STMT_CONTACT] = "contact", + [LY_STMT_DEFAULT] = "default", + [LY_STMT_DESCRIPTION] = "description", + [LY_STMT_ERRTAG] = "error-app-tag", + [LY_STMT_ERRMSG] = "error-message", + [LY_STMT_KEY] = "key", + [LY_STMT_NAMESPACE] = "namespace", + [LY_STMT_ORGANIZATION] = "organization", + [LY_STMT_PATH] = "path", + [LY_STMT_PREFIX] = "prefix", + [LY_STMT_PRESENCE] = "presence", + [LY_STMT_REFERENCE] = "reference", + [LY_STMT_REVISIONDATE] = "revision-date", + [LY_STMT_UNITS] = "units", + [LY_STMT_VALUE] = "value", + [LY_STMT_VERSION] = "yang-version", + [LY_STMT_MODIFIER] = "modifier", + [LY_STMT_REQINSTANCE] = "require-instance", + [LY_STMT_YINELEM] = "yin-element", + [LY_STMT_CONFIG] = "config", + [LY_STMT_MANDATORY] = "mandatory", + [LY_STMT_ORDEREDBY] = "ordered-by", + [LY_STMT_STATUS] = "status", + [LY_STMT_DIGITS] = "fraction-digits", + [LY_STMT_MAX] = "max-elements", + [LY_STMT_MIN] = "min-elements", + [LY_STMT_POSITION] = "position", + [LY_STMT_UNIQUE] = "unique", + [LY_STMT_MODULE] = "module", + [LY_STMT_SUBMODULE] = "submodule", + [LY_STMT_ACTION] = "action", + [LY_STMT_ANYDATA] = "anydata", + [LY_STMT_ANYXML] = "anyxml", + [LY_STMT_CASE] = "case", + [LY_STMT_CHOICE] = "choice", + [LY_STMT_CONTAINER] = "container", + [LY_STMT_GROUPING] = "grouping", + [LY_STMT_INPUT] = "input", + [LY_STMT_LEAF] = "leaf", + [LY_STMT_LEAFLIST] = "leaf-list", + [LY_STMT_LIST] = "list", + [LY_STMT_NOTIFICATION] = "notification", + [LY_STMT_OUTPUT] = "output", + [LY_STMT_RPC] = "rpc", + [LY_STMT_USES] = "uses", + [LY_STMT_TYPEDEF] = "typedef", + [LY_STMT_TYPE] = "type", + [LY_STMT_BIT] = "bit", + [LY_STMT_ENUM] = "enum", + [LY_STMT_REFINE] = "refine", + [LY_STMT_AUGMENT] = "augment", + [LY_STMT_DEVIATE] = "deviate", + [LY_STMT_DEVIATION] = "deviation", + [LY_STMT_EXTENSION] = "extension", + [LY_STMT_FEATURE] = "feature", + [LY_STMT_IDENTITY] = "identity", + [LY_STMT_IFFEATURE] = "if-feature", + [LY_STMT_IMPORT] = "import", + [LY_STMT_INCLUDE] = "include", + [LY_STMT_LENGTH] = "length", + [LY_STMT_MUST] = "must", + [LY_STMT_PATTERN] = "pattern", + [LY_STMT_RANGE] = "range", + [LY_STMT_WHEN] = "when", + [LY_STMT_REVISION] = "revision" +}; + +int +lyp_is_rpc_action(struct lys_node *node) +{ + assert(node); + + while (lys_parent(node)) { + node = lys_parent(node); + if (node->nodetype == LYS_ACTION) { + break; + } + } + + if (node->nodetype & (LYS_RPC | LYS_ACTION)) { + return 1; + } else { + return 0; + } +} + +int +lyp_data_check_options(struct ly_ctx *ctx, int options, const char *func) +{ + int x = options & LYD_OPT_TYPEMASK; + + /* LYD_OPT_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG */ + if (options & LYD_OPT_WHENAUTODEL) { + if ((x == LYD_OPT_EDIT) || (x == LYD_OPT_NOTIF_FILTER)) { + LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG)", + func, options); + return 1; + } + } + + if (options & (LYD_OPT_DATA_ADD_YANGLIB | LYD_OPT_DATA_NO_YANGLIB)) { + if (x != LYD_OPT_DATA) { + LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_*_YANGLIB can be used only with LYD_OPT_DATA)", + func, options); + return 1; + } + } + + /* "is power of 2" algorithm, with 0 exception */ + if (x && !(x && !(x & (x - 1)))) { + LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (multiple data type flags set).", func, options); + return 1; + } + + return 0; +} + +int +lyp_mmap(struct ly_ctx *ctx, int fd, size_t addsize, size_t *length, void **addr) +{ + struct stat sb; + long pagesize; + size_t m; + + assert(fd >= 0); + if (fstat(fd, &sb) == -1) { + LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno)); + return 1; + } + if (!S_ISREG(sb.st_mode)) { + LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file."); + return 1; + } + if (!sb.st_size) { + *addr = NULL; + return 0; + } + pagesize = sysconf(_SC_PAGESIZE); + ++addsize; /* at least one additional byte for terminating NULL byte */ + + m = sb.st_size % pagesize; + if (m && pagesize - m >= addsize) { + /* there will be enough space after the file content mapping to provide zeroed additional bytes */ + *length = sb.st_size + addsize; + *addr = mmap(NULL, *length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + } else { + /* there will not be enough bytes after the file content mapping for the additional bytes and some of them + * would overflow into another page that would not be zeroed and any access into it would generate SIGBUS. + * Therefore we have to do the following hack with double mapping. First, the required number of bytes + * (including the additional bytes) is required as anonymous and thus they will be really provided (actually more + * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address + * where the anonymous mapping starts. */ + *length = sb.st_size + pagesize; + *addr = mmap(NULL, *length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + *addr = mmap(*addr, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd, 0); + } + if (*addr == MAP_FAILED) { + LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno)); + return 1; + } + + return 0; +} + +int +lyp_munmap(void *addr, size_t length) +{ + return munmap(addr, length); +} + +int +lyp_add_ietf_netconf_annotations_config(struct lys_module *mod) +{ + void *reallocated; + struct lys_ext_instance_complex *op; + struct lys_type **type; + struct lys_node_anydata *anyxml; + int i; + struct ly_ctx *ctx = mod->ctx; /* shortcut */ + + reallocated = realloc(mod->ext, (mod->ext_size + 3) * sizeof *mod->ext); + LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx), EXIT_FAILURE); + mod->ext = reallocated; + /* 1) edit-config's operation */ + op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); + LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); + mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; + op->arg_value = lydict_insert(ctx, "operation", 9); + op->def = &ctx->models.list[0]->extensions[0]; + op->ext_type = LYEXT_COMPLEX; + op->module = op->parent = mod; + op->parent_type = LYEXT_PAR_MODULE; + op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; + op->nodetype = LYS_EXT; + type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ + *type = calloc(1, sizeof(struct lys_type)); + LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); + (*type)->base = LY_TYPE_ENUM; + (*type)->der = ly_types[LY_TYPE_ENUM]; + (*type)->parent = (struct lys_tpdf *)op; + (*type)->info.enums.count = 5; + (*type)->info.enums.enm = calloc(5, sizeof *(*type)->info.enums.enm); + LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm, LOGMEM(ctx), EXIT_FAILURE); + (*type)->info.enums.enm[0].value = 0; + (*type)->info.enums.enm[0].name = lydict_insert(ctx, "merge", 5); + (*type)->info.enums.enm[1].value = 1; + (*type)->info.enums.enm[1].name = lydict_insert(ctx, "replace", 7); + (*type)->info.enums.enm[2].value = 2; + (*type)->info.enums.enm[2].name = lydict_insert(ctx, "create", 6); + (*type)->info.enums.enm[3].value = 3; + (*type)->info.enums.enm[3].name = lydict_insert(ctx, "delete", 6); + (*type)->info.enums.enm[4].value = 4; + (*type)->info.enums.enm[4].name = lydict_insert(ctx, "remove", 6); + mod->ext_size++; + + /* 2) filter's type */ + op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); + LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); + mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; + op->arg_value = lydict_insert(ctx, "type", 4); + op->def = &ctx->models.list[0]->extensions[0]; + op->ext_type = LYEXT_COMPLEX; + op->module = op->parent = mod; + op->parent_type = LYEXT_PAR_MODULE; + op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; + op->nodetype = LYS_EXT; + type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ + *type = calloc(1, sizeof(struct lys_type)); + LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); + (*type)->base = LY_TYPE_ENUM; + (*type)->der = ly_types[LY_TYPE_ENUM]; + (*type)->parent = (struct lys_tpdf *)op; + (*type)->info.enums.count = 2; + (*type)->info.enums.enm = calloc(2, sizeof *(*type)->info.enums.enm); + LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm, LOGMEM(ctx), EXIT_FAILURE); + (*type)->info.enums.enm[0].value = 0; + (*type)->info.enums.enm[0].name = lydict_insert(ctx, "subtree", 7); + (*type)->info.enums.enm[1].value = 1; + (*type)->info.enums.enm[1].name = lydict_insert(ctx, "xpath", 5); + for (i = mod->features_size; i > 0; i--) { + if (!strcmp(mod->features[i - 1].name, "xpath")) { + (*type)->info.enums.enm[1].iffeature_size = 1; + (*type)->info.enums.enm[1].iffeature = calloc(1, sizeof(struct lys_feature)); + LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature, LOGMEM(ctx), EXIT_FAILURE); + (*type)->info.enums.enm[1].iffeature[0].expr = malloc(sizeof(uint8_t)); + LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature[0].expr, LOGMEM(ctx), EXIT_FAILURE); + *(*type)->info.enums.enm[1].iffeature[0].expr = 3; /* LYS_IFF_F */ + (*type)->info.enums.enm[1].iffeature[0].features = malloc(sizeof(struct lys_feature*)); + LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature[0].features, LOGMEM(ctx), EXIT_FAILURE); + (*type)->info.enums.enm[1].iffeature[0].features[0] = &mod->features[i - 1]; + break; + } + } + mod->ext_size++; + + /* 3) filter's select */ + op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); + LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); + mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; + op->arg_value = lydict_insert(ctx, "select", 6); + op->def = &ctx->models.list[0]->extensions[0]; + op->ext_type = LYEXT_COMPLEX; + op->module = op->parent = mod; + op->parent_type = LYEXT_PAR_MODULE; + op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; + op->nodetype = LYS_EXT; + type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ + *type = calloc(1, sizeof(struct lys_type)); + LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); + (*type)->base = LY_TYPE_STRING; + (*type)->der = ly_types[LY_TYPE_STRING]; + (*type)->parent = (struct lys_tpdf *)op; + mod->ext_size++; + + /* 4) URL config */ + anyxml = calloc(1, sizeof *anyxml); + LY_CHECK_ERR_RETURN(!anyxml, LOGMEM(ctx), EXIT_FAILURE); + anyxml->nodetype = LYS_ANYXML; + anyxml->prev = (struct lys_node *)anyxml; + anyxml->name = lydict_insert(ctx, "config", 0); + anyxml->module = mod; + anyxml->flags = LYS_CONFIG_W; + if (lys_node_addchild(NULL, mod, (struct lys_node *)anyxml, 0)) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/* logs directly + * base: 0 - to accept decimal, octal, hexadecimal (in default value) + * 10 - to accept only decimal (instance value) + */ +static int +parse_int(const char *val_str, int64_t min, int64_t max, int base, int64_t *ret, struct lyd_node *node) +{ + char *strptr; + + assert(node); + + if (!val_str || !val_str[0]) { + goto error; + } + + /* convert to 64-bit integer, all the redundant characters are handled */ + errno = 0; + strptr = NULL; + + /* parse the value */ + *ret = strtoll(val_str, &strptr, base); + if (errno || (*ret < min) || (*ret > max)) { + goto error; + } else if (strptr && *strptr) { + while (isspace(*strptr)) { + ++strptr; + } + if (*strptr) { + goto error; + } + } + + return EXIT_SUCCESS; + +error: + LOGVAL(node->schema->module->ctx, LYE_INVAL, LY_VLOG_LYD, node, val_str ? val_str : "", node->schema->name); + return EXIT_FAILURE; +} + +/* logs directly + * base: 0 - to accept decimal, octal, hexadecimal (in default value) + * 10 - to accept only decimal (instance value) + */ +static int +parse_uint(const char *val_str, uint64_t max, int base, uint64_t *ret, struct lyd_node *node) +{ + char *strptr; + uint64_t u; + + assert(node); + + if (!val_str || !val_str[0]) { + goto error; + } + + errno = 0; + strptr = NULL; + u = strtoull(val_str, &strptr, base); + if (errno || (u > max)) { + goto error; + } else if (strptr && *strptr) { + while (isspace(*strptr)) { + ++strptr; + } + if (*strptr) { + goto error; + } + } else if (u != 0 && val_str[0] == '-') { + goto error; + } + + *ret = u; + return EXIT_SUCCESS; + +error: + LOGVAL(node->schema->module->ctx, LYE_INVAL, LY_VLOG_LYD, node, val_str ? val_str : "", node->schema->name); + return EXIT_FAILURE; +} + +/* logs directly + * + * kind == 0 - unsigned (unum used), 1 - signed (snum used), 2 - floating point (fnum used) + */ +static int +validate_length_range(uint8_t kind, uint64_t unum, int64_t snum, int64_t fnum, uint8_t fnum_dig, struct lys_type *type, + const char *val_str, struct lyd_node *node) +{ + struct lys_restr *restr = NULL; + struct len_ran_intv *intv = NULL, *tmp_intv; + struct lys_type *cur_type; + struct ly_ctx *ctx = type->parent->module->ctx; + int match; + + if (resolve_len_ran_interval(ctx, NULL, type, &intv)) { + /* already done during schema parsing */ + LOGINT(ctx); + return EXIT_FAILURE; + } + if (!intv) { + return EXIT_SUCCESS; + } + + /* I know that all intervals belonging to a single restriction share one type pointer */ + tmp_intv = intv; + cur_type = intv->type; + do { + match = 0; + for (; tmp_intv && (tmp_intv->type == cur_type); tmp_intv = tmp_intv->next) { + if (match) { + /* just iterate through the rest of this restriction intervals */ + continue; + } + + if (((kind == 0) && (unum < tmp_intv->value.uval.min)) + || ((kind == 1) && (snum < tmp_intv->value.sval.min)) + || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) < 0))) { + break; + } + + if (((kind == 0) && (unum >= tmp_intv->value.uval.min) && (unum <= tmp_intv->value.uval.max)) + || ((kind == 1) && (snum >= tmp_intv->value.sval.min) && (snum <= tmp_intv->value.sval.max)) + || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) > -1) + && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.max, cur_type->info.dec64.dig) < 1))) { + match = 1; + } + } + + if (!match) { + break; + } else if (tmp_intv) { + cur_type = tmp_intv->type; + } + } while (tmp_intv); + + while (intv) { + tmp_intv = intv->next; + free(intv); + intv = tmp_intv; + } + + if (!match) { + switch (cur_type->base) { + case LY_TYPE_BINARY: + restr = cur_type->info.binary.length; + break; + case LY_TYPE_DEC64: + restr = cur_type->info.dec64.range; + break; + case LY_TYPE_INT8: + case LY_TYPE_INT16: + case LY_TYPE_INT32: + case LY_TYPE_INT64: + case LY_TYPE_UINT8: + case LY_TYPE_UINT16: + case LY_TYPE_UINT32: + case LY_TYPE_UINT64: + restr = cur_type->info.num.range; + break; + case LY_TYPE_STRING: + restr = cur_type->info.str.length; + break; + default: + LOGINT(ctx); + return EXIT_FAILURE; + } + + LOGVAL(ctx, LYE_NOCONSTR, LY_VLOG_LYD, node, (val_str ? val_str : ""), restr ? restr->expr : ""); + if (restr && restr->emsg) { + ly_vlog_str(ctx, LY_VLOG_PREV, restr->emsg); + } + if (restr && restr->eapptag) { + ly_err_last_set_apptag(ctx, restr->eapptag); + } + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +/* logs directly */ +static int +validate_pattern(struct ly_ctx *ctx, const char *val_str, struct lys_type *type, struct lyd_node *node) +{ + int rc; + unsigned int i; +#ifndef LY_ENABLED_CACHE + pcre *precomp; +#endif + + assert(ctx && (type->base == LY_TYPE_STRING)); + + if (!val_str) { + val_str = ""; + } + + if (type->der && validate_pattern(ctx, val_str, &type->der->type, node)) { + return EXIT_FAILURE; + } + +#ifdef LY_ENABLED_CACHE + /* there is no cache, build it */ + if (!type->info.str.patterns_pcre && type->info.str.pat_count) { + type->info.str.patterns_pcre = malloc(2 * type->info.str.pat_count * sizeof *type->info.str.patterns_pcre); + LY_CHECK_ERR_RETURN(!type->info.str.patterns_pcre, LOGMEM(ctx), -1); + + for (i = 0; i < type->info.str.pat_count; ++i) { + if (lyp_precompile_pattern(ctx, &type->info.str.patterns[i].expr[1], + (pcre**)&type->info.str.patterns_pcre[i * 2], + (pcre_extra**)&type->info.str.patterns_pcre[i * 2 + 1])) { + return EXIT_FAILURE; + } + } + } +#endif + + for (i = 0; i < type->info.str.pat_count; ++i) { +#ifdef LY_ENABLED_CACHE + rc = pcre_exec((pcre *)type->info.str.patterns_pcre[2 * i], (pcre_extra *)type->info.str.patterns_pcre[2 * i + 1], + val_str, strlen(val_str), 0, 0, NULL, 0); +#else + if (lyp_check_pattern(ctx, &type->info.str.patterns[i].expr[1], &precomp)) { + return EXIT_FAILURE; + } + rc = pcre_exec(precomp, NULL, val_str, strlen(val_str), 0, 0, NULL, 0); + free(precomp); +#endif + if ((rc && type->info.str.patterns[i].expr[0] == 0x06) || (!rc && type->info.str.patterns[i].expr[0] == 0x15)) { + LOGVAL(ctx, LYE_NOCONSTR, LY_VLOG_LYD, node, val_str, &type->info.str.patterns[i].expr[1]); + if (type->info.str.patterns[i].emsg) { + ly_vlog_str(ctx, LY_VLOG_PREV, type->info.str.patterns[i].emsg); + } + if (type->info.str.patterns[i].eapptag) { + ly_err_last_set_apptag(ctx, type->info.str.patterns[i].eapptag); + } + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +static void +check_number(const char *str_num, const char **num_end, LY_DATA_TYPE base) +{ + if (!isdigit(str_num[0]) && (str_num[0] != '-') && (str_num[0] != '+')) { + *num_end = str_num; + return; + } + + if ((str_num[0] == '-') || (str_num[0] == '+')) { + ++str_num; + } + + while (isdigit(str_num[0])) { + ++str_num; + } + + if ((base != LY_TYPE_DEC64) || (str_num[0] != '.') || !isdigit(str_num[1])) { + *num_end = str_num; + return; + } + + ++str_num; + while (isdigit(str_num[0])) { + ++str_num; + } + + *num_end = str_num; +} + +/** + * @brief Checks the syntax of length or range statement, + * on success checks the semantics as well. Does not log. + * + * @param[in] expr Length or range expression. + * @param[in] type Type with the restriction. + * + * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. + */ +int +lyp_check_length_range(struct ly_ctx *ctx, const char *expr, struct lys_type *type) +{ + struct len_ran_intv *intv = NULL, *tmp_intv; + const char *c = expr, *tail; + int ret = EXIT_FAILURE, flg = 1; /* first run flag */ + + assert(expr); + +lengthpart: + + while (isspace(*c)) { + c++; + } + + /* lower boundary or explicit number */ + if (!strncmp(c, "max", 3)) { +max: + c += 3; + while (isspace(*c)) { + c++; + } + if (*c != '\0') { + goto error; + } + + goto syntax_ok; + + } else if (!strncmp(c, "min", 3)) { + if (!flg) { + /* min cannot be used elsewhere than in the first length-part */ + goto error; + } else { + flg = 0; + } + c += 3; + while (isspace(*c)) { + c++; + } + + if (*c == '|') { + c++; + /* process next length-part */ + goto lengthpart; + } else if (*c == '\0') { + goto syntax_ok; + } else if (!strncmp(c, "..", 2)) { +upper: + c += 2; + while (isspace(*c)) { + c++; + } + if (*c == '\0') { + goto error; + } + + /* upper boundary */ + if (!strncmp(c, "max", 3)) { + goto max; + } + + check_number(c, &tail, type->base); + if (c == tail) { + goto error; + } + c = tail; + while (isspace(*c)) { + c++; + } + if (*c == '\0') { + goto syntax_ok; + } else if (*c == '|') { + c++; + /* process next length-part */ + goto lengthpart; + } else { + goto error; + } + } else { + goto error; + } + + } else if (isdigit(*c) || (*c == '-') || (*c == '+')) { + /* number */ + check_number(c, &tail, type->base); + if (c == tail) { + goto error; + } + c = tail; + + while (isspace(*c)) { + c++; + } + + if (*c == '|') { + c++; + /* process next length-part */ + goto lengthpart; + } else if (*c == '\0') { + goto syntax_ok; + } else if (!strncmp(c, "..", 2)) { + goto upper; + } + + } else { + goto error; + } + +syntax_ok: + if (resolve_len_ran_interval(ctx, expr, type, &intv)) { + goto error; + } + + ret = EXIT_SUCCESS; + +error: + while (intv) { + tmp_intv = intv->next; + free(intv); + intv = tmp_intv; + } + + return ret; +} + +/** + * @brief Checks pattern syntax. Logs directly. + * + * @param[in] pattern Pattern to check. + * @param[out] pcre_precomp Precompiled PCRE pattern. Can be NULL. + * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. + */ +int +lyp_check_pattern(struct ly_ctx *ctx, const char *pattern, pcre **pcre_precomp) +{ + int idx, idx2, start, end, err_offset, count; + char *perl_regex, *ptr; + const char *err_msg, *orig_ptr; + pcre *precomp; + + /* + * adjust the expression to a Perl equivalent + * + * http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs + */ + + /* we need to replace all "$" with "\$", count them now */ + for (count = 0, ptr = strchr(pattern, '$'); ptr; ++count, ptr = strchr(ptr + 1, '$')); + + perl_regex = malloc((strlen(pattern) + 4 + count) * sizeof(char)); + LY_CHECK_ERR_RETURN(!perl_regex, LOGMEM(ctx), EXIT_FAILURE); + perl_regex[0] = '\0'; + + ptr = perl_regex; + + if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) { + /* we wil add line-end anchoring */ + ptr[0] = '('; + ++ptr; + } + + for (orig_ptr = pattern; orig_ptr[0]; ++orig_ptr) { + if (orig_ptr[0] == '$') { + ptr += sprintf(ptr, "\\$"); + } else { + ptr[0] = orig_ptr[0]; + ++ptr; + } + } + + if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) { + ptr += sprintf(ptr, ")$"); + } else { + ptr[0] = '\0'; + ++ptr; + } + + /* substitute Unicode Character Blocks with exact Character Ranges */ + while ((ptr = strstr(perl_regex, "\\p{Is"))) { + start = ptr - perl_regex; + + ptr = strchr(ptr, '}'); + if (!ptr) { + LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 2, "unterminated character property"); + free(perl_regex); + return EXIT_FAILURE; + } + + end = (ptr - perl_regex) + 1; + + /* need more space */ + if (end - start < LYP_URANGE_LEN) { + perl_regex = ly_realloc(perl_regex, strlen(perl_regex) + (LYP_URANGE_LEN - (end - start)) + 1); + LY_CHECK_ERR_RETURN(!perl_regex, LOGMEM(ctx); free(perl_regex), EXIT_FAILURE); + } + + /* find our range */ + for (idx = 0; lyp_ublock2urange[idx][0]; ++idx) { + if (!strncmp(perl_regex + start + 5, lyp_ublock2urange[idx][0], strlen(lyp_ublock2urange[idx][0]))) { + break; + } + } + if (!lyp_ublock2urange[idx][0]) { + LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 5, "unknown block name"); + free(perl_regex); + return EXIT_FAILURE; + } + + /* make the space in the string and replace the block (but we cannot include brackets if it was already enclosed in them) */ + for (idx2 = 0, count = 0; idx2 < start; ++idx2) { + if ((perl_regex[idx2] == '[') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) { + ++count; + } + if ((perl_regex[idx2] == ']') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) { + --count; + } + } + if (count) { + /* skip brackets */ + memmove(perl_regex + start + (LYP_URANGE_LEN - 2), perl_regex + end, strlen(perl_regex + end) + 1); + memcpy(perl_regex + start, lyp_ublock2urange[idx][1] + 1, LYP_URANGE_LEN - 2); + } else { + memmove(perl_regex + start + LYP_URANGE_LEN, perl_regex + end, strlen(perl_regex + end) + 1); + memcpy(perl_regex + start, lyp_ublock2urange[idx][1], LYP_URANGE_LEN); + } + } + + /* must return 0, already checked during parsing */ + precomp = pcre_compile(perl_regex, PCRE_ANCHORED | PCRE_DOLLAR_ENDONLY | PCRE_NO_AUTO_CAPTURE, + &err_msg, &err_offset, NULL); + if (!precomp) { + LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + err_offset, err_msg); + free(perl_regex); + return EXIT_FAILURE; + } + free(perl_regex); + + if (pcre_precomp) { + *pcre_precomp = precomp; + } else { + free(precomp); + } + + return EXIT_SUCCESS; +} + +int +lyp_precompile_pattern(struct ly_ctx *ctx, const char *pattern, pcre** pcre_cmp, pcre_extra **pcre_std) +{ + const char *err_msg = NULL; + + if (lyp_check_pattern(ctx, pattern, pcre_cmp)) { + return EXIT_FAILURE; + } + + if (pcre_std && pcre_cmp) { + (*pcre_std) = pcre_study(*pcre_cmp, 0, &err_msg); + if (err_msg) { + LOGWRN(ctx, "Studying pattern \"%s\" failed (%s).", pattern, err_msg); + } + } + + return EXIT_SUCCESS; +} + +/** + * @brief Change the value into its canonical form. In libyang, additionally to the RFC, + * all identities have their module as a prefix in their canonical form. + * + * @param[in] ctx + * @param[in] type Type of the value. + * @param[in,out] value Original and then canonical value. + * @param[in] data1 If \p type is #LY_TYPE_BITS: (struct lys_type_bit **) type bit field, + * #LY_TYPE_DEC64: (int64_t *) parsed digits of the number itself without floating point, + * #LY_TYPE_IDENT: (const char *) local module name (identityref node module), + * #LY_TYPE_INT*: (int64_t *) parsed int number itself, + * #LY_TYPE_UINT*: (uint64_t *) parsed uint number itself, + * otherwise ignored. + * @param[in] data2 If \p type is #LY_TYPE_BITS: (int *) type bit field length, + * #LY_TYPE_DEC64: (uint8_t *) number of fraction digits (position of the floating point), + * otherwise ignored. + * @return 1 if a conversion took place, 0 if the value was kept the same, -1 on error. + */ +static int +make_canonical(struct ly_ctx *ctx, int type, const char **value, void *data1, void *data2) +{ + const uint16_t buf_len = 511; + char buf[buf_len + 1]; + struct lys_type_bit **bits = NULL; + struct lyxp_expr *exp; + const char *module_name, *cur_expr, *end; + int i, j, count; + int64_t num; + uint64_t unum; + uint8_t c; + +#define LOGBUF(str) LOGERR(ctx, LY_EINVAL, "Value \"%s\" is too long.", str) + + switch (type) { + case LY_TYPE_BITS: + bits = (struct lys_type_bit **)data1; + count = *((int *)data2); + /* in canonical form, the bits are ordered by their position */ + buf[0] = '\0'; + for (i = 0; i < count; i++) { + if (!bits[i]) { + /* bit not set */ + continue; + } + if (buf[0]) { + LY_CHECK_ERR_RETURN(strlen(buf) + 1 + strlen(bits[i]->name) > buf_len, LOGBUF(bits[i]->name), -1); + sprintf(buf + strlen(buf), " %s", bits[i]->name); + } else { + LY_CHECK_ERR_RETURN(strlen(bits[i]->name) > buf_len, LOGBUF(bits[i]->name), -1); + strcpy(buf, bits[i]->name); + } + } + break; + + case LY_TYPE_IDENT: + module_name = (const char *)data1; + /* identity must always have a prefix */ + if (!strchr(*value, ':')) { + sprintf(buf, "%s:%s", module_name, *value); + } else { + strcpy(buf, *value); + } + break; + + case LY_TYPE_INST: + exp = lyxp_parse_expr(ctx, *value); + LY_CHECK_ERR_RETURN(!exp, LOGINT(ctx), -1); + + module_name = NULL; + count = 0; + for (i = 0; (unsigned)i < exp->used; ++i) { + cur_expr = &exp->expr[exp->expr_pos[i]]; + + /* copy WS */ + if (i && ((end = exp->expr + exp->expr_pos[i - 1] + exp->tok_len[i - 1]) != cur_expr)) { + if (count + (cur_expr - end) > buf_len) { + lyxp_expr_free(exp); + LOGBUF(end); + return -1; + } + strncpy(&buf[count], end, cur_expr - end); + count += cur_expr - end; + } + + if ((exp->tokens[i] == LYXP_TOKEN_NAMETEST) && (end = strnchr(cur_expr, ':', exp->tok_len[i]))) { + /* get the module name with ":" */ + ++end; + j = end - cur_expr; + + if (!module_name || strncmp(cur_expr, module_name, j)) { + /* print module name with colon, it does not equal to the parent one */ + if (count + j > buf_len) { + lyxp_expr_free(exp); + LOGBUF(cur_expr); + return -1; + } + strncpy(&buf[count], cur_expr, j); + count += j; + } + module_name = cur_expr; + + /* copy the rest */ + if (count + (exp->tok_len[i] - j) > buf_len) { + lyxp_expr_free(exp); + LOGBUF(end); + return -1; + } + strncpy(&buf[count], end, exp->tok_len[i] - j); + count += exp->tok_len[i] - j; + } else { + if (count + exp->tok_len[i] > buf_len) { + lyxp_expr_free(exp); + LOGBUF(&exp->expr[exp->expr_pos[i]]); + return -1; + } + strncpy(&buf[count], &exp->expr[exp->expr_pos[i]], exp->tok_len[i]); + count += exp->tok_len[i]; + } + } + if (count > buf_len) { + LOGINT(ctx); + lyxp_expr_free(exp); + return -1; + } + buf[count] = '\0'; + + lyxp_expr_free(exp); + break; + + case LY_TYPE_DEC64: + num = *((int64_t *)data1); + c = *((uint8_t *)data2); + if (num) { + count = sprintf(buf, "%"PRId64" ", num); + if ( (num > 0 && (count - 1) <= c) + || (count - 2) <= c ) { + /* we have 0. value, print the value with the leading zeros + * (one for 0. and also keep the correct with of num according + * to fraction-digits value) + * for (num<0) - extra character for '-' sign */ + count = sprintf(buf, "%0*"PRId64" ", (num > 0) ? (c + 1) : (c + 2), num); + } + for (i = c, j = 1; i > 0 ; i--) { + if (j && i > 1 && buf[count - 2] == '0') { + /* we have trailing zero to skip */ + buf[count - 1] = '\0'; + } else { + j = 0; + buf[count - 1] = buf[count - 2]; + } + count--; + } + buf[count - 1] = '.'; + } else { + /* zero */ + sprintf(buf, "0.0"); + } + break; + + case LY_TYPE_INT8: + case LY_TYPE_INT16: + case LY_TYPE_INT32: + case LY_TYPE_INT64: + num = *((int64_t *)data1); + sprintf(buf, "%"PRId64, num); + break; + + case LY_TYPE_UINT8: + case LY_TYPE_UINT16: + case LY_TYPE_UINT32: + case LY_TYPE_UINT64: + unum = *((uint64_t *)data1); + sprintf(buf, "%"PRIu64, unum); + break; + + default: + /* should not be even called - just do nothing */ + return 0; + } + + if (strcmp(buf, *value)) { + lydict_remove(ctx, *value); + *value = lydict_insert(ctx, buf, 0); + return 1; + } + + return 0; + +#undef LOGBUF +} + +static const char * +ident_val_add_module_prefix(const char *value, const struct lyxml_elem *xml, struct ly_ctx *ctx) +{ + const struct lyxml_ns *ns; + const struct lys_module *mod; + char *str; + + do { + LY_TREE_FOR((struct lyxml_ns *)xml->attr, ns) { + if ((ns->type == LYXML_ATTR_NS) && !ns->prefix) { + /* match */ + break; + } + } + if (!ns) { + xml = xml->parent; + } + } while (!ns && xml); + + if (!ns) { + /* no default namespace */ + LOGINT(ctx); + return NULL; + } + + /* find module */ + mod = ly_ctx_get_module_by_ns(ctx, ns->value, NULL, 1); + if (!mod) { + LOGINT(ctx); + return NULL; + } + + if (asprintf(&str, "%s:%s", mod->name, value) == -1) { + LOGMEM(ctx); + return NULL; + } + lydict_remove(ctx, value); + + return lydict_insert_zc(ctx, str); +} + +/* + * xml - optional for converting instance-identifier and identityref into JSON format + * leaf - mandatory to know the context (necessary e.g. for prefixes in idenitytref values) + * attr - alternative to leaf in case of parsing value in annotations (attributes) + * local_mod - optional if the local module dos not match the module of leaf/attr + * store - flag for union resolution - we do not want to store the result, we are just learning the type + * dflt - whether the value is a default value from the schema + * trusted - whether the value is trusted to be valid (but may not be canonical, so it is canonized) + */ +struct lys_type * +lyp_parse_value(struct lys_type *type, const char **value_, struct lyxml_elem *xml, + struct lyd_node_leaf_list *leaf, struct lyd_attr *attr, struct lys_module *local_mod, + int store, int dflt, int trusted) +{ + struct lys_type *ret = NULL, *t; + struct lys_tpdf *tpdf; + enum int_log_opts prev_ilo; + int c, len, found = 0; + unsigned int i, j; + int64_t num; + uint64_t unum, uind, u = 0; + const char *ptr, *value = *value_, *itemname, *old_val_str = NULL; + struct lys_type_bit **bits = NULL; + struct lys_ident *ident; + lyd_val *val, old_val; + LY_DATA_TYPE *val_type, old_val_type; + uint8_t *val_flags, old_val_flags; + struct lyd_node *contextnode; + struct ly_ctx *ctx = type->parent->module->ctx; + + assert(leaf || attr); + + if (leaf) { + assert(!attr); + if (!local_mod) { + local_mod = leaf->schema->module; + } + val = &leaf->value; + val_type = &leaf->value_type; + val_flags = &leaf->value_flags; + contextnode = (struct lyd_node *)leaf; + itemname = leaf->schema->name; + } else { + assert(!leaf); + if (!local_mod) { + local_mod = attr->annotation->module; + } + val = &attr->value; + val_type = &attr->value_type; + val_flags = &attr->value_flags; + contextnode = attr->parent; + itemname = attr->name; + } + + /* fully clear the value */ + if (store) { + old_val_str = lydict_insert(ctx, *value_, 0); + lyd_free_value(*val, *val_type, *val_flags, type, old_val_str, &old_val, &old_val_type, &old_val_flags); + *val_flags &= ~LY_VALUE_UNRES; + } + + switch (type->base) { + case LY_TYPE_BINARY: + /* get number of octets for length validation */ + unum = 0; + ptr = NULL; + if (value) { + /* silently skip leading/trailing whitespaces */ + for (uind = 0; isspace(value[uind]); ++uind); + ptr = &value[uind]; + u = strlen(ptr); + while (u && isspace(ptr[u - 1])) { + --u; + } + unum = u; + for (uind = 0; uind < u; ++uind) { + if (ptr[uind] == '\n') { + unum--; + } else if ((ptr[uind] < '/' && ptr[uind] != '+') || + (ptr[uind] > '9' && ptr[uind] < 'A') || + (ptr[uind] > 'Z' && ptr[uind] < 'a') || ptr[uind] > 'z') { + if (ptr[uind] == '=') { + /* padding */ + if (uind == u - 2 && ptr[uind + 1] == '=') { + found = 2; + uind++; + } else if (uind == u - 1) { + found = 1; + } + } + if (!found) { + /* error */ + LOGVAL(ctx, LYE_INCHAR, LY_VLOG_LYD, contextnode, ptr[uind], &ptr[uind]); + LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Invalid Base64 character."); + goto error; + } + } + } + } + + if (unum & 3) { + /* base64 length must be multiple of 4 chars */ + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); + } + LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Base64 encoded value length must be divisible by 4."); + goto error; + } + + /* length of the encoded string */ + len = ((unum / 4) * 3) - found; + if (!trusted && validate_length_range(0, len, 0, 0, 0, type, value, contextnode)) { + goto error; + } + + if (value && (ptr != value || ptr[u] != '\0')) { + /* update the changed value */ + ptr = lydict_insert(ctx, ptr, u); + lydict_remove(ctx, *value_); + *value_ = ptr; + } + + if (store) { + /* store the result */ + val->binary = value; + *val_type = LY_TYPE_BINARY; + } + break; + + case LY_TYPE_BITS: + /* locate bits structure with the bits definitions + * since YANG 1.1 allows restricted bits, it is the first + * bits type with some explicit bit specification */ + for (; !type->info.bits.count; type = &type->der->type); + + if (value || store) { + /* allocate the array of pointers to bits definition */ + bits = calloc(type->info.bits.count, sizeof *bits); + LY_CHECK_ERR_GOTO(!bits, LOGMEM(ctx), error); + } + + if (!value) { + /* no bits set */ + if (store) { + /* store empty array */ + val->bit = bits; + *val_type = LY_TYPE_BITS; + } + break; + } + + c = 0; + i = 0; + while (value[c]) { + /* skip leading whitespaces */ + while (isspace(value[c])) { + c++; + } + if (!value[c]) { + /* trailing white spaces */ + break; + } + + /* get the length of the bit identifier */ + for (len = 0; value[c] && !isspace(value[c]); c++, len++); + + /* go back to the beginning of the identifier */ + c = c - len; + + /* find bit definition, identifiers appear ordered by their position */ + for (found = i = 0; i < type->info.bits.count; i++) { + if (!strncmp(type->info.bits.bit[i].name, &value[c], len) && !type->info.bits.bit[i].name[len]) { + /* we have match, check if the value is enabled ... */ + for (j = 0; !trusted && (j < type->info.bits.bit[i].iffeature_size); j++) { + if (!resolve_iffeature(&type->info.bits.bit[i].iffeature[j])) { + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); + } + LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, + "Bit \"%s\" is disabled by its %d. if-feature condition.", + type->info.bits.bit[i].name, j + 1); + free(bits); + goto error; + } + } + /* check that the value was not already set */ + if (bits[i]) { + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); + } + LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Bit \"%s\" used multiple times.", + type->info.bits.bit[i].name); + free(bits); + goto error; + } + /* ... and then store the pointer */ + bits[i] = &type->info.bits.bit[i]; + + /* stop searching */ + found = 1; + break; + } + } + + if (!found) { + /* referenced bit value does not exist */ + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); + } + free(bits); + goto error; + } + c = c + len; + } + + if (make_canonical(ctx, LY_TYPE_BITS, value_, bits, &type->info.bits.count) == -1) { + free(bits); + goto error; + } + + if (store) { + /* store the result */ + val->bit = bits; + *val_type = LY_TYPE_BITS; + } else { + free(bits); + } + break; + + case LY_TYPE_BOOL: + if (value && !strcmp(value, "true")) { + if (store) { + val->bln = 1; + } + } else if (!value || strcmp(value, "false")) { + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value ? value : "", itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value ? value : ""); + } + goto error; + } else { + if (store) { + val->bln = 0; + } + } + + if (store) { + *val_type = LY_TYPE_BOOL; + } + break; + + case LY_TYPE_DEC64: + if (!value || !value[0]) { + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); + } + goto error; + } + + ptr = value; + if (parse_range_dec64(&ptr, type->info.dec64.dig, &num) || ptr[0]) { + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); + } + goto error; + } + + if (!trusted && validate_length_range(2, 0, 0, num, type->info.dec64.dig, type, value, contextnode)) { + goto error; + } + + if (make_canonical(ctx, LY_TYPE_DEC64, value_, &num, &type->info.dec64.dig) == -1) { + goto error; + } + + if (store) { + /* store the result */ + val->dec64 = num; + *val_type = LY_TYPE_DEC64; + } + break; + + case LY_TYPE_EMPTY: + if (value && value[0]) { + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); + } + goto error; + } + + if (store) { + *val_type = LY_TYPE_EMPTY; + } + break; + + case LY_TYPE_ENUM: + /* locate enums structure with the enumeration definitions, + * since YANG 1.1 allows restricted enums, it is the first + * enum type with some explicit enum specification */ + for (; !type->info.enums.count; type = &type->der->type); + + /* find matching enumeration value */ + for (i = found = 0; i < type->info.enums.count; i++) { + if (value && !strcmp(value, type->info.enums.enm[i].name)) { + /* we have match, check if the value is enabled ... */ + for (j = 0; !trusted && (j < type->info.enums.enm[i].iffeature_size); j++) { + if (!resolve_iffeature(&type->info.enums.enm[i].iffeature[j])) { + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); + } + LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Enum \"%s\" is disabled by its %d. if-feature condition.", + value, j + 1); + goto error; + } + } + /* ... and store pointer to the definition */ + if (store) { + val->enm = &type->info.enums.enm[i]; + *val_type = LY_TYPE_ENUM; + } + found = 1; + break; + } + } + + if (!found) { + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value ? value : "", itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value ? value : ""); + } + goto error; + } + break; + + case LY_TYPE_IDENT: + if (!value) { + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); + } + goto error; + } + + if (xml) { + ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); + /* first, convert value into the json format, silently */ + value = transform_xml2json(ctx, value, xml, 0, 0); + ly_ilo_restore(NULL, prev_ilo, NULL, 0); + if (!value) { + /* invalid identityref format */ + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); + } + goto error; + } + + /* the value has no prefix (default namespace), but the element's namespace has a prefix, find default namespace */ + if (!strchr(value, ':') && xml->ns->prefix) { + value = ident_val_add_module_prefix(value, xml, ctx); + if (!value) { + goto error; + } + } + } else if (dflt) { + ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); + /* the value actually uses module's prefixes instead of the module names as in JSON format, + * we have to convert it */ + value = transform_schema2json(local_mod, value); + ly_ilo_restore(NULL, prev_ilo, NULL, 0); + if (!value) { + /* invalid identityref format or it was already transformed, so ignore the error here */ + value = lydict_insert(ctx, *value_, 0); + } + } else { + value = lydict_insert(ctx, *value_, 0); + } + /* value is now in the dictionary, whether it differs from *value_ or not */ + + ident = resolve_identref(type, value, contextnode, local_mod, dflt); + if (!ident) { + lydict_remove(ctx, value); + goto error; + } else if (store) { + /* store the result */ + val->ident = ident; + *val_type = LY_TYPE_IDENT; + } + + /* the value is always changed and includes prefix */ + if (dflt) { + type->parent->flags |= LYS_DFLTJSON; + } + + if (make_canonical(ctx, LY_TYPE_IDENT, &value, (void*)lys_main_module(local_mod)->name, NULL) == -1) { + lydict_remove(ctx, value); + goto error; + } + + /* replace the old value with the new one (even if they may be the same) */ + lydict_remove(ctx, *value_); + *value_ = value; + break; + + case LY_TYPE_INST: + if (!value) { + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); + } + goto error; + } + + if (xml) { + ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); + /* first, convert value into the json format, silently */ + value = transform_xml2json(ctx, value, xml, 1, 1); + ly_ilo_restore(NULL, prev_ilo, NULL, 0); + if (!value) { + /* invalid instance-identifier format */ + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); + } + goto error; + } else if (ly_strequal(value, *value_, 1)) { + /* we have actually created the same expression (prefixes are the same as the module names) + * so we have just increased dictionary's refcount - fix it */ + lydict_remove(ctx, value); + } + } else if (dflt) { + /* turn logging off */ + ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); + + /* the value actually uses module's prefixes instead of the module names as in JSON format, + * we have to convert it */ + value = transform_schema2json(local_mod, value); + if (!value) { + /* invalid identityref format or it was already transformed, so ignore the error here */ + value = *value_; + } else if (ly_strequal(value, *value_, 1)) { + /* we have actually created the same expression (prefixes are the same as the module names) + * so we have just increased dictionary's refcount - fix it */ + lydict_remove(ctx, value); + } + /* turn logging back on */ + ly_ilo_restore(NULL, prev_ilo, NULL, 0); + } else { + if ((c = make_canonical(ctx, LY_TYPE_INST, &value, NULL, NULL))) { + if (c == -1) { + goto error; + } + + /* if a change occurred, value was removed from the dictionary so fix the pointers */ + *value_ = value; + } + } + + if (store) { + /* note that the data node is an unresolved instance-identifier */ + val->instance = NULL; + *val_type = LY_TYPE_INST; + *val_flags |= LY_VALUE_UNRES; + } + + if (!ly_strequal(value, *value_, 1)) { + /* update the changed value */ + lydict_remove(ctx, *value_); + *value_ = value; + + /* we have to remember the conversion into JSON format to be able to print it in correct form */ + if (dflt) { + type->parent->flags |= LYS_DFLTJSON; + } + } + break; + + case LY_TYPE_LEAFREF: + if (!value) { + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); + } + goto error; + } + + /* it is called not only to get the final type, but mainly to update value to canonical or JSON form + * if needed */ + t = lyp_parse_value(&type->info.lref.target->type, value_, xml, leaf, attr, NULL, store, dflt, trusted); + value = *value_; /* refresh possibly changed value */ + if (!t) { + /* already logged */ + goto error; + } + + if (store) { + /* make the note that the data node is an unresolved leafref (value union was already filled) */ + *val_flags |= LY_VALUE_UNRES; + } + + type = t; + break; + + case LY_TYPE_STRING: + if (!trusted && validate_length_range(0, (value ? ly_strlen_utf8(value) : 0), 0, 0, 0, type, value, contextnode)) { + goto error; + } + + if (!trusted && validate_pattern(ctx, value, type, contextnode)) { + goto error; + } + + /* special handling of ietf-yang-types xpath1.0 */ + for (tpdf = type->der; + tpdf->module && (strcmp(tpdf->name, "xpath1.0") || strcmp(tpdf->module->name, "ietf-yang-types")); + tpdf = tpdf->type.der); + if (tpdf->module && xml) { + /* convert value into the json format */ + value = transform_xml2json(ctx, value ? value : "", xml, 1, 1); + if (!value) { + /* invalid instance-identifier format */ + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); + } + goto error; + } + + if (!ly_strequal(value, *value_, 1)) { + /* update the changed value */ + lydict_remove(ctx, *value_); + *value_ = value; + } + } + + if (store) { + /* store the result */ + val->string = value; + *val_type = LY_TYPE_STRING; + } + break; + + case LY_TYPE_INT8: + if (parse_int(value, __INT64_C(-128), __INT64_C(127), dflt ? 0 : 10, &num, contextnode) + || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { + goto error; + } + + if (make_canonical(ctx, LY_TYPE_INT8, value_, &num, NULL) == -1) { + goto error; + } + + if (store) { + /* store the result */ + val->int8 = (int8_t)num; + *val_type = LY_TYPE_INT8; + } + break; + + case LY_TYPE_INT16: + if (parse_int(value, __INT64_C(-32768), __INT64_C(32767), dflt ? 0 : 10, &num, contextnode) + || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { + goto error; + } + + if (make_canonical(ctx, LY_TYPE_INT16, value_, &num, NULL) == -1) { + goto error; + } + + if (store) { + /* store the result */ + val->int16 = (int16_t)num; + *val_type = LY_TYPE_INT16; + } + break; + + case LY_TYPE_INT32: + if (parse_int(value, __INT64_C(-2147483648), __INT64_C(2147483647), dflt ? 0 : 10, &num, contextnode) + || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { + goto error; + } + + if (make_canonical(ctx, LY_TYPE_INT32, value_, &num, NULL) == -1) { + goto error; + } + + if (store) { + /* store the result */ + val->int32 = (int32_t)num; + *val_type = LY_TYPE_INT32; + } + break; + + case LY_TYPE_INT64: + if (parse_int(value, __INT64_C(-9223372036854775807) - __INT64_C(1), __INT64_C(9223372036854775807), + dflt ? 0 : 10, &num, contextnode) + || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { + goto error; + } + + if (make_canonical(ctx, LY_TYPE_INT64, value_, &num, NULL) == -1) { + goto error; + } + + if (store) { + /* store the result */ + val->int64 = num; + *val_type = LY_TYPE_INT64; + } + break; + + case LY_TYPE_UINT8: + if (parse_uint(value, __UINT64_C(255), dflt ? 0 : 10, &unum, contextnode) + || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { + goto error; + } + + if (make_canonical(ctx, LY_TYPE_UINT8, value_, &unum, NULL) == -1) { + goto error; + } + + if (store) { + /* store the result */ + val->uint8 = (uint8_t)unum; + *val_type = LY_TYPE_UINT8; + } + break; + + case LY_TYPE_UINT16: + if (parse_uint(value, __UINT64_C(65535), dflt ? 0 : 10, &unum, contextnode) + || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { + goto error; + } + + if (make_canonical(ctx, LY_TYPE_UINT16, value_, &unum, NULL) == -1) { + goto error; + } + + if (store) { + /* store the result */ + val->uint16 = (uint16_t)unum; + *val_type = LY_TYPE_UINT16; + } + break; + + case LY_TYPE_UINT32: + if (parse_uint(value, __UINT64_C(4294967295), dflt ? 0 : 10, &unum, contextnode) + || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { + goto error; + } + + if (make_canonical(ctx, LY_TYPE_UINT32, value_, &unum, NULL) == -1) { + goto error; + } + + if (store) { + /* store the result */ + val->uint32 = (uint32_t)unum; + *val_type = LY_TYPE_UINT32; + } + break; + + case LY_TYPE_UINT64: + if (parse_uint(value, __UINT64_C(18446744073709551615), dflt ? 0 : 10, &unum, contextnode) + || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { + goto error; + } + + if (make_canonical(ctx, LY_TYPE_UINT64, value_, &unum, NULL) == -1) { + goto error; + } + + if (store) { + /* store the result */ + val->uint64 = unum; + *val_type = LY_TYPE_UINT64; + } + break; + + case LY_TYPE_UNION: + if (store) { + /* unresolved union type */ + memset(val, 0, sizeof(lyd_val)); + *val_type = LY_TYPE_UNION; + } + + if (type->info.uni.has_ptr_type) { + /* we are not resolving anything here, only parsing, and in this case we cannot decide + * the type without resolving it -> we return the union type (resolve it with resolve_union()) */ + if (xml) { + /* in case it should resolve into a instance-identifier, we can only do the JSON conversion here */ + ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); + val->string = transform_xml2json(ctx, value, xml, 1, 1); + ly_ilo_restore(NULL, prev_ilo, NULL, 0); + if (!val->string) { + /* invalid instance-identifier format, likely some other type */ + val->string = lydict_insert(ctx, value, 0); + } + } + break; + } + + t = NULL; + found = 0; + + /* turn logging off, we are going to try to validate the value with all the types in order */ + ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); + + while ((t = lyp_get_next_union_type(type, t, &found))) { + found = 0; + ret = lyp_parse_value(t, value_, xml, leaf, attr, NULL, store, dflt, 0); + if (ret) { + /* we have the result */ + type = ret; + break; + } + + if (store) { + /* erase possible present and invalid value data */ + lyd_free_value(*val, *val_type, *val_flags, t, *value_, NULL, NULL, NULL); + memset(val, 0, sizeof(lyd_val)); + } + } + + /* turn logging back on */ + ly_ilo_restore(NULL, prev_ilo, NULL, 0); + + if (!t) { + /* not found */ + if (store) { + *val_type = 0; + } + if (leaf) { + LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_ ? *value_ : "", itemname); + } else { + LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); + } + goto error; + } + break; + + default: + LOGINT(ctx); + goto error; + } + + /* search user types in case this value is supposed to be stored in a custom way */ + if (store && type->der && type->der->module) { + c = lytype_store(type->der->module, type->der->name, value_, val); + if (c == -1) { + goto error; + } else if (!c) { + *val_flags |= LY_VALUE_USER; + } + } + + /* free backup */ + if (store) { + lyd_free_value(old_val, old_val_type, old_val_flags, type, old_val_str, NULL, NULL, NULL); + lydict_remove(ctx, old_val_str); + } + return type; + +error: + /* restore the backup */ + if (store) { + *val = old_val; + *val_type = old_val_type; + *val_flags = old_val_flags; + lydict_remove(ctx, old_val_str); + } + return NULL; +} + +/* does not log, cannot fail */ +struct lys_type * +lyp_get_next_union_type(struct lys_type *type, struct lys_type *prev_type, int *found) +{ + unsigned int i; + struct lys_type *ret = NULL; + + while (!type->info.uni.count) { + assert(type->der); /* at least the direct union type has to have type specified */ + type = &type->der->type; + } + + for (i = 0; i < type->info.uni.count; ++i) { + if (type->info.uni.types[i].base == LY_TYPE_UNION) { + ret = lyp_get_next_union_type(&type->info.uni.types[i], prev_type, found); + if (ret) { + break; + } + continue; + } + + if (!prev_type || *found) { + ret = &type->info.uni.types[i]; + break; + } + + if (&type->info.uni.types[i] == prev_type) { + *found = 1; + } + } + + return ret; +} + +/* ret 0 - ret set, ret 1 - ret not set, no log, ret -1 - ret not set, fatal error */ +int +lyp_fill_attr(struct ly_ctx *ctx, struct lyd_node *parent, const char *module_ns, const char *module_name, + const char *attr_name, const char *attr_value, struct lyxml_elem *xml, int options, struct lyd_attr **ret) +{ + const struct lys_module *mod = NULL; + const struct lys_submodule *submod = NULL; + struct lys_type **type; + struct lyd_attr *dattr; + int pos, i, j, k; + + /* first, get module where the annotation should be defined */ + if (module_ns) { + mod = (struct lys_module *)ly_ctx_get_module_by_ns(ctx, module_ns, NULL, 0); + } else if (module_name) { + mod = (struct lys_module *)ly_ctx_get_module(ctx, module_name, NULL, 0); + } else { + LOGINT(ctx); + return -1; + } + if (!mod) { + return 1; + } + + /* then, find the appropriate annotation definition */ + pos = -1; + for (i = 0, j = 0; i < mod->ext_size; i = i + j + 1) { + j = lys_ext_instance_presence(&ctx->models.list[0]->extensions[0], &mod->ext[i], mod->ext_size - i); + if (j == -1) { + break; + } + if (ly_strequal(mod->ext[i + j]->arg_value, attr_name, 0)) { + pos = i + j; + break; + } + } + + /* try submodules */ + if (pos == -1) { + for (k = 0; k < mod->inc_size; ++k) { + submod = mod->inc[k].submodule; + for (i = 0, j = 0; i < submod->ext_size; i = i + j + 1) { + j = lys_ext_instance_presence(&ctx->models.list[0]->extensions[0], &submod->ext[i], submod->ext_size - i); + if (j == -1) { + break; + } + if (ly_strequal(submod->ext[i + j]->arg_value, attr_name, 0)) { + pos = i + j; + break; + } + } + } + } + + if (pos == -1) { + return 1; + } + + /* allocate and fill the data attribute structure */ + dattr = calloc(1, sizeof *dattr); + LY_CHECK_ERR_RETURN(!dattr, LOGMEM(ctx), -1); + + dattr->parent = parent; + dattr->next = NULL; + dattr->annotation = submod ? (struct lys_ext_instance_complex *)submod->ext[pos] : + (struct lys_ext_instance_complex *)mod->ext[pos]; + dattr->name = lydict_insert(ctx, attr_name, 0); + dattr->value_str = lydict_insert(ctx, attr_value, 0); + + /* the value is here converted to a JSON format if needed in case of LY_TYPE_IDENT and LY_TYPE_INST or to a + * canonical form of the value */ + type = lys_ext_complex_get_substmt(LY_STMT_TYPE, dattr->annotation, NULL); + if (!type || !lyp_parse_value(*type, &dattr->value_str, xml, NULL, dattr, NULL, 1, 0, options & LYD_OPT_TRUSTED)) { + lydict_remove(ctx, dattr->name); + lydict_remove(ctx, dattr->value_str); + free(dattr); + return -1; + } + + *ret = dattr; + return 0; +} + +int +lyp_check_edit_attr(struct ly_ctx *ctx, struct lyd_attr *attr, struct lyd_node *parent, int *editbits) +{ + struct lyd_attr *last = NULL; + int bits = 0; + + /* 0x01 - insert attribute present + * 0x02 - insert is relative (before or after) + * 0x04 - value attribute present + * 0x08 - key attribute present + * 0x10 - operation attribute present + * 0x20 - operation not allowing insert attribute (delete or remove) + */ + LY_TREE_FOR(attr, attr) { + last = NULL; + if (!strcmp(attr->annotation->arg_value, "operation") && + !strcmp(attr->annotation->module->name, "ietf-netconf")) { + if (bits & 0x10) { + LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "operation attributes", parent->schema->name); + return -1; + } + + bits |= 0x10; + if (attr->value.enm->value >= 3) { + /* delete or remove */ + bits |= 0x20; + } + } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ + !strcmp(attr->annotation->arg_value, "insert")) { + /* 'insert' attribute present */ + if (!(parent->schema->flags & LYS_USERORDERED)) { + /* ... but it is not expected */ + LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, "insert"); + return -1; + } + if (bits & 0x01) { + LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "insert attributes", parent->schema->name); + return -1; + } + + bits |= 0x01; + if (attr->value.enm->value >= 2) { + /* before or after */ + bits |= 0x02; + } + last = attr; + } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ + !strcmp(attr->annotation->arg_value, "value")) { + if (bits & 0x04) { + LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "value attributes", parent->schema->name); + return -1; + } else if (parent->schema->nodetype & LYS_LIST) { + LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, attr->name); + return -1; + } + bits |= 0x04; + last = attr; + } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ + !strcmp(attr->annotation->arg_value, "key")) { + if (bits & 0x08) { + LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "key attributes", parent->schema->name); + return -1; + } else if (parent->schema->nodetype & LYS_LEAFLIST) { + LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, attr->name); + return -1; + } + bits |= 0x08; + last = attr; + } + } + + /* report errors */ + if (last && (!(parent->schema->nodetype & (LYS_LEAFLIST | LYS_LIST)) || !(parent->schema->flags & LYS_USERORDERED))) { + /* moving attributes in wrong elements (not an user ordered list or not a list at all) */ + LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, last->name); + return -1; + } else if (bits == 3) { + /* 0x01 | 0x02 - relative position, but value/key is missing */ + if (parent->schema->nodetype & LYS_LIST) { + LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, parent, "key", parent->schema->name); + } else { /* LYS_LEAFLIST */ + LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, parent, "value", parent->schema->name); + } + return -1; + } else if ((bits & (0x04 | 0x08)) && !(bits & 0x02)) { + /* key/value without relative position */ + LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, (bits & 0x04) ? "value" : "key"); + return -1; + } else if ((bits & 0x21) == 0x21) { + /* insert in delete/remove */ + LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, "insert"); + return -1; + } + + if (editbits) { + *editbits = bits; + } + return 0; +} + +/* does not log */ +static int +dup_identity_check(const char *id, struct lys_ident *ident, uint32_t size) +{ + uint32_t i; + + for (i = 0; i < size; i++) { + if (ly_strequal(id, ident[i].name, 1)) { + /* name collision */ + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +int +dup_identities_check(const char *id, struct lys_module *module) +{ + struct lys_module *mainmod; + int i; + + if (dup_identity_check(id, module->ident, module->ident_size)) { + LOGVAL(module->ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "identity", id); + return EXIT_FAILURE; + } + + /* check identity in submodules */ + mainmod = lys_main_module(module); + for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; ++i) { + if (dup_identity_check(id, mainmod->inc[i].submodule->ident, mainmod->inc[i].submodule->ident_size)) { + LOGVAL(module->ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "identity", id); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +/* does not log */ +int +dup_typedef_check(const char *type, struct lys_tpdf *tpdf, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (!strcmp(type, tpdf[i].name)) { + /* name collision */ + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +/* does not log */ +static int +dup_feature_check(const char *id, struct lys_module *module) +{ + int i; + + for (i = 0; i < module->features_size; i++) { + if (!strcmp(id, module->features[i].name)) { + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +/* does not log */ +static int +dup_prefix_check(const char *prefix, struct lys_module *module) +{ + int i; + + if (module->prefix && !strcmp(module->prefix, prefix)) { + return EXIT_FAILURE; + } + for (i = 0; i < module->imp_size; i++) { + if (!strcmp(module->imp[i].prefix, prefix)) { + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +/* logs directly */ +int +lyp_check_identifier(struct ly_ctx *ctx, const char *id, enum LY_IDENT type, struct lys_module *module, + struct lys_node *parent) +{ + int i, j; + int size; + struct lys_tpdf *tpdf; + struct lys_node *node; + struct lys_module *mainmod; + struct lys_submodule *submod; + + assert(ctx && id); + + /* check id syntax */ + if (!(id[0] >= 'A' && id[0] <= 'Z') && !(id[0] >= 'a' && id[0] <= 'z') && id[0] != '_') { + LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "invalid start character"); + return EXIT_FAILURE; + } + for (i = 1; id[i]; i++) { + if (!(id[i] >= 'A' && id[i] <= 'Z') && !(id[i] >= 'a' && id[i] <= 'z') + && !(id[i] >= '0' && id[i] <= '9') && id[i] != '_' && id[i] != '-' && id[i] != '.') { + LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "invalid character"); + return EXIT_FAILURE; + } + } + + if (i > 64) { + LOGWRN(ctx, "Identifier \"%s\" is long, you should use something shorter.", id); + } + + switch (type) { + case LY_IDENT_NAME: + /* check uniqueness of the node within its siblings */ + if (!parent) { + break; + } + + LY_TREE_FOR(parent->child, node) { + if (ly_strequal(node->name, id, 1)) { + LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "name duplication"); + return EXIT_FAILURE; + } + } + break; + case LY_IDENT_TYPE: + assert(module); + mainmod = lys_main_module(module); + + /* check collision with the built-in types */ + if (!strcmp(id, "binary") || !strcmp(id, "bits") || + !strcmp(id, "boolean") || !strcmp(id, "decimal64") || + !strcmp(id, "empty") || !strcmp(id, "enumeration") || + !strcmp(id, "identityref") || !strcmp(id, "instance-identifier") || + !strcmp(id, "int8") || !strcmp(id, "int16") || + !strcmp(id, "int32") || !strcmp(id, "int64") || + !strcmp(id, "leafref") || !strcmp(id, "string") || + !strcmp(id, "uint8") || !strcmp(id, "uint16") || + !strcmp(id, "uint32") || !strcmp(id, "uint64") || !strcmp(id, "union")) { + LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, id, "typedef"); + LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Typedef name duplicates a built-in type."); + return EXIT_FAILURE; + } + + /* check locally scoped typedefs (avoid name shadowing) */ + for (; parent; parent = lys_parent(parent)) { + switch (parent->nodetype) { + case LYS_CONTAINER: + size = ((struct lys_node_container *)parent)->tpdf_size; + tpdf = ((struct lys_node_container *)parent)->tpdf; + break; + case LYS_LIST: + size = ((struct lys_node_list *)parent)->tpdf_size; + tpdf = ((struct lys_node_list *)parent)->tpdf; + break; + case LYS_GROUPING: + size = ((struct lys_node_grp *)parent)->tpdf_size; + tpdf = ((struct lys_node_grp *)parent)->tpdf; + break; + default: + continue; + } + + if (dup_typedef_check(id, tpdf, size)) { + LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); + return EXIT_FAILURE; + } + } + + /* check top-level names */ + if (dup_typedef_check(id, module->tpdf, module->tpdf_size)) { + LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); + return EXIT_FAILURE; + } + + /* check submodule's top-level names */ + for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; i++) { + if (dup_typedef_check(id, mainmod->inc[i].submodule->tpdf, mainmod->inc[i].submodule->tpdf_size)) { + LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); + return EXIT_FAILURE; + } + } + + break; + case LY_IDENT_PREFIX: + assert(module); + + /* check the module itself */ + if (dup_prefix_check(id, module)) { + LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "prefix", id); + return EXIT_FAILURE; + } + break; + case LY_IDENT_FEATURE: + assert(module); + mainmod = lys_main_module(module); + + /* check feature name uniqueness*/ + /* check features in the current module */ + if (dup_feature_check(id, module)) { + LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "feature", id); + return EXIT_FAILURE; + } + + /* and all its submodules */ + for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; i++) { + if (dup_feature_check(id, (struct lys_module *)mainmod->inc[i].submodule)) { + LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "feature", id); + return EXIT_FAILURE; + } + } + break; + + case LY_IDENT_EXTENSION: + assert(module); + mainmod = lys_main_module(module); + + /* check extension name uniqueness in the main module ... */ + for (i = 0; i < mainmod->extensions_size; i++) { + if (ly_strequal(id, mainmod->extensions[i].name, 1)) { + LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "extension", id); + return EXIT_FAILURE; + } + } + + /* ... and all its submodules */ + for (j = 0; j < mainmod->inc_size && mainmod->inc[j].submodule; j++) { + submod = mainmod->inc[j].submodule; /* shortcut */ + for (i = 0; i < submod->extensions_size; i++) { + if (ly_strequal(id, submod->extensions[i].name, 1)) { + LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "extension", id); + return EXIT_FAILURE; + } + } + } + + break; + + default: + /* no check required */ + break; + } + + return EXIT_SUCCESS; +} + +/* logs directly */ +int +lyp_check_date(struct ly_ctx *ctx, const char *date) +{ + int i; + struct tm tm, tm_; + char *r; + + assert(date); + + /* check format */ + for (i = 0; i < LY_REV_SIZE - 1; i++) { + if (i == 4 || i == 7) { + if (date[i] != '-') { + goto error; + } + } else if (!isdigit(date[i])) { + goto error; + } + } + + /* check content, e.g. 2018-02-31 */ + memset(&tm, 0, sizeof tm); + r = strptime(date, "%Y-%m-%d", &tm); + if (!r || r != &date[LY_REV_SIZE - 1]) { + goto error; + } + /* set some arbitrary non-0 value in case DST changes, it could move the day otherwise */ + tm.tm_hour = 12; + + memcpy(&tm_, &tm, sizeof tm); + mktime(&tm_); /* mktime modifies tm_ if it refers invalid date */ + if (tm.tm_mday != tm_.tm_mday) { /* e.g 2018-02-29 -> 2018-03-01 */ + /* checking days is enough, since other errors + * have been checked by strptime() */ + goto error; + } + + return EXIT_SUCCESS; + +error: + LOGVAL(ctx, LYE_INDATE, LY_VLOG_NONE, NULL, date); + return EXIT_FAILURE; +} + +/** + * @return + * NULL - success + * root - not yet resolvable + * other node - mandatory node under the root + */ +static const struct lys_node * +lyp_check_mandatory_(const struct lys_node *root) +{ + int mand_flag = 0; + const struct lys_node *iter = NULL; + + while ((iter = lys_getnext(iter, root, NULL, LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHUSES | LYS_GETNEXT_INTOUSES + | LYS_GETNEXT_INTONPCONT | LYS_GETNEXT_NOSTATECHECK))) { + if (iter->nodetype == LYS_USES) { + if (!((struct lys_node_uses *)iter)->grp) { + /* not yet resolved uses */ + return root; + } else { + /* go into uses */ + continue; + } + } + if (iter->nodetype == LYS_CHOICE) { + /* skip it, it was already checked for direct mandatory node in default */ + continue; + } + if (iter->nodetype == LYS_LIST) { + if (((struct lys_node_list *)iter)->min) { + mand_flag = 1; + } + } else if (iter->nodetype == LYS_LEAFLIST) { + if (((struct lys_node_leaflist *)iter)->min) { + mand_flag = 1; + } + } else if (iter->flags & LYS_MAND_TRUE) { + mand_flag = 1; + } + + if (mand_flag) { + return iter; + } + } + + return NULL; +} + +/* logs directly */ +int +lyp_check_mandatory_augment(struct lys_node_augment *aug, const struct lys_node *target) +{ + const struct lys_node *node; + + if (aug->when || target->nodetype == LYS_CHOICE) { + /* - mandatory nodes in new cases are ok; + * clarification from YANG 1.1 - augmentation can add mandatory nodes when it is + * conditional with a when statement */ + return EXIT_SUCCESS; + } + + if ((node = lyp_check_mandatory_((struct lys_node *)aug))) { + if (node != (struct lys_node *)aug) { + LOGVAL(target->module->ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "mandatory"); + LOGVAL(target->module->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, + "Mandatory node \"%s\" appears in augment of \"%s\" without when condition.", + node->name, aug->target_name); + return -1; + } + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/** + * @brief check that a mandatory node is not directly under the default case. + * @param[in] node choice with default node + * @return EXIT_SUCCESS if the constraint is fulfilled, EXIT_FAILURE otherwise + */ +int +lyp_check_mandatory_choice(struct lys_node *node) +{ + const struct lys_node *mand, *dflt = ((struct lys_node_choice *)node)->dflt; + + if ((mand = lyp_check_mandatory_(dflt))) { + if (mand != dflt) { + LOGVAL(node->module->ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "mandatory"); + LOGVAL(node->module->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, + "Mandatory node \"%s\" is directly under the default case \"%s\" of the \"%s\" choice.", + mand->name, dflt->name, node->name); + return -1; + } + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/** + * @brief Check status for invalid combination. + * + * @param[in] flags1 Flags of the referencing node. + * @param[in] mod1 Module of the referencing node, + * @param[in] name1 Schema node name of the referencing node. + * @param[in] flags2 Flags of the referenced node. + * @param[in] mod2 Module of the referenced node, + * @param[in] name2 Schema node name of the referenced node. + * @return EXIT_SUCCES on success, EXIT_FAILURE on invalid reference. + */ +int +lyp_check_status(uint16_t flags1, struct lys_module *mod1, const char *name1, + uint16_t flags2, struct lys_module *mod2, const char *name2, + const struct lys_node *node) +{ + uint16_t flg1, flg2; + + flg1 = (flags1 & LYS_STATUS_MASK) ? (flags1 & LYS_STATUS_MASK) : LYS_STATUS_CURR; + flg2 = (flags2 & LYS_STATUS_MASK) ? (flags2 & LYS_STATUS_MASK) : LYS_STATUS_CURR; + + if ((flg1 < flg2) && (lys_main_module(mod1) == lys_main_module(mod2))) { + LOGVAL(mod1->ctx, LYE_INSTATUS, node ? LY_VLOG_LYS : LY_VLOG_NONE, node, + flg1 == LYS_STATUS_CURR ? "current" : "deprecated", name1, "references", + flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", name2); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +void +lyp_del_includedup(struct lys_module *mod, int free_subs) +{ + struct ly_modules_list *models = &mod->ctx->models; + uint8_t i; + + assert(mod && !mod->type); + + if (models->parsed_submodules_count) { + for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i); + if (models->parsed_submodules[i] == mod) { + if (free_subs) { + for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i) { + lys_sub_module_remove_devs_augs((struct lys_module *)models->parsed_submodules[i]); + lys_submodule_module_data_free((struct lys_submodule *)models->parsed_submodules[i]); + lys_submodule_free((struct lys_submodule *)models->parsed_submodules[i], NULL); + } + } + + models->parsed_submodules_count = i; + if (!models->parsed_submodules_count) { + free(models->parsed_submodules); + models->parsed_submodules = NULL; + } + } + } +} + +static void +lyp_add_includedup(struct lys_module *sub_mod, struct lys_submodule *parsed_submod) +{ + struct ly_modules_list *models = &sub_mod->ctx->models; + int16_t i; + + /* store main module if first include */ + if (models->parsed_submodules_count) { + for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i); + } else { + i = -1; + } + if ((i == -1) || (models->parsed_submodules[i] != lys_main_module(sub_mod))) { + ++models->parsed_submodules_count; + models->parsed_submodules = ly_realloc(models->parsed_submodules, + models->parsed_submodules_count * sizeof *models->parsed_submodules); + LY_CHECK_ERR_RETURN(!models->parsed_submodules, LOGMEM(sub_mod->ctx), ); + models->parsed_submodules[models->parsed_submodules_count - 1] = lys_main_module(sub_mod); + } + + /* store parsed submodule */ + ++models->parsed_submodules_count; + models->parsed_submodules = ly_realloc(models->parsed_submodules, + models->parsed_submodules_count * sizeof *models->parsed_submodules); + LY_CHECK_ERR_RETURN(!models->parsed_submodules, LOGMEM(sub_mod->ctx), ); + models->parsed_submodules[models->parsed_submodules_count - 1] = (struct lys_module *)parsed_submod; +} + +/* + * types: 0 - include, 1 - import + */ +static int +lyp_check_circmod(struct lys_module *module, const char *value, int type) +{ + LY_ECODE code = type ? LYE_CIRC_IMPORTS : LYE_CIRC_INCLUDES; + struct ly_modules_list *models = &module->ctx->models; + uint8_t i; + + /* include/import itself */ + if (ly_strequal(module->name, value, 1)) { + LOGVAL(module->ctx, code, LY_VLOG_NONE, NULL, value); + return -1; + } + + /* currently parsed modules */ + for (i = 0; i < models->parsing_sub_modules_count; i++) { + if (ly_strequal(models->parsing_sub_modules[i]->name, value, 1)) { + LOGVAL(module->ctx, code, LY_VLOG_NONE, NULL, value); + return -1; + } + } + + return 0; +} + +int +lyp_check_circmod_add(struct lys_module *module) +{ + struct ly_modules_list *models = &module->ctx->models; + + /* storing - enlarge the list of modules being currently parsed */ + ++models->parsing_sub_modules_count; + models->parsing_sub_modules = ly_realloc(models->parsing_sub_modules, + models->parsing_sub_modules_count * sizeof *models->parsing_sub_modules); + LY_CHECK_ERR_RETURN(!models->parsing_sub_modules, LOGMEM(module->ctx), -1); + models->parsing_sub_modules[models->parsing_sub_modules_count - 1] = module; + + return 0; +} + +void +lyp_check_circmod_pop(struct ly_ctx *ctx) +{ + if (!ctx->models.parsing_sub_modules_count) { + LOGINT(ctx); + return; + } + + /* update the list of currently being parsed modules */ + ctx->models.parsing_sub_modules_count--; + if (!ctx->models.parsing_sub_modules_count) { + free(ctx->models.parsing_sub_modules); + ctx->models.parsing_sub_modules = NULL; + } +} + +/* + * -1 - error - invalid duplicities) + * 0 - success, no duplicity + * 1 - success, valid duplicity found and stored in *sub + */ +static int +lyp_check_includedup(struct lys_module *mod, const char *name, struct lys_include *inc, struct lys_submodule **sub) +{ + struct lys_module **parsed_sub = mod->ctx->models.parsed_submodules; + uint8_t i, parsed_sub_count = mod->ctx->models.parsed_submodules_count; + + assert(sub); + + for (i = 0; i < mod->inc_size; ++i) { + if (ly_strequal(mod->inc[i].submodule->name, name, 1)) { + /* the same module is already included in the same module - error */ + LOGVAL(mod->ctx, LYE_INARG, LY_VLOG_NONE, NULL, name, "include"); + LOGVAL(mod->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Submodule \"%s\" included twice in the same module \"%s\".", + name, mod->name); + return -1; + } + } + + if (parsed_sub_count) { + assert(!parsed_sub[0]->type); + for (i = parsed_sub_count - 1; parsed_sub[i]->type; --i) { + if (ly_strequal(parsed_sub[i]->name, name, 1)) { + /* check revisions, including multiple revisions of a single module is error */ + if (inc->rev[0] && (!parsed_sub[i]->rev_size || strcmp(parsed_sub[i]->rev[0].date, inc->rev))) { + /* the already included submodule has + * - no revision, but here we require some + * - different revision than the one required here */ + LOGVAL(mod->ctx, LYE_INARG, LY_VLOG_NONE, NULL, name, "include"); + LOGVAL(mod->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Including multiple revisions of submodule \"%s\".", name); + return -1; + } + + /* the same module is already included in some other submodule, return it */ + (*sub) = (struct lys_submodule *)parsed_sub[i]; + return 1; + } + } + } + + /* no duplicity found */ + return 0; +} + +/* returns: + * 0 - inc successfully filled + * -1 - error + */ +int +lyp_check_include(struct lys_module *module, const char *value, struct lys_include *inc, struct unres_schema *unres) +{ + int i; + + /* check that the submodule was not included yet */ + i = lyp_check_includedup(module, value, inc, &inc->submodule); + if (i == -1) { + return -1; + } else if (i == 1) { + return 0; + } + /* submodule is not yet loaded */ + + /* circular include check */ + if (lyp_check_circmod(module, value, 0)) { + return -1; + } + + /* try to load the submodule */ + inc->submodule = (struct lys_submodule *)ly_ctx_load_sub_module(module->ctx, module, value, + inc->rev[0] ? inc->rev : NULL, 1, unres); + + /* check the result */ + if (!inc->submodule) { + if (ly_errno != LY_EVALID) { + LOGVAL(module->ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "include"); + } + LOGERR(module->ctx, LY_EVALID, "Including \"%s\" module into \"%s\" failed.", value, module->name); + return -1; + } + + /* check the revision */ + if (inc->rev[0] && inc->submodule->rev_size && strcmp(inc->rev, inc->submodule->rev[0].date)) { + LOGERR(module->ctx, LY_EVALID, "\"%s\" include of submodule \"%s\" in revision \"%s\" not found.", + module->name, value, inc->rev); + unres_schema_free((struct lys_module *)inc->submodule, &unres, 0); + lys_sub_module_remove_devs_augs((struct lys_module *)inc->submodule); + lys_submodule_module_data_free((struct lys_submodule *)inc->submodule); + lys_submodule_free(inc->submodule, NULL); + inc->submodule = NULL; + return -1; + } + + /* store the submodule as successfully parsed */ + lyp_add_includedup(module, inc->submodule); + + return 0; +} + +static int +lyp_check_include_missing_recursive(struct lys_module *main_module, struct lys_submodule *sub) +{ + uint8_t i, j; + void *reallocated; + int ret = 0, tmp; + struct ly_ctx *ctx = main_module->ctx; + + for (i = 0; i < sub->inc_size; i++) { + /* check that the include is also present in the main module */ + for (j = 0; j < main_module->inc_size; j++) { + if (main_module->inc[j].submodule == sub->inc[i].submodule) { + break; + } + } + + if (j == main_module->inc_size) { + /* match not found */ + if (main_module->version >= LYS_VERSION_1_1) { + LOGVAL(ctx, LYE_MISSSTMT, LY_VLOG_NONE, NULL, "include"); + LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, + "The main module \"%s\" misses include of the \"%s\" submodule used in another submodule \"%s\".", + main_module->name, sub->inc[i].submodule->name, sub->name); + /* now we should return error, but due to the issues with freeing the module, we actually have + * to go through the all includes and, as in case of 1.0, add them into the main module and fail + * at the end when all the includes are in the main module and we can free them */ + ret = 1; + } else { + /* not strictly an error in YANG 1.0 */ + LOGWRN(ctx, "The main module \"%s\" misses include of the \"%s\" submodule used in another submodule \"%s\".", + main_module->name, sub->inc[i].submodule->name, sub->name); + LOGWRN(ctx, "To avoid further issues, adding submodule \"%s\" into the main module \"%s\".", + sub->inc[i].submodule->name, main_module->name); + /* but since it is a good practise and because we expect all the includes in the main module + * when searching it and also when freeing the module, put it into it */ + } + main_module->inc_size++; + reallocated = realloc(main_module->inc, main_module->inc_size * sizeof *main_module->inc); + LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx), 1); + main_module->inc = reallocated; + memset(&main_module->inc[main_module->inc_size - 1], 0, sizeof *main_module->inc); + /* to avoid unexpected consequences, copy just a link to the submodule and the revision, + * all other substatements of the include are ignored */ + memcpy(&main_module->inc[main_module->inc_size - 1].rev, sub->inc[i].rev, LY_REV_SIZE - 1); + main_module->inc[main_module->inc_size - 1].submodule = sub->inc[i].submodule; + } + + /* recursion */ + tmp = lyp_check_include_missing_recursive(main_module, sub->inc[i].submodule); + if (!ret && tmp) { + ret = 1; + } + } + + return ret; +} + +int +lyp_check_include_missing(struct lys_module *main_module) +{ + int ret = 0; + uint8_t i; + + /* in YANG 1.1, all the submodules must be in the main module, check it even for + * 1.0 where it will be printed as warning and the include will be added into the main module */ + for (i = 0; i < main_module->inc_size; i++) { + if (lyp_check_include_missing_recursive(main_module, main_module->inc[i].submodule)) { + ret = 1; + } + } + + return ret; +} + +/* returns: + * 0 - imp successfully filled + * -1 - error, imp not cleaned + */ +int +lyp_check_import(struct lys_module *module, const char *value, struct lys_import *imp) +{ + int i; + struct lys_module *dup = NULL; + struct ly_ctx *ctx = module->ctx; + + /* check for importing a single module in multiple revisions */ + for (i = 0; i < module->imp_size; i++) { + if (!module->imp[i].module) { + /* skip the not yet filled records */ + continue; + } + if (ly_strequal(module->imp[i].module->name, value, 1)) { + /* check revisions, including multiple revisions of a single module is error */ + if (imp->rev[0] && (!module->imp[i].module->rev_size || strcmp(module->imp[i].module->rev[0].date, imp->rev))) { + /* the already imported module has + * - no revision, but here we require some + * - different revision than the one required here */ + LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "import"); + LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Importing multiple revisions of module \"%s\".", value); + return -1; + } else if (!imp->rev[0]) { + /* no revision, remember the duplication, but check revisions after loading the module + * because the current revision can be the same (then it is ok) or it can differ (then it + * is error */ + dup = module->imp[i].module; + break; + } + + /* there is duplication, but since prefixes differs (checked in caller of this function), + * it is ok */ + imp->module = module->imp[i].module; + return 0; + } + } + + /* circular import check */ + if (lyp_check_circmod(module, value, 1)) { + return -1; + } + + /* load module - in specific situations it tries to get the module from the context */ + imp->module = (struct lys_module *)ly_ctx_load_sub_module(module->ctx, NULL, value, imp->rev[0] ? imp->rev : NULL, + module->ctx->models.flags & LY_CTX_ALLIMPLEMENTED ? 1 : 0, + NULL); + + /* check the result */ + if (!imp->module) { + LOGERR(ctx, LY_EVALID, "Importing \"%s\" module into \"%s\" failed.", value, module->name); + return -1; + } + + if (imp->rev[0] && imp->module->rev_size && strcmp(imp->rev, imp->module->rev[0].date)) { + LOGERR(ctx, LY_EVALID, "\"%s\" import of module \"%s\" in revision \"%s\" not found.", + module->name, value, imp->rev); + return -1; + } + + if (dup) { + /* check the revisions */ + if ((dup != imp->module) || + (dup->rev_size != imp->module->rev_size && (!dup->rev_size || imp->module->rev_size)) || + (dup->rev_size && strcmp(dup->rev[0].date, imp->module->rev[0].date))) { + /* - modules are not the same + * - one of modules has no revision (except they both has no revision) + * - revisions of the modules are not the same */ + LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "import"); + LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Importing multiple revisions of module \"%s\".", value); + return -1; + } else { + LOGWRN(ctx, "Module \"%s\" is imported by \"%s\" multiple times with different prefixes.", dup->name, module->name); + } + } + + return 0; +} + +/* + * put the newest revision to the first position + */ +void +lyp_sort_revisions(struct lys_module *module) +{ + uint8_t i, r; + struct lys_revision rev; + + for (i = 1, r = 0; i < module->rev_size; i++) { + if (strcmp(module->rev[i].date, module->rev[r].date) > 0) { + r = i; + } + } + + if (r) { + /* the newest revision is not on position 0, switch them */ + memcpy(&rev, &module->rev[0], sizeof rev); + memcpy(&module->rev[0], &module->rev[r], sizeof rev); + memcpy(&module->rev[r], &rev, sizeof rev); + } +} + +void +lyp_ext_instance_rm(struct ly_ctx *ctx, struct lys_ext_instance ***ext, uint8_t *size, uint8_t index) +{ + uint8_t i; + + lys_extension_instances_free(ctx, (*ext)[index]->ext, (*ext)[index]->ext_size, NULL); + lydict_remove(ctx, (*ext)[index]->arg_value); + free((*ext)[index]); + + /* move the rest of the array */ + for (i = index + 1; i < (*size); i++) { + (*ext)[i - 1] = (*ext)[i]; + } + /* clean the last cell in the array structure */ + (*ext)[(*size) - 1] = NULL; + /* the array is not reallocated here, just change its size */ + (*size) = (*size) - 1; + + if (!(*size)) { + /* ext array is empty */ + free((*ext)); + ext = NULL; + } +} + +static int +lyp_rfn_apply_ext_(struct lys_refine *rfn, struct lys_node *target, LYEXT_SUBSTMT substmt, struct lys_ext *extdef) +{ + struct ly_ctx *ctx; + int m, n; + struct lys_ext_instance *new; + void *reallocated; + + ctx = target->module->ctx; /* shortcut */ + + m = n = -1; + while ((m = lys_ext_iter(rfn->ext, rfn->ext_size, m + 1, substmt)) != -1) { + /* refine's substatement includes extensions, copy them to the target, replacing the previous + * substatement's extensions if any. In case of refining the extension itself, we are going to + * replace only the same extension (pointing to the same definition) */ + if (substmt == LYEXT_SUBSTMT_SELF && rfn->ext[m]->def != extdef) { + continue; + } + + /* get the index of the extension to replace in the target node */ + do { + n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); + } while (n != -1 && substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef); + + /* TODO cover complex extension instances */ + if (n == -1) { + /* nothing to replace, we are going to add it - reallocate */ + new = malloc(sizeof **target->ext); + LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); + reallocated = realloc(target->ext, (target->ext_size + 1) * sizeof *target->ext); + LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx); free(new), EXIT_FAILURE); + target->ext = reallocated; + target->ext_size++; + + /* init */ + n = target->ext_size - 1; + target->ext[n] = new; + target->ext[n]->parent = target; + target->ext[n]->parent_type = LYEXT_PAR_NODE; + target->ext[n]->flags = 0; + target->ext[n]->insubstmt = substmt; + target->ext[n]->priv = NULL; + target->ext[n]->nodetype = LYS_EXT; + target->ext[n]->module = target->module; + } else { + /* replacing - first remove the allocated data from target */ + lys_extension_instances_free(ctx, target->ext[n]->ext, target->ext[n]->ext_size, NULL); + lydict_remove(ctx, target->ext[n]->arg_value); + } + /* common part for adding and replacing */ + target->ext[n]->def = rfn->ext[m]->def; + /* parent and parent_type do not change */ + target->ext[n]->arg_value = lydict_insert(ctx, rfn->ext[m]->arg_value, 0); + /* flags do not change */ + target->ext[n]->ext_size = rfn->ext[m]->ext_size; + lys_ext_dup(ctx, target->module, rfn->ext[m]->ext, rfn->ext[m]->ext_size, target, LYEXT_PAR_NODE, + &target->ext[n]->ext, 0, NULL); + /* substmt does not change, but the index must be taken from the refine */ + target->ext[n]->insubstmt_index = rfn->ext[m]->insubstmt_index; + } + + /* remove the rest of extensions belonging to the original substatement in the target node */ + while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt)) != -1) { + if (substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef) { + /* keep this extension */ + continue; + } + + /* remove the item */ + lyp_ext_instance_rm(ctx, &target->ext, &target->ext_size, n); + --n; + } + + return EXIT_SUCCESS; +} + +/* + * apply extension instances defined under refine's substatements. + * It cannot be done immediately when applying the refine because there can be + * still unresolved data (e.g. type) and mainly the targeted extension instances. + */ +int +lyp_rfn_apply_ext(struct lys_module *module) +{ + int i, k, a = 0; + struct lys_node *root, *nextroot, *next, *node; + struct lys_node *target; + struct lys_node_uses *uses; + struct lys_refine *rfn; + struct ly_set *extset; + + /* refines in uses */ + LY_TREE_FOR_SAFE(module->data, nextroot, root) { + /* go through the data tree of the module and all the defined augments */ + + LY_TREE_DFS_BEGIN(root, next, node) { + if (node->nodetype == LYS_USES) { + uses = (struct lys_node_uses *)node; + + for (i = 0; i < uses->refine_size; i++) { + if (!uses->refine[i].ext_size) { + /* no extensions in refine */ + continue; + } + rfn = &uses->refine[i]; /* shortcut */ + + /* get the target node */ + target = NULL; + resolve_descendant_schema_nodeid(rfn->target_name, uses->child, + LYS_NO_RPC_NOTIF_NODE | LYS_ACTION | LYS_NOTIF, + 0, (const struct lys_node **)&target); + if (!target) { + /* it should always succeed since the target_name was already resolved at least + * once when the refine itself was being resolved */ + LOGINT(module->ctx);; + return EXIT_FAILURE; + } + + /* extensions */ + extset = ly_set_new(); + k = -1; + while ((k = lys_ext_iter(rfn->ext, rfn->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { + ly_set_add(extset, rfn->ext[k]->def, 0); + } + for (k = 0; (unsigned int)k < extset->number; k++) { + if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_SELF, (struct lys_ext *)extset->set.g[k])) { + ly_set_free(extset); + return EXIT_FAILURE; + } + } + ly_set_free(extset); + + /* description */ + if (rfn->dsc && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_DESCRIPTION, NULL)) { + return EXIT_FAILURE; + } + /* reference */ + if (rfn->ref && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_REFERENCE, NULL)) { + return EXIT_FAILURE; + } + /* config, in case of notification or rpc/action{notif, the config is not applicable + * (there is no config status) */ + if ((rfn->flags & LYS_CONFIG_MASK) && (target->flags & LYS_CONFIG_MASK)) { + if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_CONFIG, NULL)) { + return EXIT_FAILURE; + } + } + /* default value */ + if (rfn->dflt_size && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_DEFAULT, NULL)) { + return EXIT_FAILURE; + } + /* mandatory */ + if (rfn->flags & LYS_MAND_MASK) { + if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MANDATORY, NULL)) { + return EXIT_FAILURE; + } + } + /* presence */ + if ((target->nodetype & LYS_CONTAINER) && rfn->mod.presence) { + if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_PRESENCE, NULL)) { + return EXIT_FAILURE; + } + } + /* min/max */ + if (rfn->flags & LYS_RFN_MINSET) { + if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MIN, NULL)) { + return EXIT_FAILURE; + } + } + if (rfn->flags & LYS_RFN_MAXSET) { + if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MAX, NULL)) { + return EXIT_FAILURE; + } + } + /* must and if-feature contain extensions on their own, not needed to be solved here */ + + if (target->ext_size) { + /* the allocated target's extension array can be now longer than needed in case + * there is less refine substatement's extensions than in original. Since we are + * going to reduce or keep the same memory, it is not necessary to test realloc's result */ + target->ext = realloc(target->ext, target->ext_size * sizeof *target->ext); + } + } + } + LY_TREE_DFS_END(root, next, node) + } + + if (!nextroot && a < module->augment_size) { + nextroot = module->augment[a].child; + a++; + } + } + + return EXIT_SUCCESS; +} + +/* + * check mandatory substatements defined under extension instances. + */ +int +lyp_mand_check_ext(struct lys_ext_instance_complex *ext, const char *ext_name) +{ + void *p; + int i; + struct ly_ctx *ctx = ext->module->ctx; + + /* check for mandatory substatements */ + for (i = 0; ext->substmt[i].stmt; i++) { + if (ext->substmt[i].cardinality == LY_STMT_CARD_OPT || ext->substmt[i].cardinality == LY_STMT_CARD_ANY) { + /* not a mandatory */ + continue; + } else if (ext->substmt[i].cardinality == LY_STMT_CARD_SOME) { + goto array; + } + + /* + * LY_STMT_ORDEREDBY - not checked, has a default value which is the same as explicit system order + * LY_STMT_MODIFIER, LY_STMT_STATUS, LY_STMT_MANDATORY, LY_STMT_CONFIG - checked, but mandatory requirement + * does not make sense since there is also a default value specified + */ + switch(ext->substmt[i].stmt) { + case LY_STMT_ORDEREDBY: + /* always ok */ + break; + case LY_STMT_REQINSTANCE: + case LY_STMT_DIGITS: + case LY_STMT_MODIFIER: + p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); + if (!*(uint8_t*)p) { + LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); + goto error; + } + break; + case LY_STMT_STATUS: + p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); + if (!(*(uint16_t*)p & LYS_STATUS_MASK)) { + LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); + goto error; + } + break; + case LY_STMT_MANDATORY: + p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); + if (!(*(uint16_t*)p & LYS_MAND_MASK)) { + LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); + goto error; + } + break; + case LY_STMT_CONFIG: + p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); + if (!(*(uint16_t*)p & LYS_CONFIG_MASK)) { + LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); + goto error; + } + break; + default: +array: + /* stored as a pointer */ + p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); + if (!(*(void**)p)) { + LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); + goto error; + } + break; + } + } + + return EXIT_SUCCESS; + +error: + return EXIT_FAILURE; +} + +static int +lyp_deviate_del_ext(struct lys_node *target, struct lys_ext_instance *ext) +{ + int n = -1, found = 0; + char *path; + + while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, ext->insubstmt)) != -1) { + if (target->ext[n]->def != ext->def) { + continue; + } + + if (ext->def->argument) { + /* check matching arguments */ + if (!ly_strequal(target->ext[n]->arg_value, ext->arg_value, 1)) { + continue; + } + } + + /* we have the matching extension - remove it */ + ++found; + lyp_ext_instance_rm(target->module->ctx, &target->ext, &target->ext_size, n); + --n; + } + + if (!found) { + path = lys_path(target, LYS_PATH_FIRST_PREFIX); + LOGERR(target->module->ctx, LY_EVALID, "Extension deviation: extension \"%s\" to delete not found in \"%s\".", + ext->def->name, path) + free(path); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +static int +lyp_deviate_apply_ext(struct lys_deviate *dev, struct lys_node *target, LYEXT_SUBSTMT substmt, struct lys_ext *extdef) +{ + struct ly_ctx *ctx; + int m, n; + struct lys_ext_instance *new; + void *reallocated; + + /* LY_DEVIATE_ADD and LY_DEVIATE_RPL are very similar so they are implement the same way - in replacing, + * there can be some extension instances in the target, in case of adding, there should not be any so we + * will be just adding. */ + + ctx = target->module->ctx; /* shortcut */ + m = n = -1; + + while ((m = lys_ext_iter(dev->ext, dev->ext_size, m + 1, substmt)) != -1) { + /* deviate and its substatements include extensions, copy them to the target, replacing the previous + * extensions if any. In case of deviating extension itself, we have to deviate only the same type + * of the extension as specified in the deviation */ + if (substmt == LYEXT_SUBSTMT_SELF && dev->ext[m]->def != extdef) { + continue; + } + + if (substmt == LYEXT_SUBSTMT_SELF && dev->mod == LY_DEVIATE_ADD) { + /* in case of adding extension, we will be replacing only the inherited extensions */ + do { + n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); + } while (n != -1 && (target->ext[n]->def != extdef || !(target->ext[n]->flags & LYEXT_OPT_INHERIT))); + } else { + /* get the index of the extension to replace in the target node */ + do { + n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); + /* if we are applying extension deviation, we have to deviate only the same type of the extension */ + } while (n != -1 && substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef); + } + + if (n == -1) { + /* nothing to replace, we are going to add it - reallocate */ + new = malloc(sizeof **target->ext); + LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); + reallocated = realloc(target->ext, (target->ext_size + 1) * sizeof *target->ext); + LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx); free(new), EXIT_FAILURE); + target->ext = reallocated; + target->ext_size++; + + n = target->ext_size - 1; + } else { + /* replacing - the original set of extensions is actually backuped together with the + * node itself, so we are supposed only to free the allocated data here ... */ + lys_extension_instances_free(ctx, target->ext[n]->ext, target->ext[n]->ext_size, NULL); + lydict_remove(ctx, target->ext[n]->arg_value); + free(target->ext[n]); + + /* and prepare the new structure */ + new = malloc(sizeof **target->ext); + LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); + } + /* common part for adding and replacing - fill the newly created / replaced cell */ + target->ext[n] = new; + target->ext[n]->def = dev->ext[m]->def; + target->ext[n]->arg_value = lydict_insert(ctx, dev->ext[m]->arg_value, 0); + target->ext[n]->flags = 0; + target->ext[n]->parent = target; + target->ext[n]->parent_type = LYEXT_PAR_NODE; + target->ext[n]->insubstmt = substmt; + target->ext[n]->insubstmt_index = dev->ext[m]->insubstmt_index; + target->ext[n]->ext_size = dev->ext[m]->ext_size; + lys_ext_dup(ctx, target->module, dev->ext[m]->ext, dev->ext[m]->ext_size, target, LYEXT_PAR_NODE, + &target->ext[n]->ext, 1, NULL); + target->ext[n]->nodetype = LYS_EXT; + target->ext[n]->module = target->module; + target->ext[n]->priv = NULL; + + /* TODO cover complex extension instances */ + } + + /* remove the rest of extensions belonging to the original substatement in the target node, + * due to possible reverting of the deviation effect, they are actually not removed, just moved + * to the backup of the original node when the original node is backuped, here we just have to + * free the replaced / deleted originals */ + while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt)) != -1) { + if (substmt == LYEXT_SUBSTMT_SELF) { + /* if we are applying extension deviation, we are going to remove only + * - the same type of the extension in case of replacing + * - the same type of the extension which was inherited in case of adding + * note - delete deviation is covered in lyp_deviate_del_ext */ + if (target->ext[n]->def != extdef || + (dev->mod == LY_DEVIATE_ADD && !(target->ext[n]->flags & LYEXT_OPT_INHERIT))) { + /* keep this extension */ + continue; + } + + } + + /* remove the item */ + lyp_ext_instance_rm(ctx, &target->ext, &target->ext_size, n); + --n; + } + + return EXIT_SUCCESS; +} + +/* + * not-supported deviations are not processed since they affect the complete node, not just their substatements + */ +int +lyp_deviation_apply_ext(struct lys_module *module) +{ + int i, j, k; + struct lys_deviate *dev; + struct lys_node *target; + struct ly_set *extset; + + for (i = 0; i < module->deviation_size; i++) { + target = NULL; + extset = NULL; + j = resolve_schema_nodeid(module->deviation[i].target_name, NULL, module, &extset, 0, 0); + if (j == -1) { + return EXIT_FAILURE; + } else if (!extset) { + /* LY_DEVIATE_NO */ + ly_set_free(extset); + continue; + } + target = extset->set.s[0]; + ly_set_free(extset); + + for (j = 0; j < module->deviation[i].deviate_size; j++) { + dev = &module->deviation[i].deviate[j]; + if (!dev->ext_size) { + /* no extensions in deviate and its substatement, nothing to do here */ + continue; + } + + /* extensions */ + if (dev->mod == LY_DEVIATE_DEL) { + k = -1; + while ((k = lys_ext_iter(dev->ext, dev->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { + if (lyp_deviate_del_ext(target, dev->ext[k])) { + return EXIT_FAILURE; + } + } + + /* In case of LY_DEVIATE_DEL, we are applying only extension deviation, removing + * of the substatement's extensions was already done when the substatement was applied. + * Extension deviation could not be applied by the parser since the extension could be unresolved, + * which is not the issue of the other substatements. */ + continue; + } else { + extset = ly_set_new(); + k = -1; + while ((k = lys_ext_iter(dev->ext, dev->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { + ly_set_add(extset, dev->ext[k]->def, 0); + } + for (k = 0; (unsigned int)k < extset->number; k++) { + if (lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_SELF, (struct lys_ext *)extset->set.g[k])) { + ly_set_free(extset); + return EXIT_FAILURE; + } + } + ly_set_free(extset); + } + + /* unique */ + if (dev->unique_size && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_UNIQUE, NULL)) { + return EXIT_FAILURE; + } + /* units */ + if (dev->units && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_UNITS, NULL)) { + return EXIT_FAILURE; + } + /* default */ + if (dev->dflt_size && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_DEFAULT, NULL)) { + return EXIT_FAILURE; + } + /* config */ + if ((dev->flags & LYS_CONFIG_MASK) && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_CONFIG, NULL)) { + return EXIT_FAILURE; + } + /* mandatory */ + if ((dev->flags & LYS_MAND_MASK) && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MANDATORY, NULL)) { + return EXIT_FAILURE; + } + /* min/max */ + if (dev->min_set && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MIN, NULL)) { + return EXIT_FAILURE; + } + if (dev->min_set && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MAX, NULL)) { + return EXIT_FAILURE; + } + /* type and must contain extension instances in their structures */ + } + } + + return EXIT_SUCCESS; +} + +int +lyp_ctx_check_module(struct lys_module *module) +{ + struct ly_ctx *ctx; + int i, match_i = -1, to_implement; + const char *last_rev = NULL; + + assert(module); + to_implement = 0; + ctx = module->ctx; + + /* find latest revision */ + for (i = 0; i < module->rev_size; ++i) { + if (!last_rev || (strcmp(last_rev, module->rev[i].date) < 0)) { + last_rev = module->rev[i].date; + } + } + + for (i = 0; i < ctx->models.used; i++) { + /* check name (name/revision) and namespace uniqueness */ + if (!strcmp(ctx->models.list[i]->name, module->name)) { + if (to_implement) { + if (i == match_i) { + continue; + } + LOGERR(ctx, LY_EINVAL, "Module \"%s@%s\" in another revision \"%s\" already implemented.", + module->name, last_rev ? last_rev : "", ctx->models.list[i]->rev[0].date); + return -1; + } else if (!ctx->models.list[i]->rev_size && module->rev_size) { + LOGERR(ctx, LY_EINVAL, "Module \"%s\" without revision already in context.", module->name); + return -1; + } else if (ctx->models.list[i]->rev_size && !module->rev_size) { + LOGERR(ctx, LY_EINVAL, "Module \"%s\" with revision \"%s\" already in context.", + module->name, ctx->models.list[i]->rev[0].date); + return -1; + } else if ((!module->rev_size && !ctx->models.list[i]->rev_size) + || !strcmp(ctx->models.list[i]->rev[0].date, last_rev)) { + + LOGVRB("Module \"%s@%s\" already in context.", module->name, last_rev ? last_rev : ""); + + /* if disabled, enable first */ + if (ctx->models.list[i]->disabled) { + lys_set_enabled(ctx->models.list[i]); + } + + to_implement = module->implemented; + match_i = i; + if (to_implement && !ctx->models.list[i]->implemented) { + /* check first that it is okay to change it to implemented */ + i = -1; + continue; + } + return 1; + + } else if (module->implemented && ctx->models.list[i]->implemented) { + LOGERR(ctx, LY_EINVAL, "Module \"%s@%s\" in another revision \"%s\" already implemented.", + module->name, last_rev ? last_rev : "", ctx->models.list[i]->rev[0].date); + return -1; + } + /* else keep searching, for now the caller is just adding + * another revision of an already present schema + */ + } else if (!strcmp(ctx->models.list[i]->ns, module->ns)) { + LOGERR(ctx, LY_EINVAL, "Two different modules (\"%s\" and \"%s\") have the same namespace \"%s\".", + ctx->models.list[i]->name, module->name, module->ns); + return -1; + } + } + + if (to_implement) { + if (lys_set_implemented(ctx->models.list[match_i])) { + return -1; + } + return 1; + } + + return 0; +} + +int +lyp_ctx_add_module(struct lys_module *module) +{ + struct lys_module **newlist = NULL; + int i; + + assert(!lyp_ctx_check_module(module)); + +#ifndef NDEBUG + int j; + /* check that all augments are resolved */ + for (i = 0; i < module->augment_size; ++i) { + assert(module->augment[i].target); + } + for (i = 0; i < module->inc_size; ++i) { + for (j = 0; j < module->inc[i].submodule->augment_size; ++j) { + assert(module->inc[i].submodule->augment[j].target); + } + } +#endif + + /* add to the context's list of modules */ + if (module->ctx->models.used == module->ctx->models.size) { + newlist = realloc(module->ctx->models.list, (2 * module->ctx->models.size) * sizeof *newlist); + LY_CHECK_ERR_RETURN(!newlist, LOGMEM(module->ctx), -1); + for (i = module->ctx->models.size; i < module->ctx->models.size * 2; i++) { + newlist[i] = NULL; + } + module->ctx->models.size *= 2; + module->ctx->models.list = newlist; + } + module->ctx->models.list[module->ctx->models.used++] = module; + module->ctx->models.module_set_id++; + + return 0; +} + +/** + * Store UTF-8 character specified as 4byte integer into the dst buffer. + * Returns number of written bytes (4 max), expects that dst has enough space. + * + * UTF-8 mapping: + * 00000000 -- 0000007F: 0xxxxxxx + * 00000080 -- 000007FF: 110xxxxx 10xxxxxx + * 00000800 -- 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx + * 00010000 -- 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * + * Includes checking for valid characters (following RFC 7950, sec 9.4) + */ +unsigned int +pututf8(struct ly_ctx *ctx, char *dst, int32_t value) +{ + if (value < 0x80) { + /* one byte character */ + if (value < 0x20 && + value != 0x09 && + value != 0x0a && + value != 0x0d) { + goto error; + } + + dst[0] = value; + return 1; + } else if (value < 0x800) { + /* two bytes character */ + dst[0] = 0xc0 | (value >> 6); + dst[1] = 0x80 | (value & 0x3f); + return 2; + } else if (value < 0xfffe) { + /* three bytes character */ + if (((value & 0xf800) == 0xd800) || + (value >= 0xfdd0 && value <= 0xfdef)) { + /* exclude surrogate blocks %xD800-DFFF */ + /* exclude noncharacters %xFDD0-FDEF */ + goto error; + } + + dst[0] = 0xe0 | (value >> 12); + dst[1] = 0x80 | ((value >> 6) & 0x3f); + dst[2] = 0x80 | (value & 0x3f); + + return 3; + } else if (value < 0x10fffe) { + if ((value & 0xffe) == 0xffe) { + /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF, + * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF, + * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */ + goto error; + } + /* four bytes character */ + dst[0] = 0xf0 | (value >> 18); + dst[1] = 0x80 | ((value >> 12) & 0x3f); + dst[2] = 0x80 | ((value >> 6) & 0x3f); + dst[3] = 0x80 | (value & 0x3f); + + return 4; + } + +error: + /* out of range */ + LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, NULL); + LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); + return 0; +} + +unsigned int +copyutf8(struct ly_ctx *ctx, char *dst, const char *src) +{ + uint32_t value; + + /* unicode characters */ + if (!(src[0] & 0x80)) { + /* one byte character */ + if (src[0] < 0x20 && + src[0] != 0x09 && + src[0] != 0x0a && + src[0] != 0x0d) { + LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); + LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%02x", src[0]); + return 0; + } + + dst[0] = src[0]; + return 1; + } else if (!(src[0] & 0x20)) { + /* two bytes character */ + dst[0] = src[0]; + dst[1] = src[1]; + return 2; + } else if (!(src[0] & 0x10)) { + /* three bytes character */ + value = ((uint32_t)(src[0] & 0xf) << 12) | ((uint32_t)(src[1] & 0x3f) << 6) | (src[2] & 0x3f); + if (((value & 0xf800) == 0xd800) || + (value >= 0xfdd0 && value <= 0xfdef) || + (value & 0xffe) == 0xffe) { + /* exclude surrogate blocks %xD800-DFFF */ + /* exclude noncharacters %xFDD0-FDEF */ + /* exclude noncharacters %xFFFE-FFFF */ + LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); + LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); + return 0; + } + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + return 3; + } else if (!(src[0] & 0x08)) { + /* four bytes character */ + value = ((uint32_t)(src[0] & 0x7) << 18) | ((uint32_t)(src[1] & 0x3f) << 12) | ((uint32_t)(src[2] & 0x3f) << 6) | (src[3] & 0x3f); + if ((value & 0xffe) == 0xffe) { + /* exclude noncharacters %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF, + * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF, + * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */ + LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); + LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); + return 0; + } + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + return 4; + } else { + LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); + LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 leading byte 0x%02x", src[0]); + return 0; + } +} + +const struct lys_module * +lyp_get_module(const struct lys_module *module, const char *prefix, int pref_len, const char *name, int name_len, int in_data) +{ + const struct lys_module *main_module; + char *str; + int i; + + assert(!prefix || !name); + + if (prefix && !pref_len) { + pref_len = strlen(prefix); + } + if (name && !name_len) { + name_len = strlen(name); + } + + main_module = lys_main_module(module); + + /* module own prefix, submodule own prefix, (sub)module own name */ + if ((!prefix || (!module->type && !strncmp(main_module->prefix, prefix, pref_len) && !main_module->prefix[pref_len]) + || (module->type && !strncmp(module->prefix, prefix, pref_len) && !module->prefix[pref_len])) + && (!name || (!strncmp(main_module->name, name, name_len) && !main_module->name[name_len]))) { + return main_module; + } + + /* standard import */ + for (i = 0; i < module->imp_size; ++i) { + if ((!prefix || (!strncmp(module->imp[i].prefix, prefix, pref_len) && !module->imp[i].prefix[pref_len])) + && (!name || (!strncmp(module->imp[i].module->name, name, name_len) && !module->imp[i].module->name[name_len]))) { + return module->imp[i].module; + } + } + + /* module required by a foreign grouping, deviation, or submodule */ + if (name) { + str = strndup(name, name_len); + if (!str) { + LOGMEM(module->ctx); + return NULL; + } + main_module = ly_ctx_get_module(module->ctx, str, NULL, 0); + + /* try data callback */ + if (!main_module && in_data && module->ctx->data_clb) { + main_module = module->ctx->data_clb(module->ctx, str, NULL, 0, module->ctx->data_clb_data); + } + + free(str); + return main_module; + } + + return NULL; +} + +const struct lys_module * +lyp_get_import_module_ns(const struct lys_module *module, const char *ns) +{ + int i; + const struct lys_module *mod = NULL; + + assert(module && ns); + + if (module->type) { + /* the module is actually submodule and to get the namespace, we need the main module */ + if (ly_strequal(((struct lys_submodule *)module)->belongsto->ns, ns, 0)) { + return ((struct lys_submodule *)module)->belongsto; + } + } else { + /* module's own namespace */ + if (ly_strequal(module->ns, ns, 0)) { + return module; + } + } + + /* imported modules */ + for (i = 0; i < module->imp_size; ++i) { + if (ly_strequal(module->imp[i].module->ns, ns, 0)) { + return module->imp[i].module; + } + } + + return mod; +} + +const char * +lyp_get_yang_data_template_name(const struct lyd_node *node) +{ + struct lys_node *snode; + + snode = lys_parent(node->schema); + while (snode && snode->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE)) { + snode = lys_parent(snode); + } + + if (snode && snode->nodetype == LYS_EXT && strcmp(((struct lys_ext_instance_complex *)snode)->def->name, "yang-data") == 0) { + return ((struct lys_ext_instance_complex *)snode)->arg_value; + } else { + return NULL; + } +} + +const struct lys_node * +lyp_get_yang_data_template(const struct lys_module *module, const char *yang_data_name, int yang_data_name_len) +{ + int i, j; + const struct lys_node *ret = NULL; + const struct lys_submodule *submodule; + + for(i = 0; i < module->ext_size; ++i) { + if (!strcmp(module->ext[i]->def->name, "yang-data") && !strncmp(module->ext[i]->arg_value, yang_data_name, yang_data_name_len) + && !module->ext[i]->arg_value[yang_data_name_len]) { + ret = (struct lys_node *)module->ext[i]; + break; + } + } + + for(j = 0; !ret && j < module->inc_size; ++j) { + submodule = module->inc[j].submodule; + for(i = 0; i < submodule->ext_size; ++i) { + if (!strcmp(submodule->ext[i]->def->name, "yang-data") && !strncmp(submodule->ext[i]->arg_value, yang_data_name, yang_data_name_len) + && !submodule->ext[i]->arg_value[yang_data_name_len]) { + ret = (struct lys_node *)submodule->ext[i]; + break; + } + } + } + + return ret; +}