libxml_xpathprocessor.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780
  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 <memory>
  35. static inline char *skipWS(char *s)
  36. {
  37. while (isspace(*s)) s++;
  38. return s;
  39. }
  40. static char *markEndGetNext(char *line)
  41. {
  42. char *end = (char *)strchr(line, '\n');
  43. if (!end)
  44. return nullptr;
  45. *end=0;
  46. if (isEmptyString(++end))
  47. return nullptr;
  48. return end;
  49. }
  50. static char *extractFromLineGetNext(StringArray &functions, StringArray &variables, char *line)
  51. {
  52. line = skipWS(line);
  53. if (isEmptyString(line))
  54. return nullptr;
  55. char *next = markEndGetNext(line);
  56. if (strncmp(line, "FUNCTION", 8)==0)
  57. {
  58. char *paren = (char *)strchr(line, '(');
  59. if (paren)
  60. *paren=0;
  61. functions.append(skipWS(line+8+1));
  62. }
  63. else if (strncmp(line, "VARIABLE", 8)==0)
  64. {
  65. variables.append(skipWS(line+8+1));
  66. }
  67. return next;
  68. }
  69. class CLibCompiledXpath : public CInterfaceOf<ICompiledXpath>
  70. {
  71. private:
  72. xmlXPathCompExprPtr m_compiledXpathExpression = nullptr;
  73. StringBuffer m_xpath;
  74. ReadWriteLock m_rwlock;
  75. public:
  76. CLibCompiledXpath(const char * xpath)
  77. {
  78. m_xpath.set(xpath);
  79. m_compiledXpathExpression = xmlXPathCompile(BAD_CAST m_xpath.str());
  80. }
  81. ~CLibCompiledXpath()
  82. {
  83. xmlXPathFreeCompExpr(m_compiledXpathExpression);
  84. }
  85. const char * getXpath()
  86. {
  87. return m_xpath.str();
  88. }
  89. xmlXPathCompExprPtr getCompiledXPathExpression()
  90. {
  91. return m_compiledXpathExpression;
  92. }
  93. virtual void extractReferences(StringArray &functions, StringArray &variables) override
  94. {
  95. char *buf = nullptr;
  96. size_t len = 0;
  97. FILE *stream = open_memstream(&buf, &len);
  98. if (stream == nullptr)
  99. return;
  100. xmlXPathDebugDumpCompExpr(stream, m_compiledXpathExpression, 0);
  101. fputc(0, stream);
  102. fflush(stream);
  103. fclose (stream);
  104. char *line = buf;
  105. while (line)
  106. line = extractFromLineGetNext(functions, variables, line);
  107. free (buf);
  108. }
  109. };
  110. static xmlXPathObjectPtr variableLookupFunc(void *data, const xmlChar *name, const xmlChar *ns_uri);
  111. typedef std::map<std::string, xmlXPathObjectPtr> XPathObjectMap;
  112. class CLibXpathScope
  113. {
  114. public:
  115. StringAttr name; //in future allow named parent access?
  116. XPathObjectMap variables;
  117. public:
  118. CLibXpathScope(const char *_name) : name(_name){}
  119. ~CLibXpathScope()
  120. {
  121. for (XPathObjectMap::iterator it=variables.begin(); it!=variables.end(); ++it)
  122. xmlXPathFreeObject(it->second);
  123. }
  124. bool setObject(const char *key, xmlXPathObjectPtr obj)
  125. {
  126. std::pair<XPathObjectMap::iterator,bool> ret = variables.emplace(key, obj);
  127. if (ret.second==true)
  128. return true;
  129. //within scope, behave exactly like xmlXPathContext variables are added now, which seems to be that they are replaced
  130. //if we're preventing replacing variables we need to handle elsewhere
  131. // and still replace external values when treated as xsdl:variables, but not when treated as xsdl:params
  132. if (ret.first->second)
  133. xmlXPathFreeObject(ret.first->second);
  134. ret.first->second = obj;
  135. return true;
  136. }
  137. xmlXPathObjectPtr getObject(const char *key)
  138. {
  139. XPathObjectMap::iterator it = variables.find(key);
  140. if (it == variables.end())
  141. return nullptr;
  142. return it->second;
  143. }
  144. };
  145. typedef std::vector<std::unique_ptr<CLibXpathScope>> XPathScopeVector;
  146. typedef std::map<std::string, ICompiledXpath*> XPathInputMap;
  147. class XpathContextState
  148. {
  149. private:
  150. xmlDocPtr doc = nullptr;
  151. xmlNodePtr node = nullptr;
  152. int contextSize = 0;
  153. int proximityPosition = 0;
  154. public:
  155. XpathContextState(xmlXPathContextPtr ctx)
  156. {
  157. doc = ctx->doc;
  158. node = ctx->node;
  159. contextSize = ctx->contextSize;
  160. proximityPosition = ctx->proximityPosition;
  161. }
  162. void restore(xmlXPathContextPtr ctx)
  163. {
  164. ctx->doc = doc;
  165. ctx->node = node;
  166. ctx->contextSize = contextSize;
  167. ctx->proximityPosition = proximityPosition;
  168. }
  169. };
  170. typedef std::vector<XpathContextState> XPathContextStateVector;
  171. class CLibXpathContext : public CInterfaceOf<IXpathContext>
  172. {
  173. public:
  174. XPathInputMap provided;
  175. xmlDocPtr m_xmlDoc = nullptr;
  176. xmlXPathContextPtr m_xpathContext = nullptr;
  177. ReadWriteLock m_rwlock;
  178. XPathScopeVector scopes;
  179. bool strictParameterDeclaration = true;
  180. //saved state
  181. XPathContextStateVector saved;
  182. public:
  183. CLibXpathContext(const char * xmldoc, bool _strictParameterDeclaration) : strictParameterDeclaration(_strictParameterDeclaration)
  184. {
  185. beginScope("/");
  186. setXmlDoc(xmldoc);
  187. exsltDateXpathCtxtRegister(m_xpathContext, (xmlChar*)"date");
  188. exsltMathXpathCtxtRegister(m_xpathContext, (xmlChar*)"math");
  189. exsltSetsXpathCtxtRegister(m_xpathContext, (xmlChar*)"set");
  190. exsltStrXpathCtxtRegister(m_xpathContext, (xmlChar*)"str");
  191. }
  192. ~CLibXpathContext()
  193. {
  194. for (XPathInputMap::iterator it=provided.begin(); it!=provided.end(); ++it)
  195. it->second->Release();
  196. xmlXPathFreeContext(m_xpathContext);
  197. xmlFreeDoc(m_xmlDoc);
  198. }
  199. void pushLocation()
  200. {
  201. WriteLockBlock wblock(m_rwlock);
  202. saved.emplace_back(XpathContextState(m_xpathContext));
  203. }
  204. void setLocation(xmlDocPtr doc, xmlNodePtr node, int contextSize, int proximityPosition)
  205. {
  206. WriteLockBlock wblock(m_rwlock);
  207. m_xpathContext->doc = doc;
  208. m_xpathContext->node = node;
  209. m_xpathContext->contextSize = contextSize;
  210. m_xpathContext->proximityPosition = proximityPosition;
  211. }
  212. void setLocation(xmlXPathContextPtr ctx)
  213. {
  214. WriteLockBlock wblock(m_rwlock);
  215. m_xpathContext->doc = ctx->doc;
  216. m_xpathContext->node = ctx->node;
  217. m_xpathContext->contextSize = ctx->contextSize;
  218. m_xpathContext->proximityPosition = ctx->proximityPosition;
  219. }
  220. void popLocation()
  221. {
  222. WriteLockBlock wblock(m_rwlock);
  223. saved.back().restore(m_xpathContext);
  224. saved.pop_back();
  225. }
  226. void beginScope(const char *name) override
  227. {
  228. WriteLockBlock wblock(m_rwlock);
  229. scopes.emplace_back(new CLibXpathScope(name));
  230. }
  231. void endScope() override
  232. {
  233. WriteLockBlock wblock(m_rwlock);
  234. if (scopes.size()>1) //preserve root scope
  235. scopes.pop_back();
  236. }
  237. static void tableScanCallback(void *payload, void *data, xmlChar *name)
  238. {
  239. DBGLOG("k/v == [%s,%s]\n", (char *) name, (char *) payload);
  240. }
  241. virtual void registerNamespace(const char *prefix, const char * uri) override
  242. {
  243. if (m_xpathContext)
  244. {
  245. WriteLockBlock wblock(m_rwlock);
  246. xmlXPathRegisterNs(m_xpathContext, (const xmlChar *) prefix, (const xmlChar *) uri);
  247. }
  248. }
  249. virtual void registerFunction(const char *xmlns, const char * name, void *f) override
  250. {
  251. if (m_xpathContext)
  252. {
  253. WriteLockBlock wblock(m_rwlock);
  254. xmlXPathRegisterFuncNS(m_xpathContext, (const xmlChar *) name, (const xmlChar *) xmlns, (xmlXPathFunction) f);
  255. }
  256. }
  257. virtual void setUserData(void *userdata) override
  258. {
  259. if (m_xpathContext)
  260. {
  261. WriteLockBlock wblock(m_rwlock);
  262. m_xpathContext->userData = userdata;
  263. }
  264. }
  265. virtual void *getUserData() override
  266. {
  267. if (m_xpathContext)
  268. {
  269. ReadLockBlock wblock(m_rwlock);
  270. return m_xpathContext->userData;
  271. }
  272. return nullptr;
  273. }
  274. inline CLibXpathScope *getCurrentScope()
  275. {
  276. ReadLockBlock wblock(m_rwlock);
  277. assertex(scopes.size());
  278. return scopes.back().get();
  279. }
  280. xmlXPathObjectPtr findVariable(const char *name, const char *ns_uri, CLibXpathScope *scope)
  281. {
  282. const char *fullname = name;
  283. StringBuffer s;
  284. if (!isEmptyString(ns_uri))
  285. fullname = s.append(ns_uri).append(':').append(name).str();
  286. ReadLockBlock wblock(m_rwlock);
  287. xmlXPathObjectPtr obj = nullptr;
  288. if (scope)
  289. return scope->getObject(fullname);
  290. for (XPathScopeVector::const_reverse_iterator it=scopes.crbegin(); !obj && it!=scopes.crend(); ++it)
  291. obj = it->get()->getObject(fullname);
  292. //check libxml2 level variables, shouldn't happen currently but we may want to wrap existing xpathcontexts in the future
  293. if (!obj)
  294. obj = (xmlXPathObjectPtr)xmlHashLookup2(m_xpathContext->varHash, (const xmlChar *)name, (const xmlChar *)ns_uri);
  295. return obj;
  296. }
  297. xmlXPathObjectPtr getVariableObject(const char *name, const char *ns_uri, CLibXpathScope *scope)
  298. {
  299. return xmlXPathObjectCopy(findVariable(name, ns_uri, scope));
  300. }
  301. bool hasVariable(const char *name, const char *ns_uri, CLibXpathScope *scope)
  302. {
  303. return (findVariable(name, ns_uri, scope)!=nullptr);
  304. }
  305. virtual bool addObjectVariable(const char * name, xmlXPathObjectPtr obj, CLibXpathScope *scope)
  306. {
  307. if (isEmptyString(name))
  308. return false;
  309. if (m_xpathContext)
  310. {
  311. if (!obj)
  312. throw MakeStringException(-1, "addObjectVariable %s error", name);
  313. WriteLockBlock wblock(m_rwlock);
  314. if (!scope && !scopes.empty())
  315. scope = scopes.back().get();
  316. if (scope)
  317. return scope->setObject(name, obj);
  318. return xmlXPathRegisterVariable(m_xpathContext, (xmlChar *)name, obj) == 0;
  319. }
  320. return false;
  321. }
  322. bool addStringVariable(const char * name, const char * val, CLibXpathScope *scope)
  323. {
  324. if (!val)
  325. return false;
  326. return addObjectVariable(name, xmlXPathNewCString(val), scope);
  327. }
  328. virtual bool addXpathVariable(const char * name, const char * xpath, CLibXpathScope *scope)
  329. {
  330. if (isEmptyString(xpath))
  331. addVariable(name, "");
  332. if (m_xpathContext)
  333. {
  334. xmlXPathObjectPtr obj = evaluate(xpath);
  335. if (!obj)
  336. throw MakeStringException(-1, "addXpathVariable xpath error %s", xpath);
  337. return addObjectVariable(name, obj, scope);
  338. }
  339. return false;
  340. }
  341. bool addCompiledVariable(const char * name, ICompiledXpath * compiled, CLibXpathScope *scope)
  342. {
  343. if (!compiled)
  344. addVariable(name, "");
  345. if (m_xpathContext)
  346. {
  347. CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiled);
  348. xmlXPathObjectPtr obj = evaluate(clibCompiledXpath->getCompiledXPathExpression(), clibCompiledXpath->getXpath());
  349. if (!obj)
  350. throw MakeStringException(-1, "addEvaluateVariable xpath error %s", clibCompiledXpath->getXpath());
  351. return addObjectVariable(name, obj, scope);
  352. }
  353. return false;
  354. }
  355. virtual bool addInputValue(const char * name, const char * value) override
  356. {
  357. if (isEmptyString(name)||isEmptyString(value))
  358. return false;
  359. VStringBuffer xpath("'%s'", value);
  360. return addInputXpath(name, xpath);
  361. }
  362. virtual bool addInputXpath(const char * name, const char * xpath) override
  363. {
  364. if (isEmptyString(name)||isEmptyString(xpath))
  365. return false;
  366. Owned<ICompiledXpath> compiled = compileXpath(xpath);
  367. if (compiled)
  368. {
  369. WriteLockBlock wblock(m_rwlock);
  370. provided.emplace(name, compiled.getClear());
  371. return true;
  372. }
  373. return false;
  374. }
  375. inline ICompiledXpath *findInput(const char *name)
  376. {
  377. ReadLockBlock rblock(m_rwlock);
  378. XPathInputMap::iterator it = provided.find(name);
  379. if (it == provided.end())
  380. return nullptr;
  381. return it->second;
  382. }
  383. virtual bool declareCompiledParameter(const char * name, ICompiledXpath * compiled) override
  384. {
  385. if (hasVariable(name, nullptr, getCurrentScope()))
  386. return false;
  387. //use input value
  388. ICompiledXpath *inputxp = findInput(name);
  389. if (inputxp)
  390. return addCompiledVariable(name, inputxp, getCurrentScope());
  391. //use default provided
  392. return addCompiledVariable(name, compiled, getCurrentScope());
  393. }
  394. virtual void declareRemainingInputs() override
  395. {
  396. for (XPathInputMap::iterator it=provided.begin(); it!=provided.end(); ++it)
  397. declareCompiledParameter(it->first.c_str(), it->second);
  398. }
  399. virtual bool declareParameter(const char * name, const char *value) override
  400. {
  401. if (hasVariable(name, nullptr, getCurrentScope()))
  402. return false;
  403. //use input value
  404. ICompiledXpath *input = findInput(name);
  405. if (input)
  406. return addCompiledVariable(name, input, getCurrentScope());
  407. //use default provided
  408. return addStringVariable(name, value, getCurrentScope());
  409. }
  410. virtual bool addXpathVariable(const char * name, const char * xpath) override
  411. {
  412. return addXpathVariable(name, xpath, nullptr);
  413. }
  414. virtual bool addVariable(const char * name, const char * val) override
  415. {
  416. return addStringVariable(name, val, nullptr);
  417. }
  418. virtual bool addCompiledVariable(const char * name, ICompiledXpath * compiled) override
  419. {
  420. return addCompiledVariable(name, compiled, nullptr);
  421. }
  422. virtual const char * getVariable(const char * name, StringBuffer & variable) override
  423. {
  424. if (m_xpathContext)
  425. {
  426. ReadLockBlock rblock(m_rwlock);
  427. xmlXPathObjectPtr ptr = xmlXPathVariableLookupNS(m_xpathContext, (const xmlChar *)name, nullptr);
  428. if (!ptr)
  429. return nullptr;
  430. variable.append((const char *) ptr->stringval);
  431. xmlXPathFreeObject(ptr);
  432. return variable;
  433. }
  434. return nullptr;
  435. }
  436. virtual IXpathContextIterator *evaluateAsNodeSet(ICompiledXpath * compiled) override
  437. {
  438. CLibCompiledXpath * clCompiled = static_cast<CLibCompiledXpath *>(compiled);
  439. if (!clCompiled)
  440. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsNodeSet: Error: Could not evaluate XPATH");
  441. return evaluateAsNodeSet(evaluate(clCompiled->getCompiledXPathExpression(), compiled->getXpath()), compiled->getXpath());
  442. }
  443. IXpathContextIterator *evaluateAsNodeSet(xmlXPathObjectPtr evaluated, const char* xpath);
  444. virtual bool evaluateAsBoolean(const char * xpath) override
  445. {
  446. if (!xpath || !*xpath)
  447. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsBoolean: Error: Could not evaluate empty XPATH");
  448. return evaluateAsBoolean(evaluate(xpath), xpath);
  449. }
  450. virtual bool evaluateAsString(const char * xpath, StringBuffer & evaluated) override
  451. {
  452. if (!xpath || !*xpath)
  453. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate empty XPATH");
  454. return evaluateAsString(evaluate(xpath), evaluated, xpath);
  455. }
  456. virtual bool evaluateAsBoolean(ICompiledXpath * compiledXpath) override
  457. {
  458. CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  459. if (!clibCompiledXpath)
  460. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsBoolean: Error: Missing compiled XPATH");
  461. return evaluateAsBoolean(evaluate(clibCompiledXpath->getCompiledXPathExpression(), compiledXpath->getXpath()), compiledXpath->getXpath());
  462. }
  463. virtual const char * evaluateAsString(ICompiledXpath * compiledXpath, StringBuffer & evaluated) override
  464. {
  465. CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  466. if (!clibCompiledXpath)
  467. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsString: Error: Missing compiled XPATH");
  468. return evaluateAsString(evaluate(clibCompiledXpath->getCompiledXPathExpression(), compiledXpath->getXpath()), evaluated, compiledXpath->getXpath());
  469. }
  470. virtual double evaluateAsNumber(ICompiledXpath * compiledXpath) override
  471. {
  472. CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  473. if (!clibCompiledXpath)
  474. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsNumber: Error: Missing compiled XPATH");
  475. return evaluateAsNumber(evaluate(clibCompiledXpath->getCompiledXPathExpression(), compiledXpath->getXpath()), compiledXpath->getXpath());
  476. }
  477. virtual bool setXmlDoc(const char * xmldoc) override
  478. {
  479. if (isEmptyString(xmldoc))
  480. return false;
  481. xmlDocPtr doc = xmlParseDoc((const unsigned char *)xmldoc);
  482. if (doc == nullptr)
  483. {
  484. ERRLOG("XpathProcessor:setXmlDoc Error: Unable to parse XMLLib document");
  485. return false;
  486. }
  487. return setContextDocument(doc, xmlDocGetRootElement(doc));
  488. }
  489. private:
  490. bool setContextDocument(xmlDocPtr doc, xmlNodePtr node)
  491. {
  492. WriteLockBlock rblock(m_rwlock);
  493. m_xmlDoc = doc;
  494. m_xpathContext = xmlXPathNewContext(m_xmlDoc);
  495. if(m_xpathContext == nullptr)
  496. {
  497. ERRLOG("XpathProcessor:setContextDocument: Error: Unable to create new XMLLib XPath context");
  498. return false;
  499. }
  500. //relative paths need something to be relative to
  501. xmlXPathSetContextNode(node, m_xpathContext);
  502. xmlXPathRegisterVariableLookup(m_xpathContext, variableLookupFunc, this);
  503. return true;
  504. }
  505. bool evaluateAsBoolean(xmlXPathObjectPtr evaluatedXpathObj, const char* xpath)
  506. {
  507. if (!evaluatedXpathObj)
  508. {
  509. throw MakeStringException(XPATHERR_InvalidInput, "XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s'", xpath);
  510. }
  511. if (XPATH_BOOLEAN != evaluatedXpathObj->type)
  512. {
  513. xmlXPathFreeObject(evaluatedXpathObj);
  514. throw MakeStringException(XPATHERR_UnexpectedInput, "XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s' as Boolean", xpath);
  515. }
  516. bool bresult = evaluatedXpathObj->boolval;
  517. xmlXPathFreeObject(evaluatedXpathObj);
  518. return bresult;
  519. }
  520. const char* evaluateAsString(xmlXPathObjectPtr evaluatedXpathObj, StringBuffer& evaluated, const char* xpath)
  521. {
  522. if (!evaluatedXpathObj)
  523. throw MakeStringException(XPATHERR_InvalidInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s'", xpath);
  524. evaluated.clear();
  525. switch (evaluatedXpathObj->type)
  526. {
  527. case XPATH_NODESET:
  528. {
  529. xmlNodeSetPtr nodes = evaluatedXpathObj->nodesetval;
  530. for (int i = 0; i < nodes->nodeNr; i++)
  531. {
  532. xmlNodePtr nodeTab = nodes->nodeTab[i];
  533. auto nodeContent = xmlNodeGetContent(nodeTab);
  534. evaluated.append((const char *)nodeContent);
  535. xmlFree(nodeContent);
  536. }
  537. break;
  538. }
  539. case XPATH_BOOLEAN:
  540. case XPATH_NUMBER:
  541. case XPATH_STRING:
  542. case XPATH_POINT:
  543. case XPATH_RANGE:
  544. case XPATH_LOCATIONSET:
  545. case XPATH_USERS:
  546. case XPATH_XSLT_TREE:
  547. {
  548. evaluatedXpathObj = xmlXPathConvertString (evaluatedXpathObj); //existing object is freed
  549. if (!evaluatedXpathObj)
  550. throw MakeStringException(XPATHERR_UnexpectedInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s'; could not convert result to string", xpath);
  551. evaluated.append(evaluatedXpathObj->stringval);
  552. break;
  553. }
  554. default:
  555. {
  556. xmlXPathFreeObject(evaluatedXpathObj);
  557. throw MakeStringException(XPATHERR_UnexpectedInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s' as string; unexpected type %d", xpath, evaluatedXpathObj->type);
  558. break;
  559. }
  560. }
  561. xmlXPathFreeObject(evaluatedXpathObj);
  562. return evaluated.str();
  563. }
  564. double evaluateAsNumber(xmlXPathObjectPtr evaluatedXpathObj, const char* xpath)
  565. {
  566. if (!evaluatedXpathObj)
  567. throw MakeStringException(XPATHERR_InvalidInput,"XpathProcessor:evaluateAsNumber: Error: Could not evaluate XPATH '%s'", xpath);
  568. double ret = xmlXPathCastToNumber(evaluatedXpathObj);
  569. xmlXPathFreeObject(evaluatedXpathObj);
  570. return ret;
  571. }
  572. virtual xmlXPathObjectPtr evaluate(xmlXPathCompExprPtr compiled, const char *xpath)
  573. {
  574. xmlXPathObjectPtr evaluatedXpathObj = nullptr;
  575. if (compiled)
  576. {
  577. ReadLockBlock rlock(m_rwlock);
  578. if ( m_xpathContext)
  579. {
  580. evaluatedXpathObj = xmlXPathCompiledEval(compiled, m_xpathContext);
  581. }
  582. else
  583. {
  584. throw MakeStringException(XPATHERR_InvalidState,"XpathProcessor:evaluate: Error: Could not evaluate XPATH '%s'", xpath);
  585. }
  586. }
  587. return evaluatedXpathObj;
  588. }
  589. virtual xmlXPathObjectPtr evaluate(const char * xpath)
  590. {
  591. xmlXPathObjectPtr evaluatedXpathObj = nullptr;
  592. if (xpath && *xpath)
  593. {
  594. ReadLockBlock rlock(m_rwlock);
  595. if ( m_xpathContext)
  596. {
  597. evaluatedXpathObj = xmlXPathEval((const xmlChar *)xpath, m_xpathContext);
  598. }
  599. else
  600. {
  601. throw MakeStringException(XPATHERR_InvalidState,"XpathProcessor:evaluate: Error: Could not evaluate XPATH '%s'; ensure xmldoc has been set", xpath);
  602. }
  603. }
  604. return evaluatedXpathObj;
  605. }
  606. };
  607. class XPathNodeSetIterator : public CInterfaceOf<IXpathContextIterator>
  608. {
  609. public:
  610. CLibXpathContext *context;
  611. xmlNodeSetPtr list;
  612. unsigned pos = 0;
  613. public:
  614. XPathNodeSetIterator(CLibXpathContext *xpctx, xmlNodeSetPtr nodeset) : context(xpctx), list(nodeset)
  615. {
  616. context->pushLocation();
  617. context->m_xpathContext->contextSize = xmlXPathNodeSetGetLength(list);
  618. }
  619. virtual ~XPathNodeSetIterator()
  620. {
  621. context->popLocation();
  622. if (list)
  623. xmlXPathFreeNodeSet(list);
  624. }
  625. bool update(int newpos)
  626. {
  627. pos = newpos;
  628. if (!isValid())
  629. return false;
  630. xmlNodePtr node = xmlXPathNodeSetItem(list, pos);;
  631. context->m_xpathContext->node = node;
  632. if ((node->type != XML_NAMESPACE_DECL && node->doc != nullptr))
  633. context->m_xpathContext->doc = node->doc;
  634. context->m_xpathContext->proximityPosition = pos + 1;
  635. return true;
  636. }
  637. virtual bool first()
  638. {
  639. if (xmlXPathNodeSetGetLength(list)==0)
  640. return false;
  641. return update(0);
  642. }
  643. virtual bool next()
  644. {
  645. return update(pos+1);
  646. }
  647. virtual bool isValid()
  648. {
  649. return (pos < xmlXPathNodeSetGetLength(list));
  650. }
  651. virtual IXpathContext & query()
  652. {
  653. return *context;
  654. }
  655. };
  656. IXpathContextIterator *CLibXpathContext::evaluateAsNodeSet(xmlXPathObjectPtr evaluated, const char* xpath)
  657. {
  658. if (!evaluated)
  659. {
  660. throw MakeStringException(XPATHERR_InvalidInput, "XpathProcessor:evaluateAsNodeSet: Error: Could not evaluate XPATH '%s'", xpath);
  661. }
  662. if (XPATH_NODESET != evaluated->type)
  663. {
  664. xmlXPathFreeObject(evaluated);
  665. throw MakeStringException(XPATHERR_UnexpectedInput, "XpathProcessor:evaluateAsNodeSet: Error: Could not evaluate XPATH '%s' as NodeSet", xpath);
  666. }
  667. xmlNodeSetPtr ns = evaluated->nodesetval;
  668. evaluated->nodesetval = nullptr;
  669. xmlXPathFreeObject(evaluated);
  670. return new XPathNodeSetIterator(this, ns);
  671. }
  672. static xmlXPathObjectPtr variableLookupFunc(void *data, const xmlChar *name, const xmlChar *ns_uri)
  673. {
  674. CLibXpathContext *ctxt = (CLibXpathContext *) data;
  675. if (!ctxt)
  676. return nullptr;
  677. return ctxt->getVariableObject((const char *)name, (const char *)ns_uri, nullptr);
  678. }
  679. extern ICompiledXpath* compileXpath(const char * xpath)
  680. {
  681. return new CLibCompiledXpath(xpath);
  682. }
  683. extern IXpathContext* getXpathContext(const char * xmldoc, bool strictParameterDeclaration)
  684. {
  685. return new CLibXpathContext(xmldoc, strictParameterDeclaration);
  686. }