libxml_xpathprocessor.cpp 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2018 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 <libexslt/exslt.h>
  30. #include "xpathprocessor.hpp"
  31. #include "xmlerror.hpp"
  32. #include <map>
  33. #include <stack>
  34. #include <string>
  35. #include <memory>
  36. static inline char *skipWS(char *s)
  37. {
  38. while (isspace(*s)) s++;
  39. return s;
  40. }
  41. static char *markEndGetNext(char *line)
  42. {
  43. char *end = (char *)strchr(line, '\n');
  44. if (!end)
  45. return nullptr;
  46. *end=0;
  47. if (isEmptyString(++end))
  48. return nullptr;
  49. return end;
  50. }
  51. static char *extractFromLineGetNext(StringArray &functions, StringArray &variables, char *line)
  52. {
  53. line = skipWS(line);
  54. if (isEmptyString(line))
  55. return nullptr;
  56. char *next = markEndGetNext(line);
  57. if (strncmp(line, "FUNCTION", 8)==0)
  58. {
  59. char *paren = (char *)strchr(line, '(');
  60. if (paren)
  61. *paren=0;
  62. functions.append(skipWS(line+8+1));
  63. }
  64. else if (strncmp(line, "VARIABLE", 8)==0)
  65. {
  66. variables.append(skipWS(line+8+1));
  67. }
  68. return next;
  69. }
  70. class CLibCompiledXpath : public CInterfaceOf<ICompiledXpath>
  71. {
  72. private:
  73. xmlXPathCompExprPtr m_compiledXpathExpression = nullptr;
  74. StringBuffer m_xpath;
  75. ReadWriteLock m_rwlock;
  76. public:
  77. CLibCompiledXpath(const char * xpath)
  78. {
  79. m_xpath.set(xpath);
  80. m_compiledXpathExpression = xmlXPathCompile(BAD_CAST m_xpath.str());
  81. }
  82. ~CLibCompiledXpath()
  83. {
  84. xmlXPathFreeCompExpr(m_compiledXpathExpression);
  85. }
  86. const char * getXpath()
  87. {
  88. return m_xpath.str();
  89. }
  90. xmlXPathCompExprPtr getCompiledXPathExpression()
  91. {
  92. return m_compiledXpathExpression;
  93. }
  94. virtual void extractReferences(StringArray &functions, StringArray &variables) override
  95. {
  96. #ifndef _WIN32
  97. char *buf = nullptr;
  98. size_t len = 0;
  99. FILE *stream = open_memstream(&buf, &len);
  100. if (stream == nullptr)
  101. return;
  102. xmlXPathDebugDumpCompExpr(stream, m_compiledXpathExpression, 0);
  103. fputc(0, stream);
  104. fflush(stream);
  105. fclose (stream);
  106. char *line = buf;
  107. while (line)
  108. line = extractFromLineGetNext(functions, variables, line);
  109. free (buf);
  110. #else
  111. UNIMPLEMENTED;
  112. #endif
  113. }
  114. };
  115. static xmlXPathObjectPtr variableLookupFunc(void *data, const xmlChar *name, const xmlChar *ns_uri);
  116. typedef std::map<std::string, xmlXPathObjectPtr> XPathObjectMap;
  117. class CLibXpathScope
  118. {
  119. public:
  120. StringAttr name; //in future allow named parent access?
  121. XPathObjectMap variables;
  122. public:
  123. CLibXpathScope(const char *_name) : name(_name){}
  124. ~CLibXpathScope()
  125. {
  126. for (XPathObjectMap::iterator it=variables.begin(); it!=variables.end(); ++it)
  127. xmlXPathFreeObject(it->second);
  128. }
  129. bool setObject(const char *key, xmlXPathObjectPtr obj)
  130. {
  131. std::pair<XPathObjectMap::iterator,bool> ret = variables.emplace(key, obj);
  132. if (ret.second==true)
  133. return true;
  134. //within scope, behave exactly like xmlXPathContext variables are added now, which seems to be that they are replaced
  135. //if we're preventing replacing variables we need to handle elsewhere
  136. // and still replace external values when treated as xsdl:variables, but not when treated as xsdl:params
  137. if (ret.first->second)
  138. xmlXPathFreeObject(ret.first->second);
  139. ret.first->second = obj;
  140. return true;
  141. }
  142. xmlXPathObjectPtr getObject(const char *key)
  143. {
  144. XPathObjectMap::iterator it = variables.find(key);
  145. if (it == variables.end())
  146. return nullptr;
  147. return it->second;
  148. }
  149. };
  150. typedef std::vector<std::unique_ptr<CLibXpathScope>> XPathScopeVector;
  151. typedef std::map<std::string, ICompiledXpath*> XPathInputMap;
  152. class XpathContextState
  153. {
  154. private:
  155. xmlDocPtr doc = nullptr;
  156. xmlNodePtr node = nullptr;
  157. int contextSize = 0;
  158. int proximityPosition = 0;
  159. public:
  160. XpathContextState(xmlXPathContextPtr ctx)
  161. {
  162. doc = ctx->doc;
  163. node = ctx->node;
  164. contextSize = ctx->contextSize;
  165. proximityPosition = ctx->proximityPosition;
  166. }
  167. void restore(xmlXPathContextPtr ctx)
  168. {
  169. ctx->doc = doc;
  170. ctx->node = node;
  171. ctx->contextSize = contextSize;
  172. ctx->proximityPosition = proximityPosition;
  173. }
  174. };
  175. typedef std::vector<XpathContextState> XPathContextStateVector;
  176. class CLibXpathContext : public CInterfaceOf<IXpathContext>
  177. {
  178. public:
  179. XPathInputMap provided;
  180. xmlDocPtr m_xmlDoc = nullptr;
  181. xmlXPathContextPtr m_xpathContext = nullptr;
  182. ReadWriteLock m_rwlock;
  183. XPathScopeVector scopes;
  184. bool strictParameterDeclaration = true;
  185. bool removeDocNamespaces = false;
  186. bool ownedDoc = false;
  187. //saved state
  188. XPathContextStateVector saved;
  189. public:
  190. CLibXpathContext(const char * xmldoc, bool _strictParameterDeclaration, bool removeDocNs) : strictParameterDeclaration(_strictParameterDeclaration), removeDocNamespaces(removeDocNs)
  191. {
  192. beginScope("/");
  193. setXmlDoc(xmldoc);
  194. registerExslt();
  195. }
  196. CLibXpathContext(xmlDocPtr doc, xmlNodePtr node, bool _strictParameterDeclaration) : strictParameterDeclaration(_strictParameterDeclaration)
  197. {
  198. beginScope("/");
  199. setContextDocument(doc, node);
  200. registerExslt();
  201. }
  202. ~CLibXpathContext()
  203. {
  204. for (XPathInputMap::iterator it=provided.begin(); it!=provided.end(); ++it)
  205. it->second->Release();
  206. xmlXPathFreeContext(m_xpathContext);
  207. if (ownedDoc)
  208. xmlFreeDoc(m_xmlDoc);
  209. }
  210. void registerExslt()
  211. {
  212. exsltDateXpathCtxtRegister(m_xpathContext, (xmlChar*)"date");
  213. exsltMathXpathCtxtRegister(m_xpathContext, (xmlChar*)"math");
  214. exsltSetsXpathCtxtRegister(m_xpathContext, (xmlChar*)"set");
  215. exsltStrXpathCtxtRegister(m_xpathContext, (xmlChar*)"str");
  216. }
  217. void pushLocation()
  218. {
  219. WriteLockBlock wblock(m_rwlock);
  220. saved.emplace_back(XpathContextState(m_xpathContext));
  221. }
  222. void setLocation(xmlDocPtr doc, xmlNodePtr node, int contextSize, int proximityPosition)
  223. {
  224. WriteLockBlock wblock(m_rwlock);
  225. m_xpathContext->doc = doc;
  226. m_xpathContext->node = node;
  227. m_xpathContext->contextSize = contextSize;
  228. m_xpathContext->proximityPosition = proximityPosition;
  229. }
  230. void setLocation(xmlXPathContextPtr ctx)
  231. {
  232. WriteLockBlock wblock(m_rwlock);
  233. m_xpathContext->doc = ctx->doc;
  234. m_xpathContext->node = ctx->node;
  235. m_xpathContext->contextSize = ctx->contextSize;
  236. m_xpathContext->proximityPosition = ctx->proximityPosition;
  237. }
  238. void popLocation()
  239. {
  240. WriteLockBlock wblock(m_rwlock);
  241. saved.back().restore(m_xpathContext);
  242. saved.pop_back();
  243. }
  244. void beginScope(const char *name) override
  245. {
  246. WriteLockBlock wblock(m_rwlock);
  247. scopes.emplace_back(new CLibXpathScope(name));
  248. }
  249. void endScope() override
  250. {
  251. WriteLockBlock wblock(m_rwlock);
  252. if (scopes.size()>1) //preserve root scope
  253. scopes.pop_back();
  254. }
  255. static void tableScanCallback(void *payload, void *data, xmlChar *name)
  256. {
  257. DBGLOG("k/v == [%s,%s]\n", (char *) name, (char *) payload);
  258. }
  259. virtual void registerNamespace(const char *prefix, const char * uri) override
  260. {
  261. if (m_xpathContext)
  262. {
  263. WriteLockBlock wblock(m_rwlock);
  264. xmlXPathRegisterNs(m_xpathContext, (const xmlChar *) prefix, (const xmlChar *) uri);
  265. }
  266. }
  267. virtual const char *queryNamespace(const char *prefix) override
  268. {
  269. if (!m_xpathContext)
  270. return nullptr;
  271. WriteLockBlock wblock(m_rwlock);
  272. return (const char *) xmlXPathNsLookup(m_xpathContext, (const xmlChar *)prefix);
  273. }
  274. virtual void registerFunction(const char *xmlns, const char * name, void *f) override
  275. {
  276. if (m_xpathContext)
  277. {
  278. WriteLockBlock wblock(m_rwlock);
  279. xmlXPathRegisterFuncNS(m_xpathContext, (const xmlChar *) name, (const xmlChar *) xmlns, (xmlXPathFunction) f);
  280. }
  281. }
  282. virtual void setUserData(void *userdata) override
  283. {
  284. if (m_xpathContext)
  285. {
  286. WriteLockBlock wblock(m_rwlock);
  287. m_xpathContext->userData = userdata;
  288. }
  289. }
  290. virtual void *getUserData() override
  291. {
  292. if (m_xpathContext)
  293. {
  294. ReadLockBlock wblock(m_rwlock);
  295. return m_xpathContext->userData;
  296. }
  297. return nullptr;
  298. }
  299. inline CLibXpathScope *getCurrentScope()
  300. {
  301. ReadLockBlock wblock(m_rwlock);
  302. assertex(scopes.size());
  303. return scopes.back().get();
  304. }
  305. xmlXPathObjectPtr findVariable(const char *name, const char *ns_uri, CLibXpathScope *scope)
  306. {
  307. const char *fullname = name;
  308. StringBuffer s;
  309. if (!isEmptyString(ns_uri))
  310. fullname = s.append(ns_uri).append(':').append(name).str();
  311. ReadLockBlock wblock(m_rwlock);
  312. xmlXPathObjectPtr obj = nullptr;
  313. if (scope)
  314. return scope->getObject(fullname);
  315. for (XPathScopeVector::const_reverse_iterator it=scopes.crbegin(); !obj && it!=scopes.crend(); ++it)
  316. obj = it->get()->getObject(fullname);
  317. //check libxml2 level variables, shouldn't happen currently but we may want to wrap existing xpathcontexts in the future
  318. if (!obj)
  319. obj = (xmlXPathObjectPtr)xmlHashLookup2(m_xpathContext->varHash, (const xmlChar *)name, (const xmlChar *)ns_uri);
  320. return obj;
  321. }
  322. xmlXPathObjectPtr getVariableObject(const char *name, const char *ns_uri, CLibXpathScope *scope)
  323. {
  324. return xmlXPathObjectCopy(findVariable(name, ns_uri, scope));
  325. }
  326. bool hasVariable(const char *name, const char *ns_uri, CLibXpathScope *scope)
  327. {
  328. return (findVariable(name, ns_uri, scope)!=nullptr);
  329. }
  330. virtual bool addObjectVariable(const char * name, xmlXPathObjectPtr obj, CLibXpathScope *scope)
  331. {
  332. if (isEmptyString(name))
  333. return false;
  334. if (m_xpathContext)
  335. {
  336. if (!obj)
  337. throw MakeStringException(-1, "addObjectVariable %s error", name);
  338. WriteLockBlock wblock(m_rwlock);
  339. if (!scope && !scopes.empty())
  340. scope = scopes.back().get();
  341. if (scope)
  342. return scope->setObject(name, obj);
  343. return xmlXPathRegisterVariable(m_xpathContext, (xmlChar *)name, obj) == 0;
  344. }
  345. return false;
  346. }
  347. bool addStringVariable(const char * name, const char * val, CLibXpathScope *scope)
  348. {
  349. if (!val)
  350. return false;
  351. return addObjectVariable(name, xmlXPathNewCString(val), scope);
  352. }
  353. virtual bool addXpathVariable(const char * name, const char * xpath, CLibXpathScope *scope)
  354. {
  355. if (isEmptyString(xpath))
  356. addVariable(name, "");
  357. if (m_xpathContext)
  358. {
  359. xmlXPathObjectPtr obj = evaluate(xpath);
  360. if (!obj)
  361. throw MakeStringException(-1, "addXpathVariable xpath error %s", xpath);
  362. return addObjectVariable(name, obj, scope);
  363. }
  364. return false;
  365. }
  366. bool addCompiledVariable(const char * name, ICompiledXpath * compiled, CLibXpathScope *scope)
  367. {
  368. if (!compiled)
  369. addVariable(name, "");
  370. if (m_xpathContext)
  371. {
  372. CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiled);
  373. xmlXPathObjectPtr obj = evaluate(clibCompiledXpath->getCompiledXPathExpression(), clibCompiledXpath->getXpath());
  374. if (!obj)
  375. throw MakeStringException(-1, "addEvaluateVariable xpath error %s", clibCompiledXpath->getXpath());
  376. return addObjectVariable(name, obj, scope);
  377. }
  378. return false;
  379. }
  380. virtual bool addInputValue(const char * name, const char * value) override
  381. {
  382. if (isEmptyString(name)||isEmptyString(value))
  383. return false;
  384. VStringBuffer xpath("'%s'", value);
  385. return addInputXpath(name, xpath);
  386. }
  387. virtual bool addInputXpath(const char * name, const char * xpath) override
  388. {
  389. if (isEmptyString(name)||isEmptyString(xpath))
  390. return false;
  391. Owned<ICompiledXpath> compiled = compileXpath(xpath);
  392. if (compiled)
  393. {
  394. WriteLockBlock wblock(m_rwlock);
  395. provided.emplace(name, compiled.getClear());
  396. return true;
  397. }
  398. return false;
  399. }
  400. inline ICompiledXpath *findInput(const char *name)
  401. {
  402. ReadLockBlock rblock(m_rwlock);
  403. XPathInputMap::iterator it = provided.find(name);
  404. if (it == provided.end())
  405. return nullptr;
  406. return it->second;
  407. }
  408. virtual bool declareCompiledParameter(const char * name, ICompiledXpath * compiled) override
  409. {
  410. if (hasVariable(name, nullptr, getCurrentScope()))
  411. return false;
  412. //use input value
  413. ICompiledXpath *inputxp = findInput(name);
  414. if (inputxp)
  415. return addCompiledVariable(name, inputxp, getCurrentScope());
  416. //use default provided
  417. return addCompiledVariable(name, compiled, getCurrentScope());
  418. }
  419. virtual void declareRemainingInputs() override
  420. {
  421. for (XPathInputMap::iterator it=provided.begin(); it!=provided.end(); ++it)
  422. declareCompiledParameter(it->first.c_str(), it->second);
  423. }
  424. virtual bool declareParameter(const char * name, const char *value) override
  425. {
  426. if (hasVariable(name, nullptr, getCurrentScope()))
  427. return false;
  428. //use input value
  429. ICompiledXpath *input = findInput(name);
  430. if (input)
  431. return addCompiledVariable(name, input, getCurrentScope());
  432. //use default provided
  433. return addStringVariable(name, value, getCurrentScope());
  434. }
  435. virtual bool addXpathVariable(const char * name, const char * xpath) override
  436. {
  437. return addXpathVariable(name, xpath, nullptr);
  438. }
  439. virtual bool addVariable(const char * name, const char * val) override
  440. {
  441. return addStringVariable(name, val, nullptr);
  442. }
  443. virtual bool addCompiledVariable(const char * name, ICompiledXpath * compiled) override
  444. {
  445. return addCompiledVariable(name, compiled, nullptr);
  446. }
  447. virtual const char * getVariable(const char * name, StringBuffer & variable) override
  448. {
  449. if (m_xpathContext)
  450. {
  451. ReadLockBlock rblock(m_rwlock);
  452. xmlXPathObjectPtr ptr = xmlXPathVariableLookupNS(m_xpathContext, (const xmlChar *)name, nullptr);
  453. if (!ptr)
  454. return nullptr;
  455. variable.append((const char *) ptr->stringval);
  456. xmlXPathFreeObject(ptr);
  457. return variable;
  458. }
  459. return nullptr;
  460. }
  461. virtual IXpathContextIterator *evaluateAsNodeSet(ICompiledXpath * compiled) override
  462. {
  463. CLibCompiledXpath * clCompiled = static_cast<CLibCompiledXpath *>(compiled);
  464. if (!clCompiled)
  465. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsNodeSet: Error: Could not evaluate XPATH");
  466. return evaluateAsNodeSet(evaluate(clCompiled->getCompiledXPathExpression(), compiled->getXpath()), compiled->getXpath());
  467. }
  468. IXpathContextIterator *evaluateAsNodeSet(xmlXPathObjectPtr evaluated, const char* xpath);
  469. virtual bool evaluateAsBoolean(const char * xpath) override
  470. {
  471. if (!xpath || !*xpath)
  472. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsBoolean: Error: Could not evaluate empty XPATH");
  473. return evaluateAsBoolean(evaluate(xpath), xpath);
  474. }
  475. virtual bool evaluateAsString(const char * xpath, StringBuffer & evaluated) override
  476. {
  477. if (!xpath || !*xpath)
  478. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate empty XPATH");
  479. return evaluateAsString(evaluate(xpath), evaluated, xpath);
  480. }
  481. virtual bool evaluateAsBoolean(ICompiledXpath * compiledXpath) override
  482. {
  483. CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  484. if (!clibCompiledXpath)
  485. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsBoolean: Error: Missing compiled XPATH");
  486. return evaluateAsBoolean(evaluate(clibCompiledXpath->getCompiledXPathExpression(), compiledXpath->getXpath()), compiledXpath->getXpath());
  487. }
  488. virtual const char * evaluateAsString(ICompiledXpath * compiledXpath, StringBuffer & evaluated) override
  489. {
  490. CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  491. if (!clibCompiledXpath)
  492. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsString: Error: Missing compiled XPATH");
  493. return evaluateAsString(evaluate(clibCompiledXpath->getCompiledXPathExpression(), compiledXpath->getXpath()), evaluated, compiledXpath->getXpath());
  494. }
  495. virtual double evaluateAsNumber(ICompiledXpath * compiledXpath) override
  496. {
  497. CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  498. if (!clibCompiledXpath)
  499. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsNumber: Error: Missing compiled XPATH");
  500. return evaluateAsNumber(evaluate(clibCompiledXpath->getCompiledXPathExpression(), compiledXpath->getXpath()), compiledXpath->getXpath());
  501. }
  502. virtual bool setXmlDoc(const char * xmldoc) override
  503. {
  504. if (isEmptyString(xmldoc))
  505. return false;
  506. xmlDocPtr doc = xmlParseDoc((const unsigned char *)xmldoc);
  507. if (doc == nullptr)
  508. {
  509. ERRLOG("XpathProcessor:setXmlDoc Error: Unable to parse XMLLib document");
  510. return false;
  511. }
  512. ownedDoc = true;
  513. return setContextDocument(doc, xmlDocGetRootElement(doc));
  514. }
  515. private:
  516. xmlNodePtr checkGetSoapNamespace(xmlNodePtr cur, const char *expected, const xmlChar *&soap)
  517. {
  518. if (!cur)
  519. return nullptr;
  520. if (!streq((const char *)cur->name, expected))
  521. return cur;
  522. if (!soap && cur->ns && cur->ns->href)
  523. soap = cur->ns->href;
  524. return xmlFirstElementChild(cur);
  525. }
  526. void removeNamespace(xmlNodePtr cur, const xmlChar *remove)
  527. {
  528. //we need to recognize different namespace behavior at each entry point
  529. //
  530. //for backward compatibility we need to be flexible with what we receive, from the user
  531. //but other entry points are dealing with standardized content
  532. //hopefully even calls out to a given 3rd party services will be self consistent
  533. //
  534. if (!remove)
  535. cur->ns=nullptr; //just a "reference", no free
  536. else if (cur->ns && cur->ns->href && streq((const char *)cur->ns->href, (const char *)remove))
  537. cur->ns=nullptr;
  538. for (xmlNodePtr child = xmlFirstElementChild(cur); child!=nullptr; child=xmlNextElementSibling(child))
  539. removeNamespace(child, remove);
  540. for (xmlAttrPtr att = cur->properties; att!=nullptr; att=att->next)
  541. {
  542. if (!remove)
  543. att->ns=nullptr;
  544. else if (att->ns && att->ns->href && streq((const char *)att->ns->href, (const char *)remove))
  545. att->ns=nullptr;
  546. }
  547. }
  548. void processNamespaces()
  549. {
  550. const xmlChar *soap = nullptr;
  551. const xmlChar *n = nullptr;
  552. xmlNodePtr start = (m_xpathContext && m_xpathContext->node) ? m_xpathContext->node : xmlDocGetRootElement(m_xmlDoc);
  553. xmlNodePtr cur = checkGetSoapNamespace(start, "Envelope", soap);
  554. cur = checkGetSoapNamespace(cur, "Body", soap);
  555. if (soap)
  556. xmlXPathRegisterNs(m_xpathContext, (const xmlChar *)"soap", soap);
  557. if (cur->ns && cur->ns->href)
  558. n = cur->ns->href;
  559. if (n)
  560. xmlXPathRegisterNs(m_xpathContext, (const xmlChar *)"n", n);
  561. if (removeDocNamespaces)
  562. removeNamespace(xmlDocGetRootElement(m_xmlDoc), nullptr);
  563. }
  564. bool setContextDocument(xmlDocPtr doc, xmlNodePtr node)
  565. {
  566. WriteLockBlock rblock(m_rwlock);
  567. m_xmlDoc = doc;
  568. m_xpathContext = xmlXPathNewContext(m_xmlDoc);
  569. if(m_xpathContext == nullptr)
  570. {
  571. ERRLOG("XpathProcessor:setContextDocument: Error: Unable to create new XMLLib XPath context");
  572. return false;
  573. }
  574. //relative paths need something to be relative to
  575. if (node)
  576. m_xpathContext->node = node;
  577. xmlXPathRegisterVariableLookup(m_xpathContext, variableLookupFunc, this);
  578. processNamespaces();
  579. return true;
  580. }
  581. bool evaluateAsBoolean(xmlXPathObjectPtr evaluatedXpathObj, const char* xpath)
  582. {
  583. if (!evaluatedXpathObj)
  584. {
  585. throw MakeStringException(XPATHERR_InvalidInput, "XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s'", xpath);
  586. }
  587. if (XPATH_BOOLEAN != evaluatedXpathObj->type)
  588. {
  589. xmlXPathFreeObject(evaluatedXpathObj);
  590. throw MakeStringException(XPATHERR_UnexpectedInput, "XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s' as Boolean", xpath);
  591. }
  592. bool bresult = evaluatedXpathObj->boolval;
  593. xmlXPathFreeObject(evaluatedXpathObj);
  594. return bresult;
  595. }
  596. const char* evaluateAsString(xmlXPathObjectPtr evaluatedXpathObj, StringBuffer& evaluated, const char* xpath)
  597. {
  598. if (!evaluatedXpathObj)
  599. throw MakeStringException(XPATHERR_InvalidInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s'", xpath);
  600. evaluated.clear();
  601. switch (evaluatedXpathObj->type)
  602. {
  603. case XPATH_NODESET:
  604. {
  605. xmlNodeSetPtr nodes = evaluatedXpathObj->nodesetval;
  606. for (int i = 0; nodes!=nullptr && i < nodes->nodeNr; i++)
  607. {
  608. xmlNodePtr nodeTab = nodes->nodeTab[i];
  609. auto nodeContent = xmlNodeGetContent(nodeTab);
  610. evaluated.append((const char *)nodeContent);
  611. xmlFree(nodeContent);
  612. }
  613. break;
  614. }
  615. case XPATH_BOOLEAN:
  616. case XPATH_NUMBER:
  617. case XPATH_STRING:
  618. case XPATH_POINT:
  619. case XPATH_RANGE:
  620. case XPATH_LOCATIONSET:
  621. case XPATH_USERS:
  622. case XPATH_XSLT_TREE:
  623. {
  624. evaluatedXpathObj = xmlXPathConvertString (evaluatedXpathObj); //existing object is freed
  625. if (!evaluatedXpathObj)
  626. throw MakeStringException(XPATHERR_UnexpectedInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s'; could not convert result to string", xpath);
  627. evaluated.append(evaluatedXpathObj->stringval);
  628. break;
  629. }
  630. default:
  631. {
  632. xmlXPathFreeObject(evaluatedXpathObj);
  633. throw MakeStringException(XPATHERR_UnexpectedInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s' as string; unexpected type %d", xpath, evaluatedXpathObj->type);
  634. break;
  635. }
  636. }
  637. xmlXPathFreeObject(evaluatedXpathObj);
  638. return evaluated.str();
  639. }
  640. double evaluateAsNumber(xmlXPathObjectPtr evaluatedXpathObj, const char* xpath)
  641. {
  642. if (!evaluatedXpathObj)
  643. throw MakeStringException(XPATHERR_InvalidInput,"XpathProcessor:evaluateAsNumber: Error: Could not evaluate XPATH '%s'", xpath);
  644. double ret = xmlXPathCastToNumber(evaluatedXpathObj);
  645. xmlXPathFreeObject(evaluatedXpathObj);
  646. return ret;
  647. }
  648. virtual xmlXPathObjectPtr evaluate(xmlXPathCompExprPtr compiled, const char *xpath)
  649. {
  650. xmlXPathObjectPtr evaluatedXpathObj = nullptr;
  651. if (compiled)
  652. {
  653. ReadLockBlock rlock(m_rwlock);
  654. if ( m_xpathContext)
  655. {
  656. evaluatedXpathObj = xmlXPathCompiledEval(compiled, m_xpathContext);
  657. }
  658. else
  659. {
  660. throw MakeStringException(XPATHERR_InvalidState,"XpathProcessor:evaluate: Error: Could not evaluate XPATH '%s'", xpath);
  661. }
  662. }
  663. return evaluatedXpathObj;
  664. }
  665. virtual xmlXPathObjectPtr evaluate(const char * xpath)
  666. {
  667. xmlXPathObjectPtr evaluatedXpathObj = nullptr;
  668. if (xpath && *xpath)
  669. {
  670. ReadLockBlock rlock(m_rwlock);
  671. if ( m_xpathContext)
  672. {
  673. evaluatedXpathObj = xmlXPathEval((const xmlChar *)xpath, m_xpathContext);
  674. }
  675. else
  676. {
  677. throw MakeStringException(XPATHERR_InvalidState,"XpathProcessor:evaluate: Error: Could not evaluate XPATH '%s'; ensure xmldoc has been set", xpath);
  678. }
  679. }
  680. return evaluatedXpathObj;
  681. }
  682. };
  683. class XPathNodeSetIterator : public CInterfaceOf<IXpathContextIterator>
  684. {
  685. public:
  686. CLibXpathContext *context;
  687. xmlNodeSetPtr list;
  688. unsigned pos = 0;
  689. public:
  690. XPathNodeSetIterator(CLibXpathContext *xpctx, xmlNodeSetPtr nodeset) : context(xpctx), list(nodeset)
  691. {
  692. context->pushLocation();
  693. context->m_xpathContext->contextSize = xmlXPathNodeSetGetLength(list);
  694. }
  695. virtual ~XPathNodeSetIterator()
  696. {
  697. context->popLocation();
  698. if (list)
  699. xmlXPathFreeNodeSet(list);
  700. }
  701. bool update(int newpos)
  702. {
  703. pos = newpos;
  704. if (!isValid())
  705. return false;
  706. xmlNodePtr node = xmlXPathNodeSetItem(list, pos);
  707. context->m_xpathContext->node = node;
  708. if ((node->type != XML_NAMESPACE_DECL && node->doc != nullptr))
  709. context->m_xpathContext->doc = node->doc;
  710. context->m_xpathContext->proximityPosition = pos + 1;
  711. return true;
  712. }
  713. virtual bool first()
  714. {
  715. if (xmlXPathNodeSetGetLength(list)==0)
  716. return false;
  717. return update(0);
  718. }
  719. virtual bool next()
  720. {
  721. return update(pos+1);
  722. }
  723. virtual bool isValid()
  724. {
  725. return (pos < xmlXPathNodeSetGetLength(list));
  726. }
  727. virtual IXpathContext & query()
  728. {
  729. return *context;
  730. }
  731. };
  732. IXpathContextIterator *CLibXpathContext::evaluateAsNodeSet(xmlXPathObjectPtr evaluated, const char* xpath)
  733. {
  734. if (!evaluated)
  735. {
  736. throw MakeStringException(XPATHERR_InvalidInput, "XpathProcessor:evaluateAsNodeSet: Error: Could not evaluate XPATH '%s'", xpath);
  737. }
  738. if (XPATH_NODESET != evaluated->type)
  739. {
  740. xmlXPathFreeObject(evaluated);
  741. throw MakeStringException(XPATHERR_UnexpectedInput, "XpathProcessor:evaluateAsNodeSet: Error: Could not evaluate XPATH '%s' as NodeSet", xpath);
  742. }
  743. xmlNodeSetPtr ns = evaluated->nodesetval;
  744. evaluated->nodesetval = nullptr;
  745. xmlXPathFreeObject(evaluated);
  746. return new XPathNodeSetIterator(this, ns);
  747. }
  748. static xmlXPathObjectPtr variableLookupFunc(void *data, const xmlChar *name, const xmlChar *ns_uri)
  749. {
  750. CLibXpathContext *ctxt = (CLibXpathContext *) data;
  751. if (!ctxt)
  752. return nullptr;
  753. return ctxt->getVariableObject((const char *)name, (const char *)ns_uri, nullptr);
  754. }
  755. extern ICompiledXpath* compileXpath(const char * xpath)
  756. {
  757. return new CLibCompiledXpath(xpath);
  758. }
  759. void addChildFromPtree(xmlNodePtr parent, IPropertyTree &tree, const char *name)
  760. {
  761. if (isEmptyString(name))
  762. name = tree.queryName();
  763. if (*name==':') //invalid xml but maybe valid ptree?
  764. name++;
  765. StringAttr prefix;
  766. const char *colon = strchr(name, ':');
  767. if (colon)
  768. {
  769. prefix.set(name, colon-name);
  770. name = colon+1;
  771. }
  772. xmlNodePtr node = xmlNewChild(parent, nullptr, (const xmlChar *)name, (const xmlChar *)tree.queryProp(nullptr));
  773. if (!node)
  774. return;
  775. xmlNsPtr ns = nullptr;
  776. if (tree.getAttributeCount())
  777. {
  778. Owned<IAttributeIterator> atts = tree.getAttributes();
  779. ForEach(*atts)
  780. {
  781. const char *attname = atts->queryName()+1;
  782. if (strncmp(attname, "xmlns", 5)!=0)
  783. xmlNewProp(node, (const xmlChar *)attname, (const xmlChar *)atts->queryValue());
  784. else
  785. {
  786. attname+=5;
  787. if (*attname==':')
  788. attname++;
  789. if (*attname==0)
  790. attname=nullptr; //default namespace needs null prefix
  791. xmlNewNs(node, (const xmlChar *)atts->queryValue(), (const xmlChar *)attname);
  792. }
  793. }
  794. }
  795. if (prefix.isEmpty())
  796. node->ns = nullptr;
  797. else
  798. {
  799. xmlNsPtr ns = xmlSearchNs(node->doc, node, (const xmlChar *)prefix.str());
  800. if (!ns) //invalid but let's repair it
  801. ns = xmlNewNs(node, (const xmlChar *)"urn:hpcc:unknown", (const xmlChar *)prefix.str());
  802. node->ns = ns;
  803. }
  804. Owned<IPropertyTreeIterator> children = tree.getElements("*");
  805. ForEach(*children)
  806. addChildFromPtree(node, children->query(), nullptr);
  807. }
  808. IPropertyTree *createPtreeFromXmlNode(xmlNodePtr node);
  809. void copyXmlNode(IPropertyTree *tree, xmlNodePtr node)
  810. {
  811. for(xmlAttrPtr att=node->properties;att!=nullptr; att=att->next)
  812. {
  813. xmlChar *value = xmlGetProp(node, att->name);
  814. tree->setProp((const char *)att->name, (const char *)value);
  815. xmlFree(value);
  816. }
  817. for (xmlNodePtr child=xmlFirstElementChild(node); child!=nullptr; child=xmlNextElementSibling(child))
  818. tree->addPropTree((const char *)child->name, createPtreeFromXmlNode(child));
  819. }
  820. IPropertyTree *createPtreeFromXmlNode(xmlNodePtr node)
  821. {
  822. if (!node)
  823. return nullptr;
  824. Owned<IPropertyTree> tree = createPTree((const char *)node->name);
  825. copyXmlNode(tree, node);
  826. return tree.getClear();
  827. }
  828. static const char *queryAttrValue(xmlNodePtr node, const char *name)
  829. {
  830. if (!node)
  831. return nullptr;
  832. xmlAttrPtr att = xmlHasProp(node, (const xmlChar *)name);
  833. if (!att)
  834. return nullptr;
  835. if (att->type != XML_ATTRIBUTE_NODE || att->children==nullptr || (att->children->type != XML_TEXT_NODE && att->children->type != XML_CDATA_SECTION_NODE))
  836. return nullptr;
  837. return (const char *) att->children->content;
  838. }
  839. class CEsdlScriptContext : public CInterfaceOf<IEsdlScriptContext>
  840. {
  841. private:
  842. void *espCtx = nullptr;
  843. xmlDocPtr doc = nullptr;
  844. xmlNodePtr root = nullptr;
  845. xmlXPathContextPtr xpathCtx = nullptr;
  846. public:
  847. CEsdlScriptContext(void *ctx) : espCtx(ctx)
  848. {
  849. doc = xmlParseDoc((const xmlChar *) "<esdl_script_context/>");
  850. xpathCtx = xmlXPathNewContext(doc);
  851. if(xpathCtx == nullptr)
  852. throw MakeStringException(-1, "CEsdlScriptContext: Unable to create new xPath context");
  853. root = xmlDocGetRootElement(doc);
  854. xpathCtx->node = root;
  855. }
  856. private:
  857. ~CEsdlScriptContext()
  858. {
  859. xmlXPathFreeContext(xpathCtx);
  860. xmlFreeDoc(doc);
  861. }
  862. virtual void *queryEspContext() override
  863. {
  864. return espCtx;
  865. }
  866. inline void sanityCheckSectionName(const char *name)
  867. {
  868. //sanity check the name, not a full validation
  869. if (strpbrk(name, "/[]()*?"))
  870. throw MakeStringException(-1, "CEsdlScriptContext:removeSection invalid section name %s", name);
  871. }
  872. xmlNodePtr getSectionNode(const char *name, const char *xpath="*[1]")
  873. {
  874. sanityCheckSectionName(name);
  875. StringBuffer fullXpath(name);
  876. if (!isEmptyString(xpath))
  877. fullXpath.append('/').append(xpath);
  878. xmlNodePtr sect = nullptr;
  879. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) fullXpath.str(), xpathCtx);
  880. if (eval && XPATH_NODESET == eval->type && eval->nodesetval && eval->nodesetval->nodeNr && eval->nodesetval->nodeTab!=nullptr)
  881. sect = eval->nodesetval->nodeTab[0];
  882. xmlXPathFreeObject(eval);
  883. return sect;
  884. }
  885. void addXpathCtxConfigInputs(IXpathContext *tgtXpathCtx)
  886. {
  887. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) "config/*/Transform/Param", xpathCtx);
  888. if (!eval)
  889. return;
  890. if (XPATH_NODESET==eval->type && eval->nodesetval!=nullptr && eval->nodesetval->nodeNr!=0 && eval->nodesetval->nodeTab!=nullptr)
  891. {
  892. for (int i=0; i<eval->nodesetval->nodeNr; i++)
  893. {
  894. xmlNodePtr node = eval->nodesetval->nodeTab[i];
  895. if (node==nullptr)
  896. continue;
  897. const char *name = queryAttrValue(node, "name");
  898. if (xmlHasProp(node,(const xmlChar *) "select"))
  899. tgtXpathCtx->addInputXpath(name, queryAttrValue(node,"select"));
  900. else
  901. tgtXpathCtx->addInputValue(name, queryAttrValue(node,"value"));
  902. }
  903. }
  904. xmlXPathFreeObject(eval);
  905. }
  906. const char *getXPathString(const char *xpath, StringBuffer &s) const override
  907. {
  908. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) xpath, xpathCtx);
  909. if (eval)
  910. {
  911. xmlChar *v = xmlXPathCastToString(eval);
  912. xmlXPathFreeObject(eval);
  913. if (v)
  914. {
  915. if (*v)
  916. s.append((const char *)v);
  917. xmlFree(v);
  918. return s;
  919. }
  920. }
  921. return nullptr;
  922. }
  923. __int64 getXPathInt64(const char *xpath, __int64 dft=0) const override
  924. {
  925. __int64 ret = dft;
  926. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) xpath, xpathCtx);
  927. if (eval)
  928. {
  929. xmlChar *val = xmlXPathCastToString(eval);
  930. xmlXPathFreeObject(eval);
  931. if (val)
  932. {
  933. if (*val)
  934. ret = _atoi64((const char *)val);
  935. xmlFree(val);
  936. }
  937. }
  938. return ret;
  939. }
  940. bool getXPathBool(const char *xpath, bool dft=false) const override
  941. {
  942. bool ret = dft;
  943. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) xpath, xpathCtx);
  944. if (eval)
  945. {
  946. xmlChar *val = xmlXPathCastToString(eval);
  947. xmlXPathFreeObject(eval);
  948. if (val)
  949. {
  950. if (*val)
  951. ret = strToBool((const char *)val);
  952. xmlFree(val);
  953. }
  954. }
  955. return ret;
  956. }
  957. xmlNodePtr getSection(const char *name)
  958. {
  959. return getSectionNode(name, nullptr);
  960. }
  961. xmlNodePtr ensureSection(const char *name)
  962. {
  963. sanityCheckSectionName(name);
  964. xmlNodePtr sect = getSection(name);
  965. if (sect)
  966. return sect;
  967. return xmlNewChild(root, nullptr, (const xmlChar *) name, nullptr);
  968. }
  969. void removeSection(const char *name)
  970. {
  971. sanityCheckSectionName(name);
  972. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) name, xpathCtx);
  973. if (!eval)
  974. return;
  975. //should be just one node, but if there are more, clean them up anyway
  976. xmlNodeSetPtr ns = eval->nodesetval;
  977. if (XPATH_NODESET==eval->type && !xmlXPathNodeSetIsEmpty(ns))
  978. {
  979. //shouldn't matter here, but reverse order ensures children are deleted before parents
  980. for(int i=xmlXPathNodeSetGetLength(ns)-1; i>=0; i--)
  981. {
  982. xmlNodePtr node = xmlXPathNodeSetItem(ns, i);
  983. if (!node)
  984. continue;
  985. xmlUnlinkNode(node);
  986. xmlFreeNode(node);
  987. ns->nodeTab[i]=nullptr;
  988. }
  989. }
  990. xmlXPathFreeObject(eval);
  991. }
  992. xmlNodePtr replaceSection(const char *name)
  993. {
  994. removeSection(name);
  995. return xmlNewChild(root, nullptr, (const xmlChar *) name, nullptr);
  996. }
  997. virtual void setContent(const char *section, const char *xml) override
  998. {
  999. xmlNodePtr sect = replaceSection(section);
  1000. if (xml==nullptr) //means delete content
  1001. return;
  1002. xmlParserCtxtPtr parserCtx = xmlCreateDocParserCtxt((const unsigned char *)xml);
  1003. if (!parserCtx)
  1004. throw MakeStringException(-1, "CEsdlScriptContext:setContent: Unable to init parse of %s XML content", section);
  1005. parserCtx->node = sect;
  1006. xmlParseDocument(parserCtx);
  1007. int wellFormed = parserCtx->wellFormed;
  1008. xmlFreeDoc(parserCtx->myDoc); //dummy document
  1009. xmlFreeParserCtxt(parserCtx);
  1010. if (!wellFormed)
  1011. throw MakeStringException(-1, "CEsdlScriptContext:setContent xml string: Unable to parse %s XML content", section);
  1012. }
  1013. virtual void setContent(const char *section, IPropertyTree *tree) override
  1014. {
  1015. xmlNodePtr sect = replaceSection(section);
  1016. if (tree==nullptr) //means delete content
  1017. return;
  1018. addChildFromPtree(sect, *tree, tree->queryName());
  1019. }
  1020. virtual void setAttribute(const char *section, const char *name, const char *value) override
  1021. {
  1022. xmlNodePtr sect = ensureSection(section);
  1023. if (value)
  1024. xmlSetProp(sect, (const xmlChar *)name, (const xmlChar *)value);
  1025. else
  1026. xmlUnsetProp(sect, (const xmlChar *)name);
  1027. }
  1028. virtual const char *queryAttribute(const char *section, const char *name) override
  1029. {
  1030. xmlNodePtr sect = getSection(section);
  1031. if (!sect)
  1032. return nullptr;
  1033. return queryAttrValue(sect, name);
  1034. }
  1035. virtual const char *getAttribute(const char *section, const char *name, StringBuffer &s) override
  1036. {
  1037. xmlNodePtr sect = getSection(section);
  1038. if (!sect)
  1039. return nullptr;
  1040. xmlChar *val = xmlGetProp(sect, (const xmlChar *)name);
  1041. if (!val)
  1042. return nullptr;
  1043. s.append((const char *)val);
  1044. xmlFree(val);
  1045. return s;
  1046. }
  1047. virtual void toXML(StringBuffer &xml, const char *section, bool includeParentNode=false) override
  1048. {
  1049. xmlNodePtr sect = root;
  1050. if (!isEmptyString(section))
  1051. {
  1052. sect = getSectionNode(section, includeParentNode ? nullptr : "*[1]");
  1053. if (!sect)
  1054. return;
  1055. }
  1056. xmlOutputBufferPtr xmlOut = xmlAllocOutputBuffer(nullptr);
  1057. xmlNodeDumpOutput(xmlOut, sect->doc, sect, 0, 1, nullptr);
  1058. xmlOutputBufferFlush(xmlOut);
  1059. xmlBufPtr buf = (xmlOut->conv != nullptr) ? xmlOut->conv : xmlOut->buffer;
  1060. if (xmlBufUse(buf))
  1061. xml.append(xmlBufUse(buf), (const char *)xmlBufContent(buf));
  1062. xmlOutputBufferClose(xmlOut);
  1063. }
  1064. virtual void toXML(StringBuffer &xml) override
  1065. {
  1066. toXML(xml, nullptr, true);
  1067. }
  1068. IXpathContext* createXpathContext(const char *section, bool strictParameterDeclaration) override
  1069. {
  1070. xmlNodePtr sect = getSectionNode(section);
  1071. if (!sect)
  1072. throw MakeStringException(-1, "CEsdlScriptContext:createXpathContext: section not found %s", section);
  1073. CLibXpathContext *xpathContext = new CLibXpathContext(doc, sect, strictParameterDeclaration);
  1074. StringBuffer val;
  1075. xpathContext->addVariable("method", getAttribute("esdl", "method", val));
  1076. xpathContext->addVariable("service", getAttribute("esdl", "service", val.clear()));
  1077. xpathContext->addVariable("request", getAttribute("esdl", "request", val.clear()));
  1078. //external parameters need <es:param> statements to make them accessible (in strict mode)
  1079. addXpathCtxConfigInputs(xpathContext);
  1080. if (!strictParameterDeclaration)
  1081. xpathContext->declareRemainingInputs();
  1082. return xpathContext;
  1083. }
  1084. };
  1085. IEsdlScriptContext *createEsdlScriptContext(void * espCtx)
  1086. {
  1087. return new CEsdlScriptContext(espCtx);
  1088. }
  1089. extern IXpathContext* getXpathContext(const char * xmldoc, bool strictParameterDeclaration, bool removeDocNamespaces)
  1090. {
  1091. return new CLibXpathContext(xmldoc, strictParameterDeclaration, removeDocNamespaces);
  1092. }