libxml_xpathprocessor.cpp 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760
  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>, implements IVariableSubstitutionHelper
  177. {
  178. public:
  179. XPathInputMap provided;
  180. xmlDocPtr m_xmlDoc = nullptr;
  181. xmlXPathContextPtr m_xpathContext = nullptr;
  182. ReadWriteLock m_rwlock;
  183. XPathScopeVector scopes;
  184. Owned<CLibXpathContext> primaryContext; //if set will lookup variables from primaryContext (secondary context is generally target not source)
  185. bool strictParameterDeclaration = true;
  186. bool removeDocNamespaces = false;
  187. bool ownedDoc = false;
  188. //saved state
  189. XPathContextStateVector saved;
  190. public:
  191. IMPLEMENT_IINTERFACE_USING(CInterfaceOf<IXpathContext>)
  192. CLibXpathContext(const char * xmldoc, bool _strictParameterDeclaration, bool removeDocNs) : strictParameterDeclaration(_strictParameterDeclaration), removeDocNamespaces(removeDocNs)
  193. {
  194. xmlKeepBlanksDefault(0);
  195. beginScope("/");
  196. setXmlDoc(xmldoc);
  197. registerExslt();
  198. }
  199. CLibXpathContext(CLibXpathContext *_primary, xmlDocPtr doc, xmlNodePtr node, bool _strictParameterDeclaration) : strictParameterDeclaration(_strictParameterDeclaration)
  200. {
  201. xmlKeepBlanksDefault(0);
  202. primaryContext.set(_primary);
  203. beginScope("/");
  204. setContextDocument(doc, node);
  205. registerExslt();
  206. }
  207. ~CLibXpathContext()
  208. {
  209. for (XPathInputMap::iterator it=provided.begin(); it!=provided.end(); ++it)
  210. it->second->Release();
  211. xmlXPathFreeContext(m_xpathContext);
  212. if (ownedDoc)
  213. xmlFreeDoc(m_xmlDoc);
  214. }
  215. void registerExslt()
  216. {
  217. exsltDateXpathCtxtRegister(m_xpathContext, (xmlChar*)"date");
  218. exsltMathXpathCtxtRegister(m_xpathContext, (xmlChar*)"math");
  219. exsltSetsXpathCtxtRegister(m_xpathContext, (xmlChar*)"set");
  220. exsltStrXpathCtxtRegister(m_xpathContext, (xmlChar*)"str");
  221. }
  222. void pushLocation() override
  223. {
  224. WriteLockBlock wblock(m_rwlock);
  225. saved.emplace_back(XpathContextState(m_xpathContext));
  226. }
  227. void setLocation(xmlDocPtr doc, xmlNodePtr node, int contextSize, int proximityPosition)
  228. {
  229. WriteLockBlock wblock(m_rwlock);
  230. m_xpathContext->doc = doc;
  231. m_xpathContext->node = node;
  232. m_xpathContext->contextSize = contextSize;
  233. m_xpathContext->proximityPosition = proximityPosition;
  234. }
  235. void setLocation(xmlXPathContextPtr ctx)
  236. {
  237. WriteLockBlock wblock(m_rwlock);
  238. m_xpathContext->doc = ctx->doc;
  239. m_xpathContext->node = ctx->node;
  240. m_xpathContext->contextSize = ctx->contextSize;
  241. m_xpathContext->proximityPosition = ctx->proximityPosition;
  242. }
  243. void popLocation() override
  244. {
  245. WriteLockBlock wblock(m_rwlock);
  246. saved.back().restore(m_xpathContext);
  247. saved.pop_back();
  248. }
  249. void beginScope(const char *name) override
  250. {
  251. WriteLockBlock wblock(m_rwlock);
  252. scopes.emplace_back(new CLibXpathScope(name));
  253. }
  254. void endScope() override
  255. {
  256. WriteLockBlock wblock(m_rwlock);
  257. if (scopes.size()>1) //preserve root scope
  258. scopes.pop_back();
  259. }
  260. static void tableScanCallback(void *payload, void *data, xmlChar *name)
  261. {
  262. DBGLOG("k/v == [%s,%s]\n", (char *) name, (char *) payload);
  263. }
  264. virtual void registerNamespace(const char *prefix, const char * uri) override
  265. {
  266. if (m_xpathContext)
  267. {
  268. WriteLockBlock wblock(m_rwlock);
  269. xmlXPathRegisterNs(m_xpathContext, (const xmlChar *) prefix, (const xmlChar *) uri);
  270. }
  271. }
  272. virtual const char *queryNamespace(const char *prefix) override
  273. {
  274. if (!m_xpathContext)
  275. return nullptr;
  276. WriteLockBlock wblock(m_rwlock);
  277. return (const char *) xmlXPathNsLookup(m_xpathContext, (const xmlChar *)prefix);
  278. }
  279. virtual void registerFunction(const char *xmlns, const char * name, void *f) override
  280. {
  281. if (m_xpathContext)
  282. {
  283. WriteLockBlock wblock(m_rwlock);
  284. xmlXPathRegisterFuncNS(m_xpathContext, (const xmlChar *) name, (const xmlChar *) xmlns, (xmlXPathFunction) f);
  285. }
  286. }
  287. virtual void setUserData(void *userdata) override
  288. {
  289. if (m_xpathContext)
  290. {
  291. WriteLockBlock wblock(m_rwlock);
  292. m_xpathContext->userData = userdata;
  293. }
  294. }
  295. virtual void *getUserData() override
  296. {
  297. if (m_xpathContext)
  298. {
  299. ReadLockBlock wblock(m_rwlock);
  300. return m_xpathContext->userData;
  301. }
  302. return nullptr;
  303. }
  304. inline CLibXpathScope *getCurrentScope()
  305. {
  306. ReadLockBlock wblock(m_rwlock);
  307. assertex(scopes.size());
  308. return scopes.back().get();
  309. }
  310. bool findVariable(const char *name, StringBuffer &s) override
  311. {
  312. xmlXPathObjectPtr obj = findVariable(name, nullptr, nullptr);
  313. return append(s, obj);
  314. }
  315. xmlXPathObjectPtr findVariable(const char *name, const char *ns_uri, CLibXpathScope *scope)
  316. {
  317. xmlXPathObjectPtr obj = nullptr;
  318. if (primaryContext)
  319. {
  320. //could always return from here, but may find a use for secondary variables, they currently can't be defined from esdl script, so can't hurt there
  321. obj = primaryContext->findVariable(name, ns_uri, scope);
  322. if (obj)
  323. return obj;
  324. }
  325. const char *fullname = name;
  326. StringBuffer s;
  327. if (!isEmptyString(ns_uri))
  328. fullname = s.append(ns_uri).append(':').append(name).str();
  329. ReadLockBlock wblock(m_rwlock);
  330. if (scope)
  331. return scope->getObject(fullname);
  332. for (XPathScopeVector::const_reverse_iterator it=scopes.crbegin(); !obj && it!=scopes.crend(); ++it)
  333. obj = it->get()->getObject(fullname);
  334. //check libxml2 level variables, shouldn't happen currently but we may want to wrap existing xpathcontexts in the future
  335. if (!obj)
  336. obj = (xmlXPathObjectPtr)xmlHashLookup2(m_xpathContext->varHash, (const xmlChar *)name, (const xmlChar *)ns_uri);
  337. return obj;
  338. }
  339. xmlXPathObjectPtr getVariableObject(const char *name, const char *ns_uri, CLibXpathScope *scope)
  340. {
  341. return xmlXPathObjectCopy(findVariable(name, ns_uri, scope));
  342. }
  343. bool hasVariable(const char *name, const char *ns_uri, CLibXpathScope *scope)
  344. {
  345. return (findVariable(name, ns_uri, scope)!=nullptr);
  346. }
  347. void setLocation(xmlNodePtr node)
  348. {
  349. WriteLockBlock wblock(m_rwlock);
  350. m_xpathContext->doc = node->doc;
  351. m_xpathContext->node = node;
  352. m_xpathContext->contextSize = 0;
  353. m_xpathContext->proximityPosition = 0;
  354. }
  355. bool setLocation(xmlXPathObjectPtr obj, bool required, const char *xpath)
  356. {
  357. if (!obj)
  358. {
  359. if (required)
  360. throw MakeStringException(XPATHERR_InvalidInput,"XpathContext:setLocation: Error: Syntax error XPATH '%s'", xpath);
  361. return false;
  362. }
  363. if (obj->type!=XPATH_NODESET || !obj->nodesetval || obj->nodesetval->nodeNr==0)
  364. {
  365. xmlXPathFreeObject(obj);
  366. if (required)
  367. throw MakeStringException(XPATHERR_EvaluationFailed,"XpathContext:setLocation: Error: Could not evaluate XPATH '%s'", xpath);
  368. return false;
  369. }
  370. if (obj->nodesetval->nodeNr>1)
  371. {
  372. xmlXPathFreeObject(obj);
  373. if (required)
  374. throw MakeStringException(XPATHERR_EvaluationFailed,"XpathContext:setLocation: Error: ambiguous XPATH '%s'", xpath);
  375. return false;
  376. }
  377. xmlNodePtr location = obj->nodesetval->nodeTab[0];
  378. xmlXPathFreeObject(obj);
  379. setLocation(location);
  380. return true;
  381. }
  382. virtual bool setLocation(ICompiledXpath * compiledXpath, bool required) override
  383. {
  384. if (!compiledXpath)
  385. return false;
  386. CLibCompiledXpath * ccXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  387. if (!ccXpath)
  388. throw MakeStringException(XPATHERR_InvalidState,"XpathProcessor:setLocation: Error: BAD compiled XPATH");
  389. xmlXPathObjectPtr obj = evaluate(ccXpath->getCompiledXPathExpression(), compiledXpath->getXpath());
  390. return setLocation(obj, required, compiledXpath->getXpath());
  391. }
  392. virtual bool setLocation(const char *s, bool required) override
  393. {
  394. StringBuffer xpath;
  395. replaceVariables(xpath, s, false, this, "{$", "}");
  396. xmlXPathObjectPtr obj = evaluate(xpath);
  397. return setLocation(obj, required, xpath.str());
  398. }
  399. virtual void addElementToLocation(const char *name) override
  400. {
  401. if (!validateXMLTag(name))
  402. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:addElementToLocation: invalid name '%s'", name);
  403. xmlNodePtr location = m_xpathContext->node;
  404. if (!location)
  405. return;
  406. location = xmlNewChild(location, nullptr, (const xmlChar *) name, nullptr);
  407. if (!location)
  408. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:addElementToLocation: failed to add element '%s'", name);
  409. setLocation(location);
  410. }
  411. virtual void addXmlContent(const char *xml) override
  412. {
  413. xmlNodePtr location = m_xpathContext->node;
  414. xmlParserCtxtPtr parserCtx = xmlCreateDocParserCtxt((const xmlChar *)xml);
  415. if (!parserCtx)
  416. throw MakeStringException(-1, "CLibXpathContext:addXmlContent: Unable to parse xml");
  417. parserCtx->node = location;
  418. xmlParseDocument(parserCtx);
  419. int wellFormed = parserCtx->wellFormed;
  420. xmlFreeDoc(parserCtx->myDoc); //dummy document
  421. xmlFreeParserCtxt(parserCtx);
  422. if (!wellFormed)
  423. throw MakeStringException(-1, "XpathContext:addXmlContent: Unable to parse %s XML content", xml);
  424. }
  425. virtual bool ensureLocation(const char *xpath, bool required) override
  426. {
  427. if (isEmptyString(xpath))
  428. return true;
  429. if (strstr(xpath, "//")) //maybe in the future, scripting error rather than not found, required==false has no effect
  430. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureLocation: '//' not currently allowed '%s'", xpath);
  431. xmlNodePtr current = m_xpathContext->node;
  432. xmlNodePtr node = current;
  433. if (*xpath=='/')
  434. {
  435. xpath++;
  436. if (strncmp(xpath, "esdl_script_context/", 20)!=0)
  437. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureLocation: incorect first absolute path node '%s'", xpath);
  438. xpath+=20;
  439. node = xmlDocGetRootElement(m_xmlDoc);
  440. }
  441. StringArray xpathnodes;
  442. xpathnodes.appendList(xpath, "/");
  443. ForEachItemIn(i, xpathnodes)
  444. {
  445. const char *finger = xpathnodes.item(i);
  446. if (isEmptyString(finger)) //shouldn't happen, scripting error rather than not found, required==false has no effect
  447. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureLocation: '//' not allowed '%s'", xpath);
  448. if (*finger=='@')
  449. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureLocation: attribute cannot be selected '%s'", xpath);
  450. xmlXPathObjectPtr obj = xmlXPathNodeEval(node, (const xmlChar*) finger, m_xpathContext);
  451. xmlXPathSetContextNode(current, m_xpathContext); //restore
  452. if (!obj)
  453. throw MakeStringException(XPATHERR_InvalidInput,"XpathContext:ensureLocation: invalid xpath '%s' at node '%s'", xpath, finger);
  454. if (obj->type!=xmlXPathObjectType::XPATH_NODESET || !obj->nodesetval || obj->nodesetval->nodeNr==0)
  455. {
  456. if (*finger=='.' || strpbrk(finger, "[()]*") || strstr(finger, "::")) //complex nodes must already exist
  457. {
  458. if (required)
  459. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureLocation: xpath node not found '%s' in xpath '%s'", finger, xpath);
  460. return false;
  461. }
  462. node = xmlNewChild(node, nullptr, (const xmlChar *) finger, nullptr);
  463. if (!node)
  464. {
  465. if (required)
  466. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureLocation: error creating node '%s' in xpath '%s'", finger, xpath);
  467. return false;
  468. }
  469. }
  470. else
  471. {
  472. xmlNodePtr *nodes = obj->nodesetval->nodeTab;
  473. if (obj->nodesetval->nodeNr>1)
  474. {
  475. if (required)
  476. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureLocation: ambiguous xpath '%s' at node '%s'", xpath, finger);
  477. return false;
  478. }
  479. node = *nodes;
  480. }
  481. xmlXPathFreeObject(obj);
  482. }
  483. setLocation(node);
  484. return true;
  485. }
  486. virtual void setLocationNamespace(const char *prefix, const char * uri, bool current) override
  487. {
  488. xmlNodePtr node = m_xpathContext->node;
  489. if (!node)
  490. return;
  491. xmlNsPtr ns = nullptr;
  492. if (isEmptyString(uri))
  493. ns = xmlSearchNs(node->doc, node, (const xmlChar *) prefix);
  494. else
  495. ns = xmlNewNs(node, (const xmlChar *) uri, (const xmlChar *) prefix);
  496. if (current && ns)
  497. xmlSetNs(node, ns);
  498. }
  499. enum class ensureValueMode { set, add, appendto };
  500. virtual void ensureValue(const char *xpath, const char *value, ensureValueMode action, bool required)
  501. {
  502. if (isEmptyString(xpath))
  503. return;
  504. if (strstr(xpath, "//")) //maybe in the future, scripting error rather than not found, required==false has no effect
  505. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureValue: '//' not currently allowed for set operations '%s'", xpath);
  506. xmlNodePtr current = m_xpathContext->node;
  507. if (!current || *xpath=='/')
  508. {
  509. current = xmlDocGetRootElement(m_xmlDoc); //valuable, but very dangerous
  510. if (*xpath=='/')
  511. xpath++;
  512. }
  513. xmlNodePtr node = current;
  514. StringArray xpathnodes;
  515. xpathnodes.appendList(xpath, "/");
  516. ForEachItemIn(i, xpathnodes)
  517. {
  518. const char *finger = xpathnodes.item(i);
  519. if (isEmptyString(finger)) //shouldn't happen, scripting error rather than not found, required==false has no effect
  520. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureValue: empty xpath node unexpected '%s'", xpath);
  521. bool last = (i == (xpathnodes.ordinality()-1));
  522. if (*finger=='@')
  523. {
  524. if (!last) //scripting error
  525. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureValue: invalid xpath attribute node '%s' in xpath '%s'", finger, xpath);
  526. if (action == ensureValueMode::appendto)
  527. {
  528. const char *existing = (const char *) xmlGetProp(node, (const xmlChar *) finger+1);
  529. if (!isEmptyString(existing))
  530. {
  531. StringBuffer appendValue(existing);
  532. appendValue.append(value);
  533. xmlSetProp(node, (const xmlChar *) finger+1, (const xmlChar *) appendValue.str());
  534. return;
  535. }
  536. }
  537. xmlSetProp(node, (const xmlChar *) finger+1, (const xmlChar *) value);
  538. return;
  539. }
  540. xmlXPathObjectPtr obj = xmlXPathNodeEval(node, (const xmlChar*) finger, m_xpathContext);
  541. xmlXPathSetContextNode(current, m_xpathContext);
  542. if (!obj)
  543. throw MakeStringException(XPATHERR_InvalidInput,"XpathContext:ensureValue: invalid xpath '%s' at node '%s'", xpath, finger);
  544. if (obj->type!=xmlXPathObjectType::XPATH_NODESET || !obj->nodesetval || obj->nodesetval->nodeNr==0)
  545. {
  546. if (*finger=='.' || strpbrk(finger, "[()]*") || strstr(finger, "::")) //complex nodes must already exist
  547. {
  548. if (required)
  549. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureValue: xpath node not found '%s' in xpath '%s'", finger, xpath);
  550. return;
  551. }
  552. const char *nsx = strchr(finger, ':');
  553. if (nsx)
  554. {
  555. StringAttr prefix(finger, nsx-finger);
  556. finger = nsx+1;
  557. //translate the xpath xmlns prefix into the one defined in the content xml (exception if not found)
  558. const char *uri = queryNamespace(prefix.str());
  559. if (isEmptyString(uri))
  560. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureValue: xmlns prefix '%s' not defined in script used in xpath '%s'", prefix.str(), xpath);
  561. xmlNsPtr ns = xmlSearchNsByHref(m_xmlDoc, node, (const xmlChar *) uri);
  562. if (!ns)
  563. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureValue: namespace uri '%s' (referenced as xmlns prefix '%s') not declared in xml ", uri, prefix.str());
  564. node = xmlNewChild(node, ns, (const xmlChar *) finger, last ? (const xmlChar *) value : nullptr);
  565. }
  566. else
  567. {
  568. node = xmlNewChild(node, nullptr, (const xmlChar *) finger, last ? (const xmlChar *) value : nullptr);
  569. //default libxml2 behavior here would be very confusing for scripts. After adding a tag with no prefix you would need a tag on the xpath to read it.
  570. //Instead when there is no prefix, create a node with no namespace. To match the parent default namespace, define an xpath prefix for it.
  571. //You can always add the namespace you want by declaring it both in the xml (<es:namespace>) and the script (xmlns:prefix on the root node of the script) and then using it on the xpath.
  572. xmlSetNs(node, nullptr);
  573. }
  574. if (!node)
  575. {
  576. if (required)
  577. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureValue: error creating node '%s' in xpath '%s'", finger, xpath);
  578. return;
  579. }
  580. }
  581. else
  582. {
  583. xmlNodePtr *nodes = obj->nodesetval->nodeTab;
  584. if (!last)
  585. {
  586. if (obj->nodesetval->nodeNr>1)
  587. {
  588. if (required)
  589. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureValue: ambiguous xpath '%s' at node '%s'", xpath, finger);
  590. return;
  591. }
  592. node = *nodes;
  593. }
  594. else
  595. {
  596. if (obj->nodesetval->nodeNr>1 && action != ensureValueMode::add)
  597. {
  598. if (required)
  599. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:ensureValue: ambiguous xpath '%s'", xpath);
  600. return;
  601. }
  602. xmlNodePtr parent = node;
  603. node = *nodes;
  604. switch (action)
  605. {
  606. case ensureValueMode::add:
  607. {
  608. StringBuffer name((const char *)node->name);
  609. xmlNewChild(parent, node->ns, (const xmlChar *) name.str(), (const xmlChar *) value);
  610. break;
  611. }
  612. case ensureValueMode::appendto:
  613. {
  614. xmlChar *existing = xmlNodeGetContent(node);
  615. StringBuffer appendValue((const char *)existing);
  616. xmlNodeSetContent(node, (const xmlChar *) appendValue.append(value).str());
  617. xmlFree(existing);
  618. break;
  619. }
  620. case ensureValueMode::set:
  621. {
  622. xmlNodeSetContent(node, (const xmlChar *) value);
  623. break;
  624. }
  625. }
  626. }
  627. }
  628. xmlXPathFreeObject(obj);
  629. }
  630. }
  631. virtual void ensureSetValue(const char *xpath, const char *value, bool required) override
  632. {
  633. ensureValue(xpath, value, ensureValueMode::set, required);
  634. }
  635. virtual void ensureAddValue(const char *xpath, const char *value, bool reqired) override
  636. {
  637. ensureValue(xpath, value, ensureValueMode::add, reqired);
  638. }
  639. virtual void ensureAppendToValue(const char *xpath, const char *value, bool required) override
  640. {
  641. ensureValue(xpath, value, ensureValueMode::appendto, required);
  642. }
  643. virtual void rename(const char *xpath, const char *name, bool all) override
  644. {
  645. if (isEmptyString(xpath) || isEmptyString(name))
  646. return;
  647. xmlXPathObjectPtr obj = xmlXPathEval((const xmlChar*) xpath, m_xpathContext);
  648. if (!obj || obj->type!=xmlXPathObjectType::XPATH_NODESET || obj->nodesetval->nodeNr==0)
  649. return;
  650. if (obj->nodesetval->nodeNr>1 && !all)
  651. {
  652. xmlXPathFreeObject(obj);
  653. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:rename: ambiguous xpath '%s' to rename all set 'all'", xpath);
  654. }
  655. xmlNodePtr *nodes = obj->nodesetval->nodeTab;
  656. for (int i = 0; i < obj->nodesetval->nodeNr; i++)
  657. xmlNodeSetName(nodes[i], (const xmlChar *)name);
  658. xmlXPathFreeObject(obj);
  659. }
  660. virtual void remove(const char *xpath, bool all) override
  661. {
  662. if (isEmptyString(xpath))
  663. return;
  664. xmlXPathObjectPtr obj = xmlXPathEval((const xmlChar*) xpath, m_xpathContext);
  665. if (!obj || obj->type!=xmlXPathObjectType::XPATH_NODESET || !obj->nodesetval || obj->nodesetval->nodeNr==0)
  666. return;
  667. if (obj->nodesetval->nodeNr>1 && !all)
  668. {
  669. xmlXPathFreeObject(obj);
  670. throw MakeStringException(XPATHERR_InvalidState,"XpathContext:remove: ambiguous xpath '%s' to remove all set 'all'", xpath);
  671. }
  672. xmlNodePtr *nodes = obj->nodesetval->nodeTab;
  673. for (int i = obj->nodesetval->nodeNr - 1; i >= 0; i--)
  674. {
  675. xmlUnlinkNode(nodes[i]);
  676. xmlFreeNode(nodes[i]);
  677. nodes[i]=nullptr;
  678. }
  679. xmlXPathFreeObject(obj);
  680. }
  681. virtual void copyFromPrimaryContext(ICompiledXpath *select, const char *newname) override
  682. {
  683. if (!primaryContext)
  684. return;
  685. xmlNodePtr current = m_xpathContext->node;
  686. if (!current || !select)
  687. return;
  688. CLibCompiledXpath * ccXpath = static_cast<CLibCompiledXpath *>(select);
  689. xmlXPathObjectPtr obj = primaryContext->evaluate(ccXpath->getCompiledXPathExpression(), ccXpath->getXpath());
  690. if (!obj)
  691. throw MakeStringException(XPATHERR_InvalidState, "XpathContext:copyof xpath syntax error '%s'", ccXpath->getXpath());
  692. if (obj->type!=xmlXPathObjectType::XPATH_NODESET || !obj->nodesetval || obj->nodesetval->nodeNr==0)
  693. {
  694. xmlXPathFreeObject(obj);
  695. return;
  696. }
  697. xmlNodePtr *nodes = obj->nodesetval->nodeTab;
  698. for (int i = 0; i < obj->nodesetval->nodeNr; i++)
  699. {
  700. xmlNodePtr copy = xmlCopyNode(nodes[i], 1);
  701. if (!copy)
  702. continue;
  703. if (!isEmptyString(newname))
  704. xmlNodeSetName(copy, (const xmlChar *)newname);
  705. xmlAddChild(current, copy);
  706. }
  707. xmlXPathFreeObject(obj);
  708. }
  709. virtual bool addObjectVariable(const char * name, xmlXPathObjectPtr obj, CLibXpathScope *scope)
  710. {
  711. if (isEmptyString(name))
  712. return false;
  713. if (m_xpathContext)
  714. {
  715. if (!obj)
  716. throw MakeStringException(-1, "addObjectVariable %s error", name);
  717. WriteLockBlock wblock(m_rwlock);
  718. if (!scope && !scopes.empty())
  719. scope = scopes.back().get();
  720. if (scope)
  721. return scope->setObject(name, obj);
  722. return xmlXPathRegisterVariable(m_xpathContext, (xmlChar *)name, obj) == 0;
  723. }
  724. return false;
  725. }
  726. bool addStringVariable(const char * name, const char * val, CLibXpathScope *scope)
  727. {
  728. if (!val)
  729. return false;
  730. return addObjectVariable(name, xmlXPathNewCString(val), scope);
  731. }
  732. virtual bool addXpathVariable(const char * name, const char * xpath, CLibXpathScope *scope)
  733. {
  734. if (isEmptyString(xpath))
  735. addVariable(name, "");
  736. if (m_xpathContext)
  737. {
  738. xmlXPathObjectPtr obj = evaluate(xpath);
  739. if (!obj)
  740. throw MakeStringException(-1, "addXpathVariable xpath error %s", xpath);
  741. return addObjectVariable(name, obj, scope);
  742. }
  743. return false;
  744. }
  745. bool addCompiledVariable(const char * name, ICompiledXpath * compiled, CLibXpathScope *scope)
  746. {
  747. if (!compiled)
  748. addVariable(name, "");
  749. if (m_xpathContext)
  750. {
  751. CLibCompiledXpath * ccXpath = static_cast<CLibCompiledXpath *>(compiled);
  752. xmlXPathObjectPtr obj = evaluate(ccXpath->getCompiledXPathExpression(), ccXpath->getXpath());
  753. if (!obj)
  754. throw MakeStringException(-1, "addEvaluateVariable xpath error %s", ccXpath->getXpath());
  755. return addObjectVariable(name, obj, scope);
  756. }
  757. return false;
  758. }
  759. virtual bool addInputValue(const char * name, const char * value) override
  760. {
  761. if (isEmptyString(name)||isEmptyString(value))
  762. return false;
  763. VStringBuffer xpath("'%s'", value);
  764. return addInputXpath(name, xpath);
  765. }
  766. virtual bool addInputXpath(const char * name, const char * xpath) override
  767. {
  768. if (isEmptyString(name)||isEmptyString(xpath))
  769. return false;
  770. Owned<ICompiledXpath> compiled = compileXpath(xpath);
  771. if (compiled)
  772. {
  773. WriteLockBlock wblock(m_rwlock);
  774. provided.emplace(name, compiled.getClear());
  775. return true;
  776. }
  777. return false;
  778. }
  779. inline ICompiledXpath *findInput(const char *name)
  780. {
  781. ReadLockBlock rblock(m_rwlock);
  782. XPathInputMap::iterator it = provided.find(name);
  783. if (it == provided.end())
  784. return nullptr;
  785. return it->second;
  786. }
  787. virtual bool declareCompiledParameter(const char * name, ICompiledXpath * compiled) override
  788. {
  789. if (hasVariable(name, nullptr, getCurrentScope()))
  790. return false;
  791. //use input value
  792. ICompiledXpath *inputxp = findInput(name);
  793. if (inputxp)
  794. return addCompiledVariable(name, inputxp, getCurrentScope());
  795. //use default provided
  796. return addCompiledVariable(name, compiled, getCurrentScope());
  797. }
  798. virtual void declareRemainingInputs() override
  799. {
  800. for (XPathInputMap::iterator it=provided.begin(); it!=provided.end(); ++it)
  801. declareCompiledParameter(it->first.c_str(), it->second);
  802. }
  803. virtual bool declareParameter(const char * name, const char *value) override
  804. {
  805. if (hasVariable(name, nullptr, getCurrentScope()))
  806. return false;
  807. //use input value
  808. ICompiledXpath *input = findInput(name);
  809. if (input)
  810. return addCompiledVariable(name, input, getCurrentScope());
  811. //use default provided
  812. return addStringVariable(name, value, getCurrentScope());
  813. }
  814. virtual bool addXpathVariable(const char * name, const char * xpath) override
  815. {
  816. return addXpathVariable(name, xpath, nullptr);
  817. }
  818. virtual bool addVariable(const char * name, const char * val) override
  819. {
  820. return addStringVariable(name, val, nullptr);
  821. }
  822. virtual bool addCompiledVariable(const char * name, ICompiledXpath * compiled) override
  823. {
  824. return addCompiledVariable(name, compiled, nullptr);
  825. }
  826. virtual const char * getVariable(const char * name, StringBuffer & variable) override
  827. {
  828. if (m_xpathContext)
  829. {
  830. ReadLockBlock rblock(m_rwlock);
  831. xmlXPathObjectPtr ptr = xmlXPathVariableLookupNS(m_xpathContext, (const xmlChar *)name, nullptr);
  832. if (!ptr)
  833. return nullptr;
  834. variable.append((const char *) ptr->stringval);
  835. xmlXPathFreeObject(ptr);
  836. return variable;
  837. }
  838. return nullptr;
  839. }
  840. virtual IXpathContextIterator *evaluateAsNodeSet(ICompiledXpath * compiled) override
  841. {
  842. CLibCompiledXpath * clCompiled = static_cast<CLibCompiledXpath *>(compiled);
  843. if (!clCompiled)
  844. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsNodeSet: Error: Could not evaluate XPATH");
  845. return evaluateAsNodeSet(evaluate(clCompiled->getCompiledXPathExpression(), compiled->getXpath()), compiled->getXpath());
  846. }
  847. IXpathContextIterator *evaluateAsNodeSet(xmlXPathObjectPtr evaluated, const char* xpath);
  848. virtual bool evaluateAsBoolean(const char * xpath) override
  849. {
  850. if (!xpath || !*xpath)
  851. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsBoolean: Error: Could not evaluate empty XPATH");
  852. return evaluateAsBoolean(evaluate(xpath), xpath);
  853. }
  854. virtual StringBuffer &toXml(const char *xpath, StringBuffer & xml) override
  855. {
  856. xmlXPathObjectPtr obj = evaluate(xpath);
  857. if (!obj || !obj->type==XPATH_NODESET || !obj->nodesetval || !obj->nodesetval->nodeTab || obj->nodesetval->nodeNr!=1)
  858. return xml;
  859. xmlNodePtr node = obj->nodesetval->nodeTab[0];
  860. if (!node)
  861. return xml;
  862. xmlOutputBufferPtr xmlOut = xmlAllocOutputBuffer(nullptr);
  863. xmlNodeDumpOutput(xmlOut, node->doc, node, 0, 1, nullptr);
  864. xmlOutputBufferFlush(xmlOut);
  865. xmlBufPtr buf = (xmlOut->conv != nullptr) ? xmlOut->conv : xmlOut->buffer;
  866. if (xmlBufUse(buf))
  867. xml.append(xmlBufUse(buf), (const char *)xmlBufContent(buf));
  868. xmlOutputBufferClose(xmlOut);
  869. xmlXPathFreeObject(obj);
  870. return xml;
  871. }
  872. virtual bool evaluateAsString(const char * xpath, StringBuffer & evaluated) override
  873. {
  874. if (!xpath || !*xpath)
  875. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate empty XPATH");
  876. return evaluateAsString(evaluate(xpath), evaluated, xpath);
  877. }
  878. virtual bool evaluateAsBoolean(ICompiledXpath * compiledXpath) override
  879. {
  880. CLibCompiledXpath * ccXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  881. if (!ccXpath)
  882. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsBoolean: Error: Missing compiled XPATH");
  883. return evaluateAsBoolean(evaluate(ccXpath->getCompiledXPathExpression(), compiledXpath->getXpath()), compiledXpath->getXpath());
  884. }
  885. virtual const char * evaluateAsString(ICompiledXpath * compiledXpath, StringBuffer & evaluated) override
  886. {
  887. CLibCompiledXpath * ccXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  888. if (!ccXpath)
  889. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsString: Error: Missing compiled XPATH");
  890. return evaluateAsString(evaluate(ccXpath->getCompiledXPathExpression(), compiledXpath->getXpath()), evaluated, compiledXpath->getXpath());
  891. }
  892. virtual double evaluateAsNumber(ICompiledXpath * compiledXpath) override
  893. {
  894. CLibCompiledXpath * ccXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  895. if (!ccXpath)
  896. throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsNumber: Error: Missing compiled XPATH");
  897. return evaluateAsNumber(evaluate(ccXpath->getCompiledXPathExpression(), compiledXpath->getXpath()), compiledXpath->getXpath());
  898. }
  899. virtual bool setXmlDoc(const char * xmldoc) override
  900. {
  901. if (isEmptyString(xmldoc))
  902. return false;
  903. xmlDocPtr doc = xmlParseDoc((const unsigned char *)xmldoc);
  904. if (doc == nullptr)
  905. {
  906. ERRLOG("XpathProcessor:setXmlDoc Error: Unable to parse XMLLib document");
  907. return false;
  908. }
  909. ownedDoc = true;
  910. return setContextDocument(doc, xmlDocGetRootElement(doc));
  911. }
  912. private:
  913. xmlNodePtr checkGetSoapNamespace(xmlNodePtr cur, const char *expected, const xmlChar *&soap)
  914. {
  915. if (!cur)
  916. return nullptr;
  917. if (!streq((const char *)cur->name, expected))
  918. return cur;
  919. if (!soap && cur->ns && cur->ns->href)
  920. soap = cur->ns->href;
  921. return xmlFirstElementChild(cur);
  922. }
  923. void removeNamespace(xmlNodePtr cur, const xmlChar *remove)
  924. {
  925. //we need to recognize different namespace behavior at each entry point
  926. //
  927. //for backward compatibility we need to be flexible with what we receive, from the user
  928. //but other entry points are dealing with standardized content
  929. //hopefully even calls out to a given 3rd party services will be self consistent
  930. //
  931. if (!remove)
  932. cur->ns=nullptr; //just a "reference", no free
  933. else if (cur->ns && cur->ns->href && streq((const char *)cur->ns->href, (const char *)remove))
  934. cur->ns=nullptr;
  935. for (xmlNodePtr child = xmlFirstElementChild(cur); child!=nullptr; child=xmlNextElementSibling(child))
  936. removeNamespace(child, remove);
  937. for (xmlAttrPtr att = cur->properties; att!=nullptr; att=att->next)
  938. {
  939. if (!remove)
  940. att->ns=nullptr;
  941. else if (att->ns && att->ns->href && streq((const char *)att->ns->href, (const char *)remove))
  942. att->ns=nullptr;
  943. }
  944. }
  945. void processNamespaces()
  946. {
  947. const xmlChar *soap = nullptr;
  948. const xmlChar *n = nullptr;
  949. xmlNodePtr start = (m_xpathContext && m_xpathContext->node) ? m_xpathContext->node : xmlDocGetRootElement(m_xmlDoc);
  950. xmlNodePtr cur = checkGetSoapNamespace(start, "Envelope", soap);
  951. cur = checkGetSoapNamespace(cur, "Body", soap);
  952. if (soap)
  953. xmlXPathRegisterNs(m_xpathContext, (const xmlChar *)"soap", soap);
  954. if (cur->ns && cur->ns->href)
  955. n = cur->ns->href;
  956. if (n)
  957. xmlXPathRegisterNs(m_xpathContext, (const xmlChar *)"n", n);
  958. if (removeDocNamespaces)
  959. removeNamespace(xmlDocGetRootElement(m_xmlDoc), nullptr);
  960. }
  961. bool setContextDocument(xmlDocPtr doc, xmlNodePtr node)
  962. {
  963. WriteLockBlock rblock(m_rwlock);
  964. m_xmlDoc = doc;
  965. m_xpathContext = xmlXPathNewContext(m_xmlDoc);
  966. if(m_xpathContext == nullptr)
  967. {
  968. ERRLOG("XpathProcessor:setContextDocument: Error: Unable to create new XMLLib XPath context");
  969. return false;
  970. }
  971. //relative paths need something to be relative to
  972. if (node)
  973. m_xpathContext->node = node;
  974. xmlXPathRegisterVariableLookup(m_xpathContext, variableLookupFunc, this);
  975. processNamespaces();
  976. return true;
  977. }
  978. bool evaluateAsBoolean(xmlXPathObjectPtr evaluatedXpathObj, const char* xpath)
  979. {
  980. if (!evaluatedXpathObj)
  981. {
  982. throw MakeStringException(XPATHERR_InvalidInput, "XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s'", xpath);
  983. }
  984. if (XPATH_BOOLEAN != evaluatedXpathObj->type)
  985. {
  986. xmlXPathFreeObject(evaluatedXpathObj);
  987. throw MakeStringException(XPATHERR_UnexpectedInput, "XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s' as Boolean", xpath);
  988. }
  989. bool bresult = evaluatedXpathObj->boolval;
  990. xmlXPathFreeObject(evaluatedXpathObj);
  991. return bresult;
  992. }
  993. const char* evaluateAsString(xmlXPathObjectPtr evaluatedXpathObj, StringBuffer& evaluated, const char* xpath)
  994. {
  995. if (!evaluatedXpathObj)
  996. throw MakeStringException(XPATHERR_InvalidInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s'", xpath);
  997. evaluated.clear();
  998. switch (evaluatedXpathObj->type)
  999. {
  1000. case XPATH_NODESET:
  1001. {
  1002. xmlNodeSetPtr nodes = evaluatedXpathObj->nodesetval;
  1003. for (int i = 0; nodes!=nullptr && i < nodes->nodeNr; i++)
  1004. {
  1005. xmlNodePtr nodeTab = nodes->nodeTab[i];
  1006. auto nodeContent = xmlNodeGetContent(nodeTab);
  1007. evaluated.append((const char *)nodeContent);
  1008. xmlFree(nodeContent);
  1009. }
  1010. break;
  1011. }
  1012. case XPATH_BOOLEAN:
  1013. case XPATH_NUMBER:
  1014. case XPATH_STRING:
  1015. case XPATH_POINT:
  1016. case XPATH_RANGE:
  1017. case XPATH_LOCATIONSET:
  1018. case XPATH_USERS:
  1019. case XPATH_XSLT_TREE:
  1020. {
  1021. evaluatedXpathObj = xmlXPathConvertString (evaluatedXpathObj); //existing object is freed
  1022. if (!evaluatedXpathObj)
  1023. throw MakeStringException(XPATHERR_UnexpectedInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s'; could not convert result to string", xpath);
  1024. evaluated.append(evaluatedXpathObj->stringval);
  1025. break;
  1026. }
  1027. default:
  1028. {
  1029. xmlXPathFreeObject(evaluatedXpathObj);
  1030. throw MakeStringException(XPATHERR_UnexpectedInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s' as string; unexpected type %d", xpath, evaluatedXpathObj->type);
  1031. break;
  1032. }
  1033. }
  1034. xmlXPathFreeObject(evaluatedXpathObj);
  1035. return evaluated.str();
  1036. }
  1037. bool append(StringBuffer &s, xmlXPathObjectPtr obj)
  1038. {
  1039. if (!obj)
  1040. return false;
  1041. xmlChar *value = xmlXPathCastToString(obj);
  1042. if (!value || !*value)
  1043. return false;
  1044. s.append((const char *) value);
  1045. xmlFree(value);
  1046. return true;
  1047. }
  1048. double evaluateAsNumber(xmlXPathObjectPtr evaluatedXpathObj, const char* xpath)
  1049. {
  1050. if (!evaluatedXpathObj)
  1051. throw MakeStringException(XPATHERR_InvalidInput,"XpathProcessor:evaluateAsNumber: Error: Could not evaluate XPATH '%s'", xpath);
  1052. double ret = xmlXPathCastToNumber(evaluatedXpathObj);
  1053. xmlXPathFreeObject(evaluatedXpathObj);
  1054. return ret;
  1055. }
  1056. virtual xmlXPathObjectPtr evaluate(xmlXPathCompExprPtr compiled, const char *xpath)
  1057. {
  1058. xmlXPathObjectPtr evaluatedXpathObj = nullptr;
  1059. if (compiled)
  1060. {
  1061. ReadLockBlock rlock(m_rwlock);
  1062. if ( m_xpathContext)
  1063. {
  1064. evaluatedXpathObj = xmlXPathCompiledEval(compiled, m_xpathContext);
  1065. }
  1066. else
  1067. {
  1068. throw MakeStringException(XPATHERR_InvalidState,"XpathProcessor:evaluate: Error: Could not evaluate XPATH '%s'", xpath);
  1069. }
  1070. }
  1071. return evaluatedXpathObj;
  1072. }
  1073. virtual xmlXPathObjectPtr evaluate(const char * xpath)
  1074. {
  1075. if (isEmptyString(xpath))
  1076. return nullptr;
  1077. if (!m_xpathContext)
  1078. throw MakeStringException(XPATHERR_InvalidState,"XpathProcessor:evaluate: Error: Could not evaluate XPATH '%s'; ensure xmldoc has been set", xpath);
  1079. ReadLockBlock rlock(m_rwlock);
  1080. return xmlXPathEval((const xmlChar *)xpath, m_xpathContext);
  1081. }
  1082. };
  1083. class XPathNodeSetIterator : public CInterfaceOf<IXpathContextIterator>
  1084. {
  1085. public:
  1086. CLibXpathContext *context;
  1087. xmlNodeSetPtr list;
  1088. unsigned pos = 0;
  1089. public:
  1090. XPathNodeSetIterator(CLibXpathContext *xpctx, xmlNodeSetPtr nodeset) : context(xpctx), list(nodeset)
  1091. {
  1092. context->pushLocation();
  1093. context->m_xpathContext->contextSize = xmlXPathNodeSetGetLength(list);
  1094. }
  1095. virtual ~XPathNodeSetIterator()
  1096. {
  1097. context->popLocation();
  1098. if (list)
  1099. xmlXPathFreeNodeSet(list);
  1100. }
  1101. bool update(int newpos)
  1102. {
  1103. pos = newpos;
  1104. if (!isValid())
  1105. return false;
  1106. xmlNodePtr node = xmlXPathNodeSetItem(list, pos);
  1107. context->m_xpathContext->node = node;
  1108. if ((node->type != XML_NAMESPACE_DECL && node->doc != nullptr))
  1109. context->m_xpathContext->doc = node->doc;
  1110. context->m_xpathContext->proximityPosition = pos + 1;
  1111. return true;
  1112. }
  1113. virtual bool first()
  1114. {
  1115. if (xmlXPathNodeSetGetLength(list)==0)
  1116. return false;
  1117. return update(0);
  1118. }
  1119. virtual bool next()
  1120. {
  1121. return update(pos+1);
  1122. }
  1123. virtual bool isValid()
  1124. {
  1125. return (pos < xmlXPathNodeSetGetLength(list));
  1126. }
  1127. virtual IXpathContext & query()
  1128. {
  1129. return *context;
  1130. }
  1131. };
  1132. IXpathContextIterator *CLibXpathContext::evaluateAsNodeSet(xmlXPathObjectPtr evaluated, const char* xpath)
  1133. {
  1134. if (!evaluated)
  1135. {
  1136. throw MakeStringException(XPATHERR_InvalidInput, "XpathProcessor:evaluateAsNodeSet: Error: Could not evaluate XPATH '%s'", xpath);
  1137. }
  1138. if (XPATH_NODESET != evaluated->type)
  1139. {
  1140. xmlXPathFreeObject(evaluated);
  1141. throw MakeStringException(XPATHERR_UnexpectedInput, "XpathProcessor:evaluateAsNodeSet: Error: Could not evaluate XPATH '%s' as NodeSet", xpath);
  1142. }
  1143. xmlNodeSetPtr ns = evaluated->nodesetval;
  1144. evaluated->nodesetval = nullptr;
  1145. xmlXPathFreeObject(evaluated);
  1146. return new XPathNodeSetIterator(this, ns);
  1147. }
  1148. static xmlXPathObjectPtr variableLookupFunc(void *data, const xmlChar *name, const xmlChar *ns_uri)
  1149. {
  1150. CLibXpathContext *ctxt = (CLibXpathContext *) data;
  1151. if (!ctxt)
  1152. return nullptr;
  1153. return ctxt->getVariableObject((const char *)name, (const char *)ns_uri, nullptr);
  1154. }
  1155. extern ICompiledXpath* compileXpath(const char * xpath)
  1156. {
  1157. return new CLibCompiledXpath(xpath);
  1158. }
  1159. void addChildFromPtree(xmlNodePtr parent, IPropertyTree &tree, const char *name)
  1160. {
  1161. if (isEmptyString(name))
  1162. name = tree.queryName();
  1163. if (*name==':') //invalid xml but maybe valid ptree?
  1164. name++;
  1165. StringAttr prefix;
  1166. const char *colon = strchr(name, ':');
  1167. if (colon)
  1168. {
  1169. prefix.set(name, colon-name);
  1170. name = colon+1;
  1171. }
  1172. xmlNodePtr node = xmlNewChild(parent, nullptr, (const xmlChar *)name, (const xmlChar *)tree.queryProp(nullptr));
  1173. if (!node)
  1174. return;
  1175. xmlNsPtr ns = nullptr;
  1176. if (tree.getAttributeCount())
  1177. {
  1178. Owned<IAttributeIterator> atts = tree.getAttributes();
  1179. ForEach(*atts)
  1180. {
  1181. const char *attname = atts->queryName()+1;
  1182. if (strncmp(attname, "xmlns", 5)!=0)
  1183. xmlNewProp(node, (const xmlChar *)attname, (const xmlChar *)atts->queryValue());
  1184. else
  1185. {
  1186. attname+=5;
  1187. if (*attname==':')
  1188. attname++;
  1189. if (*attname==0)
  1190. attname=nullptr; //default namespace needs null prefix
  1191. xmlNewNs(node, (const xmlChar *)atts->queryValue(), (const xmlChar *)attname);
  1192. }
  1193. }
  1194. }
  1195. if (prefix.isEmpty())
  1196. node->ns = nullptr;
  1197. else
  1198. {
  1199. xmlNsPtr ns = xmlSearchNs(node->doc, node, (const xmlChar *)prefix.str());
  1200. if (!ns) //invalid but let's repair it
  1201. ns = xmlNewNs(node, (const xmlChar *)"urn:hpcc:unknown", (const xmlChar *)prefix.str());
  1202. node->ns = ns;
  1203. }
  1204. Owned<IPropertyTreeIterator> children = tree.getElements("*");
  1205. ForEach(*children)
  1206. addChildFromPtree(node, children->query(), nullptr);
  1207. }
  1208. IPropertyTree *createPtreeFromXmlNode(xmlNodePtr node);
  1209. void copyXmlNode(IPropertyTree *tree, xmlNodePtr node)
  1210. {
  1211. for(xmlAttrPtr att=node->properties;att!=nullptr; att=att->next)
  1212. {
  1213. VStringBuffer name("@%s", (const char *)att->name);
  1214. xmlChar *value = xmlGetProp(node, att->name);
  1215. tree->setProp(name, (const char *)value);
  1216. xmlFree(value);
  1217. }
  1218. for (xmlNodePtr child=xmlFirstElementChild(node); child!=nullptr; child=xmlNextElementSibling(child))
  1219. tree->addPropTree((const char *)child->name, createPtreeFromXmlNode(child));
  1220. }
  1221. IPropertyTree *createPtreeFromXmlNode(xmlNodePtr node)
  1222. {
  1223. if (!node)
  1224. return nullptr;
  1225. Owned<IPropertyTree> tree = createPTree((const char *)node->name);
  1226. copyXmlNode(tree, node);
  1227. return tree.getClear();
  1228. }
  1229. static const char *queryAttrValue(xmlNodePtr node, const char *name)
  1230. {
  1231. if (!node)
  1232. return nullptr;
  1233. xmlAttrPtr att = xmlHasProp(node, (const xmlChar *)name);
  1234. if (!att)
  1235. return nullptr;
  1236. if (att->type != XML_ATTRIBUTE_NODE || att->children==nullptr || (att->children->type != XML_TEXT_NODE && att->children->type != XML_CDATA_SECTION_NODE))
  1237. return nullptr;
  1238. return (const char *) att->children->content;
  1239. }
  1240. class CEsdlScriptContext : public CInterfaceOf<IEsdlScriptContext>
  1241. {
  1242. private:
  1243. void *espCtx = nullptr;
  1244. xmlDocPtr doc = nullptr;
  1245. xmlNodePtr root = nullptr;
  1246. xmlXPathContextPtr xpathCtx = nullptr;
  1247. public:
  1248. CEsdlScriptContext(void *ctx) : espCtx(ctx)
  1249. {
  1250. doc = xmlParseDoc((const xmlChar *) "<esdl_script_context/>");
  1251. xpathCtx = xmlXPathNewContext(doc);
  1252. if(xpathCtx == nullptr)
  1253. throw MakeStringException(-1, "CEsdlScriptContext: Unable to create new xPath context");
  1254. root = xmlDocGetRootElement(doc);
  1255. xpathCtx->node = root;
  1256. }
  1257. private:
  1258. ~CEsdlScriptContext()
  1259. {
  1260. xmlXPathFreeContext(xpathCtx);
  1261. xmlFreeDoc(doc);
  1262. }
  1263. virtual void *queryEspContext() override
  1264. {
  1265. return espCtx;
  1266. }
  1267. inline void sanityCheckSectionName(const char *name)
  1268. {
  1269. //sanity check the name, not a full validation
  1270. if (strpbrk(name, "/[]()*?"))
  1271. throw MakeStringException(-1, "CEsdlScriptContext:removeSection invalid section name %s", name);
  1272. }
  1273. xmlNodePtr getSectionNode(const char *name, const char *xpath="*[1]")
  1274. {
  1275. sanityCheckSectionName(name);
  1276. StringBuffer fullXpath(name);
  1277. if (!isEmptyString(xpath))
  1278. fullXpath.append('/').append(xpath);
  1279. xmlNodePtr sect = nullptr;
  1280. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) fullXpath.str(), xpathCtx);
  1281. if (eval && XPATH_NODESET == eval->type && eval->nodesetval && eval->nodesetval->nodeNr && eval->nodesetval->nodeTab!=nullptr)
  1282. sect = eval->nodesetval->nodeTab[0];
  1283. xmlXPathFreeObject(eval);
  1284. return sect;
  1285. }
  1286. void addXpathCtxConfigInputs(IXpathContext *tgtXpathCtx)
  1287. {
  1288. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) "config/*/Transform/Param", xpathCtx);
  1289. if (!eval)
  1290. return;
  1291. if (XPATH_NODESET==eval->type && eval->nodesetval!=nullptr && eval->nodesetval->nodeNr!=0 && eval->nodesetval->nodeTab!=nullptr)
  1292. {
  1293. for (int i=0; i<eval->nodesetval->nodeNr; i++)
  1294. {
  1295. xmlNodePtr node = eval->nodesetval->nodeTab[i];
  1296. if (node==nullptr)
  1297. continue;
  1298. const char *name = queryAttrValue(node, "name");
  1299. if (xmlHasProp(node,(const xmlChar *) "select"))
  1300. tgtXpathCtx->addInputXpath(name, queryAttrValue(node,"select"));
  1301. else
  1302. tgtXpathCtx->addInputValue(name, queryAttrValue(node,"value"));
  1303. }
  1304. }
  1305. xmlXPathFreeObject(eval);
  1306. }
  1307. const char *getXPathString(const char *xpath, StringBuffer &s) const override
  1308. {
  1309. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) xpath, xpathCtx);
  1310. if (eval)
  1311. {
  1312. xmlChar *v = xmlXPathCastToString(eval);
  1313. xmlXPathFreeObject(eval);
  1314. if (v)
  1315. {
  1316. if (*v)
  1317. s.append((const char *)v);
  1318. xmlFree(v);
  1319. return s;
  1320. }
  1321. }
  1322. return nullptr;
  1323. }
  1324. __int64 getXPathInt64(const char *xpath, __int64 dft=0) const override
  1325. {
  1326. __int64 ret = dft;
  1327. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) xpath, xpathCtx);
  1328. if (eval)
  1329. {
  1330. xmlChar *val = xmlXPathCastToString(eval);
  1331. xmlXPathFreeObject(eval);
  1332. if (val)
  1333. {
  1334. if (*val)
  1335. ret = _atoi64((const char *)val);
  1336. xmlFree(val);
  1337. }
  1338. }
  1339. return ret;
  1340. }
  1341. bool getXPathBool(const char *xpath, bool dft=false) const override
  1342. {
  1343. bool ret = dft;
  1344. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) xpath, xpathCtx);
  1345. if (eval)
  1346. {
  1347. xmlChar *val = xmlXPathCastToString(eval);
  1348. xmlXPathFreeObject(eval);
  1349. if (val)
  1350. {
  1351. if (*val)
  1352. ret = strToBool((const char *)val);
  1353. xmlFree(val);
  1354. }
  1355. }
  1356. return ret;
  1357. }
  1358. xmlNodePtr getSection(const char *name)
  1359. {
  1360. return getSectionNode(name, nullptr);
  1361. }
  1362. xmlNodePtr ensureCopiedSection(const char *target, const char *source, bool replace)
  1363. {
  1364. if (isEmptyString(source)||isEmptyString(target))
  1365. return nullptr;
  1366. sanityCheckSectionName(target);
  1367. sanityCheckSectionName(source);
  1368. xmlNodePtr targetNode = getSectionNode(target, nullptr);
  1369. if (targetNode && !replace)
  1370. return targetNode;
  1371. xmlNodePtr sourceNode = getSectionNode(source, nullptr);
  1372. if (!sourceNode)
  1373. return nullptr;
  1374. xmlNodePtr copyNode = xmlCopyNode(sourceNode, 1);
  1375. xmlNodeSetName(copyNode, (const xmlChar *) target);
  1376. if (targetNode)
  1377. {
  1378. xmlNodePtr oldNode = xmlReplaceNode(targetNode, copyNode);
  1379. xmlFreeNode(oldNode);
  1380. return copyNode;
  1381. }
  1382. xmlAddChild(root, copyNode);
  1383. return copyNode;
  1384. }
  1385. xmlNodePtr ensureSection(const char *name)
  1386. {
  1387. sanityCheckSectionName(name);
  1388. xmlNodePtr sect = getSection(name);
  1389. if (sect)
  1390. return sect;
  1391. return xmlNewChild(root, nullptr, (const xmlChar *) name, nullptr);
  1392. }
  1393. void removeSection(const char *name)
  1394. {
  1395. sanityCheckSectionName(name);
  1396. xmlXPathObjectPtr eval = xmlXPathEval((const xmlChar *) name, xpathCtx);
  1397. if (!eval)
  1398. return;
  1399. //should be just one node, but if there are more, clean them up anyway
  1400. xmlNodeSetPtr ns = eval->nodesetval;
  1401. if (XPATH_NODESET==eval->type && !xmlXPathNodeSetIsEmpty(ns))
  1402. {
  1403. //shouldn't matter here, but reverse order ensures children are deleted before parents
  1404. for(int i=xmlXPathNodeSetGetLength(ns)-1; i>=0; i--)
  1405. {
  1406. xmlNodePtr node = xmlXPathNodeSetItem(ns, i);
  1407. if (!node)
  1408. continue;
  1409. xmlUnlinkNode(node);
  1410. xmlFreeNode(node);
  1411. ns->nodeTab[i]=nullptr;
  1412. }
  1413. }
  1414. xmlXPathFreeObject(eval);
  1415. }
  1416. xmlNodePtr replaceSection(const char *name)
  1417. {
  1418. removeSection(name);
  1419. return xmlNewChild(root, nullptr, (const xmlChar *) name, nullptr);
  1420. }
  1421. virtual void setContent(const char *section, const char *xml) override
  1422. {
  1423. xmlNodePtr sect = replaceSection(section);
  1424. if (xml==nullptr) //means delete content
  1425. return;
  1426. xmlParserCtxtPtr parserCtx = xmlCreateDocParserCtxt((const unsigned char *)xml);
  1427. if (!parserCtx)
  1428. throw MakeStringException(-1, "CEsdlScriptContext:setContent: Unable to init parse of %s XML content", section);
  1429. parserCtx->node = sect;
  1430. xmlParseDocument(parserCtx);
  1431. int wellFormed = parserCtx->wellFormed;
  1432. xmlFreeDoc(parserCtx->myDoc); //dummy document
  1433. xmlFreeParserCtxt(parserCtx);
  1434. if (!wellFormed)
  1435. throw MakeStringException(-1, "CEsdlScriptContext:setContent xml string: Unable to parse %s XML content", section);
  1436. }
  1437. virtual void appendContent(const char *section, const char *name, const char *xml) override
  1438. {
  1439. if (xml==nullptr)
  1440. return;
  1441. xmlNodePtr sect = ensureSection(section);
  1442. xmlNodePtr sectNode = getSectionNode(section, name);
  1443. xmlDocPtr doc = xmlParseDoc((const xmlChar *)xml);
  1444. if (!doc)
  1445. return;
  1446. xmlNodePtr content = xmlDocGetRootElement(doc);
  1447. if (!content)
  1448. {
  1449. xmlFreeDoc(doc);
  1450. return;
  1451. }
  1452. if (!sectNode)
  1453. {
  1454. xmlUnlinkNode(content);
  1455. if (!isEmptyString(name))
  1456. xmlNodeSetName(content, (const xmlChar *) name);
  1457. xmlAddChild(sect, content);
  1458. xmlFree(doc);
  1459. return;
  1460. }
  1461. xmlAttrPtr prop = content->properties;
  1462. while (prop != nullptr)
  1463. {
  1464. xmlChar *value = xmlGetProp(content, prop->name);
  1465. xmlSetProp(sectNode, prop->name, value);
  1466. xmlFree(value);
  1467. prop = prop->next;
  1468. }
  1469. for (xmlNodePtr node = xmlFirstElementChild(content); node != nullptr; node = xmlFirstElementChild(content))
  1470. {
  1471. xmlUnlinkNode(node);
  1472. xmlAddChild(sectNode, node);
  1473. }
  1474. xmlFreeDoc(doc);
  1475. }
  1476. virtual void setContent(const char *section, IPropertyTree *tree) override
  1477. {
  1478. xmlNodePtr sect = replaceSection(section);
  1479. if (tree==nullptr) //means delete content
  1480. return;
  1481. addChildFromPtree(sect, *tree, tree->queryName());
  1482. }
  1483. virtual void setAttribute(const char *section, const char *name, const char *value) override
  1484. {
  1485. xmlNodePtr sect = ensureSection(section);
  1486. if (value)
  1487. xmlSetProp(sect, (const xmlChar *)name, (const xmlChar *)value);
  1488. else
  1489. xmlUnsetProp(sect, (const xmlChar *)name);
  1490. }
  1491. virtual const char *queryAttribute(const char *section, const char *name) override
  1492. {
  1493. xmlNodePtr sect = getSection(section);
  1494. if (!sect)
  1495. return nullptr;
  1496. return queryAttrValue(sect, name);
  1497. }
  1498. virtual const char *getAttribute(const char *section, const char *name, StringBuffer &s) override
  1499. {
  1500. xmlNodePtr sect = getSection(section);
  1501. if (!sect)
  1502. return nullptr;
  1503. xmlChar *val = xmlGetProp(sect, (const xmlChar *)name);
  1504. if (!val)
  1505. return nullptr;
  1506. s.append((const char *)val);
  1507. xmlFree(val);
  1508. return s;
  1509. }
  1510. virtual bool tokenize(const char *str, const char *delimeters, StringBuffer &resultPath)
  1511. {
  1512. xmlNodePtr sect = ensureSection("temporaries");
  1513. if (!sect)
  1514. return false;
  1515. StringBuffer unique("T");
  1516. appendGloballyUniqueId(unique);
  1517. xmlNodePtr container = xmlNewChild(sect, nullptr, (const xmlChar *) unique.str(), nullptr);
  1518. if (!container)
  1519. return false;
  1520. resultPath.set("/esdl_script_context/temporaries/").append(unique.str()).append("/token");
  1521. StringArray tkns;
  1522. tkns.appendList(str, delimeters);
  1523. ForEachItemIn(i, tkns)
  1524. {
  1525. const char *tkn = tkns.item(i);
  1526. xmlNewChild(container, nullptr, (const xmlChar *) "token", (const xmlChar *) tkn);
  1527. }
  1528. return true;
  1529. }
  1530. virtual void toXML(StringBuffer &xml, const char *section, bool includeParentNode=false) override
  1531. {
  1532. xmlNodePtr sect = root;
  1533. if (!isEmptyString(section))
  1534. {
  1535. sect = getSectionNode(section, includeParentNode ? nullptr : "*[1]");
  1536. if (!sect)
  1537. return;
  1538. }
  1539. xmlOutputBufferPtr xmlOut = xmlAllocOutputBuffer(nullptr);
  1540. xmlNodeDumpOutput(xmlOut, sect->doc, sect, 0, 1, nullptr);
  1541. xmlOutputBufferFlush(xmlOut);
  1542. xmlBufPtr buf = (xmlOut->conv != nullptr) ? xmlOut->conv : xmlOut->buffer;
  1543. if (xmlBufUse(buf))
  1544. xml.append(xmlBufUse(buf), (const char *)xmlBufContent(buf));
  1545. xmlOutputBufferClose(xmlOut);
  1546. }
  1547. virtual IPropertyTree *createPTreeFromSection(const char *section) override
  1548. {
  1549. if (isEmptyString(section))
  1550. return nullptr;
  1551. xmlNodePtr sect = getSectionNode(section, nullptr);
  1552. if (!sect)
  1553. return nullptr;
  1554. return createPtreeFromXmlNode(sect);
  1555. }
  1556. virtual void toXML(StringBuffer &xml) override
  1557. {
  1558. toXML(xml, nullptr, true);
  1559. }
  1560. IXpathContext* createXpathContext(IXpathContext *primaryContext, const char *section, bool strictParameterDeclaration) override
  1561. {
  1562. xmlNodePtr sect = nullptr;
  1563. if (!isEmptyString(section))
  1564. {
  1565. sect = getSectionNode(section);
  1566. if (!sect)
  1567. throw MakeStringException(-1, "CEsdlScriptContext:createXpathContext: section not found %s", section);
  1568. }
  1569. CLibXpathContext *xpathContext = new CLibXpathContext(static_cast<CLibXpathContext *>(primaryContext), doc, sect, strictParameterDeclaration);
  1570. StringBuffer val;
  1571. xpathContext->addVariable("method", getAttribute("esdl", "method", val));
  1572. xpathContext->addVariable("service", getAttribute("esdl", "service", val.clear()));
  1573. xpathContext->addVariable("request", getAttribute("esdl", "request", val.clear()));
  1574. //external parameters need <es:param> statements to make them accessible (in strict mode)
  1575. addXpathCtxConfigInputs(xpathContext);
  1576. if (!strictParameterDeclaration)
  1577. xpathContext->declareRemainingInputs();
  1578. return xpathContext;
  1579. }
  1580. IXpathContext *getCopiedSectionXpathContext(IXpathContext *primaryContext, const char *tgtSection, const char *srcSection, bool strictParameterDeclaration) override
  1581. {
  1582. ensureCopiedSection(tgtSection, srcSection, false);
  1583. return createXpathContext(primaryContext, tgtSection, strictParameterDeclaration);
  1584. }
  1585. virtual void cleanupBetweenScripts() override
  1586. {
  1587. removeSection("temporaries");
  1588. }
  1589. };
  1590. IEsdlScriptContext *createEsdlScriptContext(void * espCtx)
  1591. {
  1592. return new CEsdlScriptContext(espCtx);
  1593. }
  1594. extern IXpathContext* getXpathContext(const char * xmldoc, bool strictParameterDeclaration, bool removeDocNamespaces)
  1595. {
  1596. return new CLibXpathContext(xmldoc, strictParameterDeclaration, removeDocNamespaces);
  1597. }