esdl_xpath_extensions_libxml.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2020 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "jstring.hpp"
  14. #include "jdebug.hpp"
  15. #include "jptree.hpp"
  16. #include "jexcept.hpp"
  17. #include "jlog.hpp"
  18. #include <libxml/xmlmemory.h>
  19. #include <libxml/parserInternals.h>
  20. #include <libxml/debugXML.h>
  21. #include <libxml/HTMLtree.h>
  22. #include <libxml/xmlIO.h>
  23. #include <libxml/xinclude.h>
  24. #include <libxml/catalog.h>
  25. #include <libxml/xpathInternals.h>
  26. #include <libxml/xpath.h>
  27. #include <libxml/xmlschemas.h>
  28. #include <libxml/hash.h>
  29. #include "xpathprocessor.hpp"
  30. #include "xmlerror.hpp"
  31. #include "espcontext.hpp"
  32. #include "esdl_script.hpp"
  33. //only support libxml2 for now
  34. void addFeaturesToAccessMap(MapStringTo<SecAccessFlags> &accessmap, const char *s)
  35. {
  36. StringArray entries;
  37. entries.appendList(s, ",");
  38. ForEachItemIn(i, entries)
  39. {
  40. StringArray pair;
  41. pair.appendList(entries.item(i), ":");
  42. if (pair.length()==0)
  43. continue;
  44. if (pair.length()==1)
  45. accessmap.setValue(pair.item(0), SecAccess_Read);
  46. else
  47. {
  48. SecAccessFlags required = getSecAccessFlagValue(pair.item(1));
  49. if (required >= SecAccess_None)
  50. accessmap.setValue(pair.item(0), required);
  51. }
  52. }
  53. }
  54. inline IEsdlScriptContext *getEsdlScriptContext(xmlXPathParserContextPtr ctxt)
  55. {
  56. if (!ctxt || !ctxt->context || !ctxt->context->userData)
  57. return nullptr;
  58. return reinterpret_cast<IEsdlScriptContext *>(ctxt->context->userData);
  59. }
  60. inline IEspContext *getEspContext(xmlXPathParserContextPtr ctxt)
  61. {
  62. IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
  63. if (!scriptContext || !scriptContext->queryEspContext())
  64. return nullptr;
  65. return reinterpret_cast<IEspContext *>(scriptContext->queryEspContext());
  66. }
  67. /**
  68. * validateFeaturesAccessFunction:
  69. * @ctxt: an XPath parser context
  70. * @nargs: the number of arguments
  71. *
  72. * Wraps IEspContext::validateFeaturesAccess()
  73. */
  74. static void validateFeaturesAccessFunction (xmlXPathParserContextPtr ctxt, int nargs)
  75. {
  76. IEspContext *espContext = getEspContext(ctxt);
  77. if (!espContext)
  78. {
  79. xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
  80. return;
  81. }
  82. if (nargs != 1)
  83. {
  84. xmlXPathSetArityError(ctxt);
  85. return;
  86. }
  87. xmlChar *authstring = xmlXPathPopString(ctxt);
  88. if (xmlXPathCheckError(ctxt)) //includes null check
  89. return;
  90. MapStringTo<SecAccessFlags> accessmap;
  91. addFeaturesToAccessMap(accessmap, (const char *)authstring);
  92. bool ok = true;
  93. if (accessmap.ordinality()!=0)
  94. ok = espContext->validateFeaturesAccess(accessmap, false);
  95. if (authstring != nullptr)
  96. xmlFree(authstring);
  97. xmlXPathReturnBoolean(ctxt, ok ? 1 : 0);
  98. }
  99. /**
  100. * evaluateSecAccessFlagsFunction
  101. * @ctxt: an XPath parser context
  102. * @nargs: the number of arguments
  103. *
  104. */
  105. static void secureAccessFlagsFunction (xmlXPathParserContextPtr ctxt, int nargs)
  106. {
  107. IEspContext *espContext = getEspContext(ctxt);
  108. if (!espContext)
  109. {
  110. xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
  111. return;
  112. }
  113. if (nargs == 0)
  114. {
  115. xmlXPathSetArityError(ctxt);
  116. return;
  117. }
  118. unsigned flags = 0;
  119. while(nargs--)
  120. {
  121. xmlChar *s = xmlXPathPopString(ctxt);
  122. if (xmlXPathCheckError(ctxt)) //includes null check
  123. return;
  124. SecAccessFlags f = getSecAccessFlagValue((const char *)s);
  125. xmlFree(s);
  126. if (f < SecAccess_None)
  127. {
  128. xmlXPathSetArityError(ctxt);
  129. return;
  130. }
  131. flags |= (unsigned)f;
  132. }
  133. xmlXPathReturnNumber(ctxt, flags);
  134. }
  135. /**
  136. * getFeatureSecAccessFlags
  137. * @ctxt: an XPath parser context
  138. * @nargs: the number of arguments
  139. *
  140. */
  141. static void getFeatureSecAccessFlagsFunction (xmlXPathParserContextPtr ctxt, int nargs)
  142. {
  143. IEspContext *espContext = getEspContext(ctxt);
  144. if (!espContext)
  145. {
  146. xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
  147. return;
  148. }
  149. if (nargs != 1)
  150. {
  151. xmlXPathSetArityError(ctxt);
  152. return;
  153. }
  154. xmlChar *authstring = xmlXPathPopString(ctxt);
  155. if (xmlXPathCheckError(ctxt)) //includes null check
  156. return;
  157. SecAccessFlags access = SecAccess_None;
  158. espContext->authorizeFeature((const char *)authstring, access);
  159. xmlFree(authstring);
  160. xmlXPathReturnNumber(ctxt, access);
  161. }
  162. /**
  163. * getStoredStringValueFunction
  164. * @ctxt: an XPath parser context
  165. * @nargs: the number of arguments
  166. *
  167. */
  168. static void getStoredStringValueFunction (xmlXPathParserContextPtr ctxt, int nargs)
  169. {
  170. IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
  171. if (!scriptContext)
  172. {
  173. xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
  174. return;
  175. }
  176. if (nargs != 1)
  177. {
  178. xmlXPathSetArityError(ctxt);
  179. return;
  180. }
  181. xmlChar *namestring = xmlXPathPopString(ctxt);
  182. if (xmlXPathCheckError(ctxt)) //includes null check
  183. return;
  184. const char *value = scriptContext->queryAttribute(ESDLScriptCtxSection_Store, (const char *)namestring);
  185. xmlFree(namestring);
  186. if (!value)
  187. xmlXPathReturnEmptyString(ctxt);
  188. else
  189. xmlXPathReturnString(ctxt, xmlStrdup((const xmlChar *)value));
  190. }
  191. /**
  192. * scriptGetDataSectionFunctionImpl
  193. * @ctxt: an XPath parser context
  194. * @nargs: the number of arguments
  195. *
  196. */
  197. static void scriptGetDataSectionFunctionImpl (xmlXPathParserContextPtr ctxt, int nargs, bool ensure)
  198. {
  199. IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
  200. if (!scriptContext)
  201. {
  202. xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
  203. return;
  204. }
  205. if (nargs != 1)
  206. {
  207. xmlXPathSetArityError(ctxt);
  208. return;
  209. }
  210. xmlChar *namestring = xmlXPathPopString(ctxt);
  211. if (xmlXPathCheckError(ctxt)) //includes null check
  212. return;
  213. const char *sectionName = isEmptyString((const char *) namestring) ? "temporaries" : (const char *) namestring;
  214. if (ensure)
  215. scriptContext->appendContent(sectionName, nullptr, nullptr);
  216. StringBuffer xpath("/esdl_script_context/");
  217. xpath.append((const char *) sectionName);
  218. xmlFree(namestring);
  219. xmlXPathObjectPtr ret = xmlXPathEval((const xmlChar *) xpath.str(), ctxt->context);
  220. if (ret)
  221. valuePush(ctxt, ret);
  222. else
  223. xmlXPathReturnEmptyNodeSet(ctxt);
  224. }
  225. /**
  226. * scriptEnsureDataSectionFunction
  227. * @ctxt: an XPath parser context
  228. * @nargs: the number of arguments
  229. *
  230. */
  231. static void scriptEnsureDataSectionFunction (xmlXPathParserContextPtr ctxt, int nargs)
  232. {
  233. scriptGetDataSectionFunctionImpl (ctxt, nargs, true);
  234. }
  235. /**
  236. * scriptGetDataSectionFunction
  237. * @ctxt: an XPath parser context
  238. * @nargs: the number of arguments
  239. *
  240. */
  241. static void scriptGetDataSectionFunction (xmlXPathParserContextPtr ctxt, int nargs)
  242. {
  243. scriptGetDataSectionFunctionImpl (ctxt, nargs, false);
  244. }
  245. /**
  246. * getLogOptionFunction
  247. * @ctxt: an XPath parser context
  248. * @nargs: the number of arguments
  249. *
  250. */
  251. static void getLogOptionFunction (xmlXPathParserContextPtr ctxt, int nargs)
  252. {
  253. IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
  254. if (!scriptContext)
  255. {
  256. xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
  257. return;
  258. }
  259. if (nargs != 1)
  260. {
  261. xmlXPathSetArityError(ctxt);
  262. return;
  263. }
  264. xmlChar *namestring = xmlXPathPopString(ctxt);
  265. if (xmlXPathCheckError(ctxt)) //includes null check
  266. return;
  267. const char *value = scriptContext->queryAttribute(ESDLScriptCtxSection_Logging, (const char *)namestring);
  268. xmlFree(namestring);
  269. if (!value)
  270. xmlXPathReturnEmptyString(ctxt);
  271. else
  272. xmlXPathReturnString(ctxt, xmlStrdup((const xmlChar *)value));
  273. }
  274. /**
  275. * getLogProfileFunction
  276. * @ctxt: an XPath parser context
  277. * @nargs: the number of arguments
  278. *
  279. */
  280. static void getLogProfileFunction (xmlXPathParserContextPtr ctxt, int nargs)
  281. {
  282. IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
  283. if (!scriptContext)
  284. {
  285. xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
  286. return;
  287. }
  288. if (nargs != 0)
  289. {
  290. xmlXPathSetArityError(ctxt);
  291. return;
  292. }
  293. const char *value = scriptContext->queryAttribute(ESDLScriptCtxSection_Logging, "profile");
  294. if (!value)
  295. xmlXPathReturnEmptyString(ctxt);
  296. else
  297. xmlXPathReturnString(ctxt, xmlStrdup((const xmlChar *)value));
  298. }
  299. /**
  300. * logOptionExistsFunction
  301. * @ctxt: an XPath parser context
  302. * @nargs: the number of arguments
  303. *
  304. */
  305. static void logOptionExistsFunction (xmlXPathParserContextPtr ctxt, int nargs)
  306. {
  307. IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
  308. if (!scriptContext)
  309. {
  310. xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
  311. return;
  312. }
  313. if (nargs != 1)
  314. {
  315. xmlXPathSetArityError(ctxt);
  316. return;
  317. }
  318. xmlChar *namestring = xmlXPathPopString(ctxt);
  319. if (xmlXPathCheckError(ctxt)) //includes null check
  320. return;
  321. const char *value = scriptContext->queryAttribute(ESDLScriptCtxSection_Logging, (const char *)namestring);
  322. xmlFree(namestring);
  323. xmlXPathReturnBoolean(ctxt, (!value) ? 0 : 1);
  324. }
  325. /**
  326. * storedValueExistsFunction
  327. * @ctxt: an XPath parser context
  328. * @nargs: the number of arguments
  329. *
  330. */
  331. static void storedValueExistsFunction (xmlXPathParserContextPtr ctxt, int nargs)
  332. {
  333. IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
  334. if (!scriptContext)
  335. {
  336. xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
  337. return;
  338. }
  339. if (nargs != 1)
  340. {
  341. xmlXPathSetArityError(ctxt);
  342. return;
  343. }
  344. xmlChar *namestring = xmlXPathPopString(ctxt);
  345. if (xmlXPathCheckError(ctxt)) //includes null check
  346. return;
  347. const char *value = scriptContext->queryAttribute(ESDLScriptCtxSection_Store, (const char *)namestring);
  348. xmlFree(namestring);
  349. xmlXPathReturnBoolean(ctxt, (!value) ? 0 : 1);
  350. }
  351. //esdl tokenize function will create temporaries in the root/temp section/node of the document
  352. //so this is not a general purpose function in that sense
  353. //this section should be cleared after every script runs
  354. //we may allow overriding the storage location in the future
  355. //
  356. static void strTokenizeFunction(xmlXPathParserContextPtr ctxt, int nargs)
  357. {
  358. IEsdlScriptContext *scriptContext = getEsdlScriptContext(ctxt);
  359. if (!scriptContext)
  360. {
  361. xmlXPathSetError((ctxt), XPATH_INVALID_CTXT);
  362. return;
  363. }
  364. if ((nargs < 1) || (nargs > 2))
  365. {
  366. xmlXPathSetArityError(ctxt);
  367. return;
  368. }
  369. xmlChar *delimiters;
  370. if (nargs == 2)
  371. {
  372. delimiters = xmlXPathPopString(ctxt);
  373. if (xmlXPathCheckError(ctxt))
  374. return;
  375. }
  376. else
  377. {
  378. delimiters = xmlStrdup((const xmlChar *) "\t\r\n ");
  379. }
  380. if (delimiters == NULL)
  381. return;
  382. xmlChar *str = xmlXPathPopString(ctxt);
  383. if (xmlXPathCheckError(ctxt) || (str == NULL))
  384. {
  385. if (str)
  386. xmlFree(str);
  387. xmlFree(delimiters);
  388. return;
  389. }
  390. StringBuffer resultPath;
  391. if (!scriptContext->tokenize((const char *)str, (const char *)delimiters, resultPath))
  392. {
  393. xmlFree(str);
  394. xmlFree(delimiters);
  395. xmlXPathSetError((ctxt), XPATH_EXPR_ERROR);
  396. return;
  397. }
  398. xmlXPathObjectPtr ret = xmlXPathEval((const xmlChar *) resultPath.str(), ctxt->context);
  399. if (ret != NULL)
  400. valuePush(ctxt, ret);
  401. else
  402. valuePush(ctxt, xmlXPathNewNodeSet(NULL));
  403. xmlFree(str);
  404. xmlFree(delimiters);
  405. }
  406. void registerEsdlXPathExtensionsForURI(IXpathContext *xpathContext, const char *uri)
  407. {
  408. xpathContext->registerFunction(uri, "validateFeaturesAccess", (void *)validateFeaturesAccessFunction);
  409. xpathContext->registerFunction(uri, "secureAccessFlags", (void *)secureAccessFlagsFunction);
  410. xpathContext->registerFunction(uri, "getFeatureSecAccessFlags", (void *)getFeatureSecAccessFlagsFunction);
  411. xpathContext->registerFunction(uri, "getStoredStringValue", (void *)getStoredStringValueFunction);
  412. xpathContext->registerFunction(uri, "getDataSection", (void *)scriptGetDataSectionFunction);
  413. xpathContext->registerFunction(uri, "ensureDataSection", (void *)scriptEnsureDataSectionFunction);
  414. xpathContext->registerFunction(uri, "storedValueExists", (void *)storedValueExistsFunction);
  415. xpathContext->registerFunction(uri, "getLogProfile", (void *)getLogProfileFunction);
  416. xpathContext->registerFunction(uri, "getLogOption", (void *)getLogOptionFunction);
  417. xpathContext->registerFunction(uri, "logOptionExists", (void *)logOptionExistsFunction);
  418. xpathContext->registerFunction(uri, "tokenize", (void *)strTokenizeFunction);
  419. }
  420. void registerEsdlXPathExtensions(IXpathContext *xpathContext, IEsdlScriptContext *context, const StringArray &prefixes)
  421. {
  422. bool includeDefaultNS = false;
  423. xpathContext->setUserData(context);
  424. if (!prefixes.ordinality())
  425. xpathContext->registerNamespace("esdl", "urn:hpcc:esdl:script");
  426. else
  427. {
  428. ForEachItemIn(i, prefixes)
  429. {
  430. if (isEmptyString(prefixes.item(i)))
  431. includeDefaultNS=true;
  432. else
  433. xpathContext->registerNamespace(prefixes.item(i), "urn:hpcc:esdl:script");
  434. }
  435. }
  436. if (includeDefaultNS)
  437. registerEsdlXPathExtensionsForURI(xpathContext, nullptr);
  438. registerEsdlXPathExtensionsForURI(xpathContext, "urn:hpcc:esdl:script");
  439. }