libxml_xpathprocessor.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  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 "xpathprocessor.hpp"
  30. class CLibCompiledXpath : public CInterface, public ICompiledXpath
  31. {
  32. private:
  33. xmlXPathCompExprPtr m_compiledXpathExpression = nullptr;
  34. StringBuffer m_xpath;
  35. ReadWriteLock m_rwlock;
  36. public:
  37. IMPLEMENT_IINTERFACE;
  38. CLibCompiledXpath(const char * xpath)
  39. {
  40. m_xpath.set(xpath);
  41. m_compiledXpathExpression = xmlXPathCompile(BAD_CAST m_xpath.str());
  42. }
  43. ~CLibCompiledXpath()
  44. {
  45. xmlXPathFreeCompExpr(m_compiledXpathExpression);
  46. }
  47. const char * getXpath()
  48. {
  49. return m_xpath.str();
  50. }
  51. xmlXPathCompExprPtr getCompiledXPathExpression()
  52. {
  53. return m_compiledXpathExpression;
  54. }
  55. };
  56. class CLibXpathContext : public CInterface, public IXpathContext
  57. {
  58. private:
  59. xmlDocPtr m_xmlDoc = nullptr;
  60. xmlXPathContextPtr m_xpathContext = nullptr;
  61. StringBuffer m_xpath;
  62. ReadWriteLock m_rwlock;
  63. public:
  64. IMPLEMENT_IINTERFACE;
  65. CLibXpathContext(const char * xmldoc) //not thread safe
  66. {
  67. setXmlDoc(xmldoc);
  68. }
  69. ~CLibXpathContext()
  70. {
  71. xmlXPathFreeContext(m_xpathContext);
  72. xmlFreeDoc(m_xmlDoc);
  73. }
  74. static void tableScanCallback(void *payload, void *data, xmlChar *name)
  75. {
  76. DBGLOG("k/v == [%s,%s]\n", (char *) name, (char *) payload);
  77. }
  78. virtual const char * getXpath() override
  79. {
  80. return m_xpath.str();
  81. }
  82. virtual bool addVariable(const char * name, const char * val) override
  83. {
  84. WriteLockBlock wblock(m_rwlock);
  85. if (m_xpathContext && val && *val)
  86. {
  87. return xmlXPathRegisterVariable(m_xpathContext, (xmlChar *)name, xmlXPathNewCString(val)) == 0;
  88. }
  89. return false;
  90. }
  91. virtual const char * getVariable(const char * name) override
  92. {
  93. ReadLockBlock rblock(m_rwlock);
  94. if (m_xpathContext)
  95. return (const char *)xmlXPathCastToString(xmlXPathVariableLookup(m_xpathContext, (const xmlChar *)name));
  96. else
  97. return nullptr;
  98. }
  99. virtual bool evaluateAsBoolean(const char * xpath)
  100. {
  101. bool bresult = false;
  102. if (xpath && *xpath)
  103. {
  104. xmlXPathObjectPtr evaluatedXpathObj = evaluate(xpath);
  105. if (evaluatedXpathObj && evaluatedXpathObj->type == XPATH_BOOLEAN)
  106. {
  107. bresult = evaluatedXpathObj->boolval;
  108. }
  109. else
  110. throw MakeStringException(-1,"XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s' as boolean", m_xpath.str());
  111. xmlXPathFreeObject(evaluatedXpathObj);
  112. }
  113. else
  114. throw MakeStringException(-1,"XpathProcessor:evaluateAsBoolean: Error: empty xpath provided");
  115. return bresult;
  116. }
  117. virtual bool evaluateAsString(const char * xpath, StringBuffer & evaluated)
  118. {
  119. if (xpath && *xpath)
  120. {
  121. xmlXPathObjectPtr evaluatedXpathObj = evaluate(xpath);
  122. evaluated.clear();
  123. if (evaluatedXpathObj && evaluatedXpathObj->type == XPATH_NODESET)
  124. {
  125. xmlNodeSetPtr nodes = evaluatedXpathObj->nodesetval;
  126. for (int i = 0; i < nodes->nodeNr; i++)
  127. {
  128. xmlNodePtr nodeTab = nodes->nodeTab[i];
  129. evaluated.append((const char *)xmlNodeGetContent(nodeTab));
  130. }
  131. xmlXPathFreeObject(evaluatedXpathObj);
  132. }
  133. else
  134. throw MakeStringException(-1,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH as string");
  135. }
  136. else
  137. throw MakeStringException(-1,"XpathProcessor:evaluateAsString: Error: empty xpath provided");
  138. return evaluated.str();
  139. }
  140. virtual bool evaluateAsBoolean(ICompiledXpath * compiledXpath) override
  141. {
  142. bool bresult = false;
  143. CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  144. xmlXPathObjectPtr evaluatedXpathObj = evaluate(clibCompiledXpath->getCompiledXPathExpression());
  145. if (evaluatedXpathObj && evaluatedXpathObj->type == XPATH_BOOLEAN)
  146. {
  147. bresult = evaluatedXpathObj->boolval;
  148. }
  149. else
  150. throw MakeStringException(-1,"XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s' as boolean", m_xpath.str());
  151. xmlXPathFreeObject(evaluatedXpathObj);
  152. return bresult;
  153. }
  154. virtual const char * evaluateAsString(ICompiledXpath * compiledXpath, StringBuffer & evaluated) override
  155. {
  156. CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
  157. xmlXPathObjectPtr evaluatedXpathObj = evaluate(clibCompiledXpath->getCompiledXPathExpression());
  158. evaluated.clear();
  159. if (evaluatedXpathObj)
  160. {
  161. switch (evaluatedXpathObj->type)
  162. {
  163. case XPATH_NODESET:
  164. {
  165. xmlNodeSetPtr nodes = evaluatedXpathObj->nodesetval;
  166. for (int i = 0; i < nodes->nodeNr; i++)
  167. {
  168. xmlNodePtr nodeTab = nodes->nodeTab[i];
  169. evaluated.append((const char *)xmlNodeGetContent(nodeTab));
  170. }
  171. break;
  172. }
  173. case XPATH_BOOLEAN:
  174. case XPATH_NUMBER:
  175. case XPATH_STRING:
  176. case XPATH_POINT:
  177. case XPATH_RANGE:
  178. case XPATH_LOCATIONSET:
  179. case XPATH_USERS:
  180. case XPATH_XSLT_TREE:
  181. {
  182. evaluatedXpathObj = xmlXPathConvertString (evaluatedXpathObj); //existing object is freed
  183. if (!evaluatedXpathObj)
  184. throw MakeStringException(-1,"XpathProcessor:evaluateAsString: could not convert result to string");
  185. evaluated.append(evaluatedXpathObj->stringval);
  186. break;
  187. }
  188. default:
  189. {
  190. xmlXPathFreeObject(evaluatedXpathObj);
  191. throw MakeStringException(-1,"XpathProcessor:evaluateAsString: Error: Encountered unsupported XPATH type");
  192. break;
  193. }
  194. }
  195. }
  196. else
  197. {
  198. xmlXPathFreeObject(evaluatedXpathObj);
  199. throw MakeStringException(-1,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH as string");
  200. }
  201. xmlXPathFreeObject(evaluatedXpathObj);
  202. return evaluated.str();
  203. }
  204. private:
  205. virtual bool setXmlDoc(const char * xmldoc) override
  206. {
  207. if (xmldoc && * xmldoc)
  208. {
  209. m_xmlDoc = xmlParseDoc((const unsigned char *)xmldoc);
  210. if (m_xmlDoc == nullptr)
  211. {
  212. ERRLOG("XpathProcessor:setxmldoc Error: Unable to parse XMLLib document");
  213. return false;
  214. }
  215. // Create xpath evaluation context
  216. m_xpathContext = xmlXPathNewContext(m_xmlDoc);
  217. if(m_xpathContext == nullptr)
  218. {
  219. ERRLOG("XpathProcessor:setxmldoc: Error: Unable to create new XMLLib XPath context");
  220. return false;
  221. }
  222. return true;
  223. }
  224. return false;
  225. }
  226. virtual xmlXPathObjectPtr evaluate(xmlXPathCompExprPtr compiledXpath)
  227. {
  228. xmlXPathObjectPtr evaluatedXpathObj = nullptr;
  229. if (compiledXpath)
  230. {
  231. ReadLockBlock rlock(m_rwlock);
  232. if ( m_xpathContext)
  233. {
  234. evaluatedXpathObj = xmlXPathCompiledEval(compiledXpath, m_xpathContext);
  235. }
  236. else
  237. {
  238. throw MakeStringException(-1,"XpathProcessor:evaluate: Error: Invalid xpathCotext detected. Ensure xmldoc has been set");
  239. }
  240. }
  241. return evaluatedXpathObj;
  242. }
  243. virtual xmlXPathObjectPtr evaluate(const char * xpath)
  244. {
  245. xmlXPathObjectPtr evaluatedXpathObj = nullptr;
  246. if (xpath && *xpath)
  247. {
  248. ReadLockBlock rlock(m_rwlock);
  249. if ( m_xpathContext)
  250. {
  251. evaluatedXpathObj = xmlXPathEval((const xmlChar *)xpath, m_xpathContext);
  252. }
  253. else
  254. {
  255. throw MakeStringException(-1,"XpathProcessor:evaluate: Error: Invalid xpathCotext detected. Ensure xmldoc has been set");
  256. }
  257. }
  258. return evaluatedXpathObj;
  259. }
  260. };
  261. extern ICompiledXpath* getCompiledXpath(const char * xpath)
  262. {
  263. return new CLibCompiledXpath(xpath);
  264. }
  265. extern IXpathContext* getXpathContext(const char * xmldoc)
  266. {
  267. return new CLibXpathContext(xmldoc);
  268. }