libxml_xpathprocessor.cpp 68 KB

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