123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2020 HPCC Systems®.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ############################################################################## */
- #include "jstring.hpp"
- #include "jdebug.hpp"
- #include "jptree.hpp"
- #include "jexcept.hpp"
- #include "jlog.hpp"
- #include <libxml/xmlmemory.h>
- #include <libxml/parserInternals.h>
- #include <libxml/debugXML.h>
- #include <libxml/HTMLtree.h>
- #include <libxml/xmlIO.h>
- #include <libxml/xinclude.h>
- #include <libxml/catalog.h>
- #include <libxml/xpathInternals.h>
- #include <libxml/xpath.h>
- #include <libxml/xmlschemas.h>
- #include <libxml/hash.h>
- #include "xpathprocessor.hpp"
- #include "xmlerror.hpp"
- #include "espcontext.hpp"
- #include "esdl_script.hpp"
- //only support libxml2 for now
- void addFeaturesToAccessMap(MapStringTo<SecAccessFlags> &accessmap, const char *s)
- {
- StringArray entries;
- entries.appendList(s, ",");
- ForEachItemIn(i, entries)
- {
- StringArray pair;
- pair.appendList(entries.item(i), ":");
- if (pair.length()==0)
- continue;
- if (pair.length()==1)
- accessmap.setValue(pair.item(0), SecAccess_Read);
- else
- {
- SecAccessFlags required = getSecAccessFlagValue(pair.item(1));
- if (required >= SecAccess_None)
- accessmap.setValue(pair.item(0), required);
- }
- }
- }
- inline IEsdlScriptContext *getEsdlScriptContext(xmlXPathParserContextPtr ctxt)
- {
- if (!ctxt || !ctxt->context || !ctxt->context->userData)
- return nullptr;
- return reinterpret_cast<IEsdlScriptContext *>(ctxt->context->userData);
- }
- inline IEspContext *getEspContext(xmlXPathParserContextPtr ctxt)
- {
- IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
- if (!scriptContext || !scriptContext->queryEspContext())
- return nullptr;
- return reinterpret_cast<IEspContext *>(scriptContext->queryEspContext());
- }
- /**
- * validateFeaturesAccessFunction:
- * @ctxt: an XPath parser context
- * @nargs: the number of arguments
- *
- * Wraps IEspContext::validateFeaturesAccess()
- */
- static void validateFeaturesAccessFunction (xmlXPathParserContextPtr ctxt, int nargs)
- {
- IEspContext *espContext = getEspContext(ctxt);
- if (!espContext)
- {
- xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
- return;
- }
- if (nargs != 1)
- {
- xmlXPathSetArityError(ctxt);
- return;
- }
- xmlChar *authstring = xmlXPathPopString(ctxt);
- if (xmlXPathCheckError(ctxt)) //includes null check
- return;
- MapStringTo<SecAccessFlags> accessmap;
- addFeaturesToAccessMap(accessmap, (const char *)authstring);
- bool ok = true;
- if (accessmap.ordinality()!=0)
- ok = espContext->validateFeaturesAccess(accessmap, false);
- if (authstring != nullptr)
- xmlFree(authstring);
- xmlXPathReturnBoolean(ctxt, ok ? 1 : 0);
- }
- /**
- * evaluateSecAccessFlagsFunction
- * @ctxt: an XPath parser context
- * @nargs: the number of arguments
- *
- */
- static void secureAccessFlagsFunction (xmlXPathParserContextPtr ctxt, int nargs)
- {
- IEspContext *espContext = getEspContext(ctxt);
- if (!espContext)
- {
- xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
- return;
- }
- if (nargs == 0)
- {
- xmlXPathSetArityError(ctxt);
- return;
- }
- unsigned flags = 0;
- while(nargs--)
- {
- xmlChar *s = xmlXPathPopString(ctxt);
- if (xmlXPathCheckError(ctxt)) //includes null check
- return;
- SecAccessFlags f = getSecAccessFlagValue((const char *)s);
- xmlFree(s);
- if (f < SecAccess_None)
- {
- xmlXPathSetArityError(ctxt);
- return;
- }
- flags |= (unsigned)f;
- }
- xmlXPathReturnNumber(ctxt, flags);
- }
- /**
- * getFeatureSecAccessFlags
- * @ctxt: an XPath parser context
- * @nargs: the number of arguments
- *
- */
- static void getFeatureSecAccessFlagsFunction (xmlXPathParserContextPtr ctxt, int nargs)
- {
- IEspContext *espContext = getEspContext(ctxt);
- if (!espContext)
- {
- xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
- return;
- }
- if (nargs != 1)
- {
- xmlXPathSetArityError(ctxt);
- return;
- }
- xmlChar *authstring = xmlXPathPopString(ctxt);
- if (xmlXPathCheckError(ctxt)) //includes null check
- return;
- SecAccessFlags access = SecAccess_None;
- espContext->authorizeFeature((const char *)authstring, access);
- xmlFree(authstring);
- xmlXPathReturnNumber(ctxt, access);
- }
- /**
- * getStoredStringValueFunction
- * @ctxt: an XPath parser context
- * @nargs: the number of arguments
- *
- */
- static void getStoredStringValueFunction (xmlXPathParserContextPtr ctxt, int nargs)
- {
- IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
- if (!scriptContext)
- {
- xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
- return;
- }
- if (nargs != 1)
- {
- xmlXPathSetArityError(ctxt);
- return;
- }
- xmlChar *namestring = xmlXPathPopString(ctxt);
- if (xmlXPathCheckError(ctxt)) //includes null check
- return;
- const char *value = scriptContext->queryAttribute(ESDLScriptCtxSection_Store, (const char *)namestring);
- xmlFree(namestring);
- if (!value)
- xmlXPathReturnEmptyString(ctxt);
- else
- xmlXPathReturnString(ctxt, xmlStrdup((const xmlChar *)value));
- }
- /**
- * scriptGetDataSectionFunctionImpl
- * @ctxt: an XPath parser context
- * @nargs: the number of arguments
- *
- */
- static void scriptGetDataSectionFunctionImpl (xmlXPathParserContextPtr ctxt, int nargs, bool ensure)
- {
- IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
- if (!scriptContext)
- {
- xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
- return;
- }
- if (nargs != 1)
- {
- xmlXPathSetArityError(ctxt);
- return;
- }
- xmlChar *namestring = xmlXPathPopString(ctxt);
- if (xmlXPathCheckError(ctxt)) //includes null check
- return;
- const char *sectionName = isEmptyString((const char *) namestring) ? "temporaries" : (const char *) namestring;
- if (ensure)
- scriptContext->appendContent(sectionName, nullptr, nullptr);
- StringBuffer xpath("/esdl_script_context/");
- xpath.append((const char *) sectionName);
- xmlFree(namestring);
- xmlXPathObjectPtr ret = xmlXPathEval((const xmlChar *) xpath.str(), ctxt->context);
- if (ret)
- valuePush(ctxt, ret);
- else
- xmlXPathReturnEmptyNodeSet(ctxt);
- }
- /**
- * scriptEnsureDataSectionFunction
- * @ctxt: an XPath parser context
- * @nargs: the number of arguments
- *
- */
- static void scriptEnsureDataSectionFunction (xmlXPathParserContextPtr ctxt, int nargs)
- {
- scriptGetDataSectionFunctionImpl (ctxt, nargs, true);
- }
- /**
- * scriptGetDataSectionFunction
- * @ctxt: an XPath parser context
- * @nargs: the number of arguments
- *
- */
- static void scriptGetDataSectionFunction (xmlXPathParserContextPtr ctxt, int nargs)
- {
- scriptGetDataSectionFunctionImpl (ctxt, nargs, false);
- }
- /**
- * getLogOptionFunction
- * @ctxt: an XPath parser context
- * @nargs: the number of arguments
- *
- */
- static void getLogOptionFunction (xmlXPathParserContextPtr ctxt, int nargs)
- {
- IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
- if (!scriptContext)
- {
- xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
- return;
- }
- if (nargs != 1)
- {
- xmlXPathSetArityError(ctxt);
- return;
- }
- xmlChar *namestring = xmlXPathPopString(ctxt);
- if (xmlXPathCheckError(ctxt)) //includes null check
- return;
- const char *value = scriptContext->queryAttribute(ESDLScriptCtxSection_Logging, (const char *)namestring);
- xmlFree(namestring);
- if (!value)
- xmlXPathReturnEmptyString(ctxt);
- else
- xmlXPathReturnString(ctxt, xmlStrdup((const xmlChar *)value));
- }
- /**
- * getLogProfileFunction
- * @ctxt: an XPath parser context
- * @nargs: the number of arguments
- *
- */
- static void getLogProfileFunction (xmlXPathParserContextPtr ctxt, int nargs)
- {
- IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
- if (!scriptContext)
- {
- xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
- return;
- }
- if (nargs != 0)
- {
- xmlXPathSetArityError(ctxt);
- return;
- }
- const char *value = scriptContext->queryAttribute(ESDLScriptCtxSection_Logging, "profile");
- if (!value)
- xmlXPathReturnEmptyString(ctxt);
- else
- xmlXPathReturnString(ctxt, xmlStrdup((const xmlChar *)value));
- }
- /**
- * logOptionExistsFunction
- * @ctxt: an XPath parser context
- * @nargs: the number of arguments
- *
- */
- static void logOptionExistsFunction (xmlXPathParserContextPtr ctxt, int nargs)
- {
- IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
- if (!scriptContext)
- {
- xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
- return;
- }
- if (nargs != 1)
- {
- xmlXPathSetArityError(ctxt);
- return;
- }
- xmlChar *namestring = xmlXPathPopString(ctxt);
- if (xmlXPathCheckError(ctxt)) //includes null check
- return;
- const char *value = scriptContext->queryAttribute(ESDLScriptCtxSection_Logging, (const char *)namestring);
- xmlFree(namestring);
- xmlXPathReturnBoolean(ctxt, (!value) ? 0 : 1);
- }
- /**
- * storedValueExistsFunction
- * @ctxt: an XPath parser context
- * @nargs: the number of arguments
- *
- */
- static void storedValueExistsFunction (xmlXPathParserContextPtr ctxt, int nargs)
- {
- IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
- if (!scriptContext)
- {
- xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
- return;
- }
- if (nargs != 1)
- {
- xmlXPathSetArityError(ctxt);
- return;
- }
- xmlChar *namestring = xmlXPathPopString(ctxt);
- if (xmlXPathCheckError(ctxt)) //includes null check
- return;
- const char *value = scriptContext->queryAttribute(ESDLScriptCtxSection_Store, (const char *)namestring);
- xmlFree(namestring);
- xmlXPathReturnBoolean(ctxt, (!value) ? 0 : 1);
- }
- //esdl tokenize function will create temporaries in the root/temp section/node of the document
- //so this is not a general purpose function in that sense
- //this section should be cleared after every script runs
- //we may allow overriding the storage location in the future
- //
- static void strTokenizeFunction(xmlXPathParserContextPtr ctxt, int nargs)
- {
- IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
- if (!scriptContext)
- {
- xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
- return;
- }
- if ((nargs < 1) || (nargs > 2))
- {
- xmlXPathSetArityError(ctxt);
- return;
- }
- xmlChar *delimiters;
- if (nargs == 2)
- {
- delimiters = xmlXPathPopString(ctxt);
- if (xmlXPathCheckError(ctxt))
- return;
- }
- else
- {
- delimiters = xmlStrdup((const xmlChar *) "\t\r\n ");
- }
- if (delimiters == NULL)
- return;
- xmlChar *str = xmlXPathPopString(ctxt);
- if (xmlXPathCheckError(ctxt) || (str == NULL))
- {
- if (str)
- xmlFree(str);
- xmlFree(delimiters);
- return;
- }
- StringBuffer resultPath;
- if (!scriptContext->tokenize((const char *)str, (const char *)delimiters, resultPath))
- {
- xmlFree(str);
- xmlFree(delimiters);
- xmlXPathSetError((ctxt), XPATH_EXPR_ERROR);
- return;
- }
- xmlXPathObjectPtr ret = xmlXPathEval((const xmlChar *) resultPath.str(), ctxt->context);
- if (ret != NULL)
- valuePush(ctxt, ret);
- else
- valuePush(ctxt, xmlXPathNewNodeSet(NULL));
- xmlFree(str);
- xmlFree(delimiters);
- }
- void registerEsdlXPathExtensionsForURI(IXpathContext *xpathContext, const char *uri)
- {
- xpathContext->registerFunction(uri, "validateFeaturesAccess", (void *)validateFeaturesAccessFunction);
- xpathContext->registerFunction(uri, "secureAccessFlags", (void *)secureAccessFlagsFunction);
- xpathContext->registerFunction(uri, "getFeatureSecAccessFlags", (void *)getFeatureSecAccessFlagsFunction);
- xpathContext->registerFunction(uri, "getStoredStringValue", (void *)getStoredStringValueFunction);
- xpathContext->registerFunction(uri, "getDataSection", (void *)scriptGetDataSectionFunction);
- xpathContext->registerFunction(uri, "ensureDataSection", (void *)scriptEnsureDataSectionFunction);
- xpathContext->registerFunction(uri, "storedValueExists", (void *)storedValueExistsFunction);
- xpathContext->registerFunction(uri, "getLogProfile", (void *)getLogProfileFunction);
- xpathContext->registerFunction(uri, "getLogOption", (void *)getLogOptionFunction);
- xpathContext->registerFunction(uri, "logOptionExists", (void *)logOptionExistsFunction);
- xpathContext->registerFunction(uri, "tokenize", (void *)strTokenizeFunction);
- }
- void registerEsdlXPathExtensions(IXpathContext *xpathContext, IEsdlScriptContext *context, const StringArray &prefixes)
- {
- bool includeDefaultNS = false;
- xpathContext->setUserData(context);
- if (!prefixes.ordinality())
- xpathContext->registerNamespace("esdl", "urn:hpcc:esdl:script");
- else
- {
- ForEachItemIn(i, prefixes)
- {
- if (isEmptyString(prefixes.item(i)))
- includeDefaultNS=true;
- else
- xpathContext->registerNamespace(prefixes.item(i), "urn:hpcc:esdl:script");
- }
- }
- if (includeDefaultNS)
- registerEsdlXPathExtensionsForURI(xpathContext, nullptr);
- registerEsdlXPathExtensionsForURI(xpathContext, "urn:hpcc:esdl:script");
- }
|