libxml_xpathprocessor.cpp 28 KB

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