pv_xml.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. /**
  2. *
  3. * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
  4. *
  5. * This file is part of kamailio, a free SIP server.
  6. *
  7. * Kamailio is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version
  11. *
  12. * Kamailio is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #include <stdlib.h>
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include <libxml/tree.h>
  25. #include <libxml/parser.h>
  26. #include <libxml/xpath.h>
  27. #include <libxml/xpathInternals.h>
  28. #include "../../mem/mem.h"
  29. #include "../../parser/parse_param.h"
  30. #include "../../hashes.h"
  31. #include "../../dprint.h"
  32. #include "pv_xml.h"
  33. int pv_xml_buf_size = 4095;
  34. typedef struct _pv_xml {
  35. str docname;
  36. unsigned int docid;
  37. str inbuf;
  38. str outbuf;
  39. int updated;
  40. xmlDocPtr doc;
  41. xmlXPathContextPtr xpathCtx;
  42. xmlXPathObjectPtr xpathObj;
  43. struct _pv_xml *next;
  44. } pv_xml_t;
  45. typedef struct _pv_xml_spec {
  46. str docname;
  47. pv_xml_t *xdoc;
  48. int type;
  49. pv_elem_t *pve;
  50. } pv_xml_spec_t;
  51. pv_xml_t *_pv_xml_root = NULL;
  52. param_t *_pv_xml_ns_root = NULL;
  53. pv_xml_t *pv_xml_get_struct(str *name)
  54. {
  55. unsigned int docid;
  56. pv_xml_t *it;
  57. docid = get_hash1_raw(name->s, name->len);
  58. it = _pv_xml_root;
  59. while(it!=NULL)
  60. {
  61. if(docid == it->docid && name->len==it->docname.len
  62. && strncmp(name->s, it->docname.s, name->len)==0)
  63. {
  64. LM_DBG("doc found [%.*s]\n", name->len, name->s);
  65. return it;
  66. }
  67. it = it->next;
  68. }
  69. it = (pv_xml_t*)pkg_malloc(sizeof(pv_xml_t)+2*(pv_xml_buf_size+1));
  70. if(it==NULL)
  71. {
  72. LM_ERR("no more pkg\n");
  73. return NULL;
  74. }
  75. memset(it, 0, sizeof(pv_xml_t)+2*(pv_xml_buf_size+1));
  76. it->docid = docid;
  77. it->docname = *name;
  78. it->inbuf.s = (char*)it + sizeof(pv_xml_t);
  79. it->outbuf.s = it->inbuf.s + pv_xml_buf_size+1;
  80. it->next = _pv_xml_root;
  81. _pv_xml_root = it;
  82. return it;
  83. }
  84. int pv_xpath_nodes_eval(pv_xml_t *xdoc)
  85. {
  86. int size;
  87. int i;
  88. xmlNodeSetPtr nodes;
  89. char *p;
  90. xmlChar *keyword;
  91. xmlBufferPtr psBuf;
  92. if(xdoc==NULL || xdoc->doc==NULL || xdoc->xpathCtx==NULL
  93. || xdoc->xpathObj==NULL)
  94. return -1;
  95. nodes = xdoc->xpathObj->nodesetval;
  96. if(nodes==NULL)
  97. {
  98. xdoc->outbuf.len = 0;
  99. xdoc->outbuf.s[xdoc->outbuf.len] = '\0';
  100. return 0;
  101. }
  102. size = nodes->nodeNr;
  103. p = xdoc->outbuf.s;
  104. for(i = 0; i < size; ++i)
  105. {
  106. if(nodes->nodeTab[i]==NULL)
  107. continue;
  108. if(i!=0)
  109. {
  110. *p = ',';
  111. p++;
  112. }
  113. if(nodes->nodeTab[i]->type == XML_ATTRIBUTE_NODE)
  114. {
  115. keyword = xmlNodeListGetString(xdoc->doc,
  116. nodes->nodeTab[i]->children, 0);
  117. if(keyword != NULL)
  118. {
  119. strcpy(p, (char*)keyword);
  120. p += strlen((char*)keyword);
  121. xmlFree(keyword);
  122. keyword = NULL;
  123. }
  124. } else {
  125. if(nodes->nodeTab[i]->content!=NULL)
  126. {
  127. strcpy(p, (char*)nodes->nodeTab[i]->content);
  128. p += strlen((char*)nodes->nodeTab[i]->content);
  129. } else {
  130. psBuf = xmlBufferCreate();
  131. if(psBuf != NULL && xmlNodeDump(psBuf, xdoc->doc,
  132. nodes->nodeTab[i], 0, 0)>0)
  133. {
  134. strcpy(p, (char*)xmlBufferContent(psBuf));
  135. p += strlen((char*)xmlBufferContent(psBuf));
  136. }
  137. if(psBuf != NULL) xmlBufferFree(psBuf);
  138. psBuf = NULL;
  139. }
  140. }
  141. }
  142. xdoc->outbuf.len = p - xdoc->outbuf.s;
  143. xdoc->outbuf.s[xdoc->outbuf.len] = '\0';
  144. return 0;
  145. }
  146. int pv_xpath_nodes_update(pv_xml_t *xdoc, str *val)
  147. {
  148. xmlNodeSetPtr nodes;
  149. const xmlChar* value;
  150. int size;
  151. int i;
  152. if(xdoc==NULL || xdoc->doc==NULL || xdoc->xpathCtx==NULL
  153. || xdoc->xpathObj==NULL || val==NULL)
  154. return -1;
  155. if(val->len>pv_xml_buf_size)
  156. {
  157. LM_ERR("internal buffer overflow - %d\n", val->len);
  158. return -1;
  159. }
  160. nodes = xdoc->xpathObj->nodesetval;
  161. if(nodes==NULL)
  162. return 0;
  163. size = nodes->nodeNr;
  164. value = (const xmlChar*)xdoc->outbuf.s;
  165. memcpy(xdoc->outbuf.s, val->s, val->len);
  166. xdoc->outbuf.s[val->len] = '\0';
  167. xdoc->outbuf.len = val->len;
  168. /*
  169. * NOTE: the nodes are processed in reverse order, i.e. reverse document
  170. * order because xmlNodeSetContent can actually free up descendant
  171. * of the node and such nodes may have been selected too ! Handling
  172. * in reverse order ensure that descendant are accessed first, before
  173. * they get removed. Mixing XPath and modifications on a tree must be
  174. * done carefully !
  175. */
  176. for(i = size - 1; i >= 0; i--) {
  177. if(nodes->nodeTab[i]==NULL)
  178. continue;
  179. xmlNodeSetContent(nodes->nodeTab[i], value);
  180. /*
  181. * All the elements returned by an XPath query are pointers to
  182. * elements from the tree *except* namespace nodes where the XPath
  183. * semantic is different from the implementation in libxml2 tree.
  184. * As a result when a returned node set is freed when
  185. * xmlXPathFreeObject() is called, that routine must check the
  186. * element type. But node from the returned set may have been removed
  187. * by xmlNodeSetContent() resulting in access to freed data.
  188. * This can be exercised by running
  189. * valgrind xpath2 test3.xml '//discarded' discarded
  190. * There is 2 ways around it:
  191. * - make a copy of the pointers to the nodes from the result set
  192. * then call xmlXPathFreeObject() and then modify the nodes
  193. * or
  194. * - remove the reference to the modified nodes from the node set
  195. * as they are processed, if they are not namespace nodes.
  196. */
  197. if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL)
  198. nodes->nodeTab[i] = NULL;
  199. }
  200. xdoc->outbuf.s[0] = '\0';
  201. xdoc->outbuf.len = 0;
  202. return 0;
  203. }
  204. void pv_xml_register_ns(xmlXPathContextPtr xpathCtx)
  205. {
  206. param_t *ns;
  207. ns = _pv_xml_ns_root;
  208. while(ns) {
  209. xmlXPathRegisterNs(xpathCtx, (xmlChar*)ns->name.s,
  210. (xmlChar*)ns->body.s);
  211. ns = ns->next;
  212. }
  213. }
  214. int pv_get_xml(struct sip_msg *msg, pv_param_t *param,
  215. pv_value_t *res)
  216. {
  217. pv_xml_spec_t *pxs = NULL;
  218. str xpaths;
  219. int size = 0;
  220. xmlChar *xmem = NULL;
  221. pxs = (pv_xml_spec_t*)param->pvn.u.dname;
  222. if(pxs->xdoc==NULL)
  223. return -1;
  224. switch(pxs->type) {
  225. case 0:
  226. /* get document */
  227. if(pxs->xdoc->inbuf.len<=0)
  228. return pv_get_null(msg, param, res);
  229. if(pxs->xdoc->doc == NULL || pxs->xdoc->updated == 0)
  230. return pv_get_strval(msg, param, res, &pxs->xdoc->inbuf);
  231. xmlDocDumpMemory(pxs->xdoc->doc, &xmem, &size);
  232. if(xmem!=NULL)
  233. {
  234. if(size>pv_xml_buf_size)
  235. {
  236. xmlFree(xmem);
  237. return pv_get_null(msg, param, res);
  238. }
  239. memcpy(pxs->xdoc->outbuf.s, xmem, size);
  240. pxs->xdoc->outbuf.s[size] = '\0';
  241. pxs->xdoc->outbuf.len = size;
  242. xmlFree(xmem);
  243. return pv_get_strval(msg, param, res, &pxs->xdoc->outbuf);
  244. }
  245. return pv_get_null(msg, param, res);
  246. break;
  247. case 1:
  248. /* get xpath element */
  249. if(pxs->xdoc->doc == NULL)
  250. {
  251. if(pxs->xdoc->inbuf.len<=0)
  252. return pv_get_null(msg, param, res);
  253. pxs->xdoc->doc = xmlParseMemory(pxs->xdoc->inbuf.s,
  254. pxs->xdoc->inbuf.len);
  255. if(pxs->xdoc->doc == NULL)
  256. return pv_get_null(msg, param, res);
  257. }
  258. if(pxs->xdoc->xpathCtx == NULL)
  259. {
  260. pxs->xdoc->xpathCtx = xmlXPathNewContext(pxs->xdoc->doc);
  261. if(pxs->xdoc->xpathCtx == NULL)
  262. {
  263. LM_ERR("unable to create new XPath context\n");
  264. xmlFreeDoc(pxs->xdoc->doc);
  265. pxs->xdoc->doc = NULL;
  266. return pv_get_null(msg, param, res);
  267. }
  268. }
  269. if(pv_printf_s(msg, pxs->pve, &xpaths)!=0)
  270. {
  271. LM_ERR("cannot get xpath string\n");
  272. return pv_get_null(msg, param, res);
  273. }
  274. /* Evaluate xpath expression */
  275. pv_xml_register_ns(pxs->xdoc->xpathCtx);
  276. pxs->xdoc->xpathObj = xmlXPathEvalExpression(
  277. (const xmlChar*)xpaths.s, pxs->xdoc->xpathCtx);
  278. if(pxs->xdoc->xpathObj == NULL)
  279. {
  280. LM_ERR("unable to evaluate xpath expression [%s/%d]\n",
  281. xpaths.s, xpaths.len);
  282. xmlXPathFreeContext(pxs->xdoc->xpathCtx);
  283. xmlFreeDoc(pxs->xdoc->doc);
  284. pxs->xdoc->xpathCtx = NULL;
  285. pxs->xdoc->doc = NULL;
  286. return pv_get_null(msg, param, res);
  287. }
  288. /* Print results */
  289. if(pv_xpath_nodes_eval(pxs->xdoc)<0)
  290. {
  291. xmlXPathFreeObject(pxs->xdoc->xpathObj);
  292. xmlXPathFreeContext(pxs->xdoc->xpathCtx);
  293. xmlFreeDoc(pxs->xdoc->doc);
  294. pxs->xdoc->xpathObj = NULL;
  295. pxs->xdoc->xpathCtx = NULL;
  296. pxs->xdoc->doc = NULL;
  297. return pv_get_null(msg, param, res);
  298. }
  299. xmlXPathFreeObject(pxs->xdoc->xpathObj);
  300. pxs->xdoc->xpathObj = NULL;
  301. return pv_get_strval(msg, param, res, &pxs->xdoc->outbuf);
  302. break;
  303. default:
  304. return pv_get_null(msg, param, res);
  305. }
  306. return pv_get_null(msg, param, res);
  307. }
  308. int pv_set_xml(struct sip_msg* msg, pv_param_t *param,
  309. int op, pv_value_t *val)
  310. {
  311. pv_xml_spec_t *pxs = NULL;
  312. str xpaths;
  313. pxs = (pv_xml_spec_t*)param->pvn.u.dname;
  314. if(pxs->xdoc==NULL)
  315. return -1;
  316. if(!(val->flags&PV_VAL_STR))
  317. return -1;
  318. switch(pxs->type) {
  319. case 0:
  320. /* set document */
  321. if(pxs->xdoc->doc!=NULL)
  322. {
  323. if(pxs->xdoc->xpathCtx!=NULL)
  324. {
  325. xmlXPathFreeContext(pxs->xdoc->xpathCtx);
  326. pxs->xdoc->xpathCtx = NULL;
  327. }
  328. xmlFreeDoc(pxs->xdoc->doc);
  329. pxs->xdoc->doc = NULL;
  330. }
  331. if(val->rs.len>pv_xml_buf_size)
  332. {
  333. LM_ERR("local buffer overflow - %d\n", val->rs.len);
  334. return -1;
  335. }
  336. memcpy(pxs->xdoc->inbuf.s, val->rs.s, val->rs.len);
  337. pxs->xdoc->inbuf.s[val->rs.len] = '\0';
  338. pxs->xdoc->inbuf.len = val->rs.len;
  339. pxs->xdoc->updated = 0;
  340. return 0;
  341. break;
  342. case 1:
  343. /* set xpath element */
  344. if(pxs->xdoc->doc == NULL)
  345. {
  346. if(pxs->xdoc->inbuf.len<=0)
  347. return -1;
  348. pxs->xdoc->doc = xmlParseMemory(pxs->xdoc->inbuf.s,
  349. pxs->xdoc->inbuf.len);
  350. if(pxs->xdoc->doc == NULL)
  351. return -1;
  352. }
  353. if(pxs->xdoc->xpathCtx == NULL)
  354. {
  355. pxs->xdoc->xpathCtx = xmlXPathNewContext(pxs->xdoc->doc);
  356. if(pxs->xdoc->xpathCtx == NULL)
  357. {
  358. LM_ERR("unable to create new XPath context\n");
  359. xmlFreeDoc(pxs->xdoc->doc);
  360. pxs->xdoc->doc = NULL;
  361. return -1;
  362. }
  363. }
  364. if(pv_printf_s(msg, pxs->pve, &xpaths)!=0)
  365. {
  366. LM_ERR("cannot get xpath string\n");
  367. return -1;
  368. }
  369. /* Evaluate xpath expression */
  370. pxs->xdoc->xpathObj = xmlXPathEvalExpression(
  371. (const xmlChar*)xpaths.s, pxs->xdoc->xpathCtx);
  372. if(pxs->xdoc->xpathObj == NULL)
  373. {
  374. LM_ERR("unable to evaluate xpath expression [%s]\n", xpaths.s);
  375. xmlXPathFreeContext(pxs->xdoc->xpathCtx);
  376. xmlFreeDoc(pxs->xdoc->doc);
  377. pxs->xdoc->xpathCtx = NULL;
  378. pxs->xdoc->doc = NULL;
  379. return -1;
  380. }
  381. /* Set value */
  382. if(pv_xpath_nodes_update(pxs->xdoc, &val->rs)<0)
  383. {
  384. LM_ERR("unable to update xpath [%s] - [%.*s]\n", xpaths.s,
  385. val->rs.len, val->rs.s);
  386. xmlXPathFreeObject(pxs->xdoc->xpathObj);
  387. xmlXPathFreeContext(pxs->xdoc->xpathCtx);
  388. xmlFreeDoc(pxs->xdoc->doc);
  389. pxs->xdoc->xpathObj = NULL;
  390. pxs->xdoc->xpathCtx = NULL;
  391. pxs->xdoc->doc = NULL;
  392. return -1;
  393. }
  394. pxs->xdoc->updated = 1;
  395. xmlXPathFreeObject(pxs->xdoc->xpathObj);
  396. pxs->xdoc->xpathObj = NULL;
  397. return 0;
  398. break;
  399. default:
  400. return -1;
  401. }
  402. return 0;
  403. }
  404. int pv_parse_xml_name(pv_spec_p sp, str *in)
  405. {
  406. pv_xml_spec_t *pxs = NULL;
  407. char *p;
  408. str pvs;
  409. if(in->s==NULL || in->len<=0)
  410. return -1;
  411. pxs = (pv_xml_spec_t*)pkg_malloc(sizeof(pv_xml_spec_t));
  412. if(pxs==NULL)
  413. return -1;
  414. memset(pxs, 0, sizeof(pv_xml_spec_t));
  415. p = in->s;
  416. while(p<in->s+in->len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r'))
  417. p++;
  418. if(p>in->s+in->len || *p=='\0')
  419. goto error;
  420. pxs->docname.s = p;
  421. while(p < in->s + in->len)
  422. {
  423. if(*p=='=' || *p==' ' || *p=='\t' || *p=='\n' || *p=='\r')
  424. break;
  425. p++;
  426. }
  427. if(p>in->s+in->len || *p=='\0')
  428. goto error;
  429. pxs->docname.len = p - pxs->docname.s;
  430. if(*p!='=')
  431. {
  432. while(p<in->s+in->len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r'))
  433. p++;
  434. if(p>in->s+in->len || *p=='\0' || *p!='=')
  435. goto error;
  436. }
  437. p++;
  438. if(*p!='>')
  439. goto error;
  440. p++;
  441. pvs.len = in->len - (int)(p - in->s);
  442. pvs.s = p;
  443. LM_DBG("xmldoc [%.*s] - key [%.*s]\n", pxs->docname.len, pxs->docname.s,
  444. pvs.len, pvs.s);
  445. if(pvs.len>=3 && strncmp(pvs.s, "doc", 3)==0) {
  446. pxs->type = 0;
  447. } else if(pvs.len>6 && strncmp(pvs.s, "xpath:", 6)==0) {
  448. pvs.s += 6;
  449. pvs.len -= 6;
  450. pxs->type = 1;
  451. LM_DBG("*** xpath expr [%.*s]\n", pvs.len, pvs.s);
  452. if(pv_parse_format(&pvs, &pxs->pve)<0 || pxs->pve==NULL)
  453. {
  454. LM_ERR("wrong xpath format [%.*s]\n", in->len, in->s);
  455. goto error;
  456. }
  457. } else {
  458. LM_ERR("unknown key type [%.*s]\n", in->len, in->s);
  459. goto error;
  460. }
  461. pxs->xdoc = pv_xml_get_struct(&pxs->docname);
  462. sp->pvp.pvn.u.dname = (void*)pxs;
  463. sp->pvp.pvn.type = PV_NAME_OTHER;
  464. return 0;
  465. error:
  466. if(pxs!=NULL)
  467. pkg_free(pxs);
  468. return -1;
  469. }
  470. int pv_xml_ns_param(modparam_t type, void *val)
  471. {
  472. char *p;
  473. param_t *ns;
  474. if(val==NULL)
  475. goto error;
  476. ns = (param_t*)pkg_malloc(sizeof(param_t));
  477. if(ns==NULL)
  478. {
  479. LM_ERR("no more pkg\n");
  480. goto error;
  481. }
  482. memset(ns, 0, sizeof(param_t));
  483. p = strchr((const char*)val, '=');
  484. if(p==NULL)
  485. {
  486. ns->name.s = "";
  487. ns->body.s = (char*)val;
  488. ns->body.len = strlen(ns->body.s);
  489. } else {
  490. *p = 0;
  491. p++;
  492. ns->name.s = (char*)val;
  493. ns->name.len = strlen(ns->name.s);
  494. ns->body.s = p;
  495. ns->body.len = strlen(ns->body.s);
  496. }
  497. ns->next = _pv_xml_ns_root;
  498. _pv_xml_ns_root = ns;
  499. return 0;
  500. error:
  501. return -1;
  502. }