1 /*
2 * Copyright (C) 2012 FRAFOS GmbH
3 *
4 * Development sponsored by Sipwise GmbH.
5 *
6 * This file is part of SEMS, a free SIP media server.
7 *
8 * SEMS is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version. This program is released under
12 * the GPL with the additional exemption that compiling, linking,
13 * and/or using OpenSSL is allowed.
14 *
15 * For a license to use the SEMS software under conditions
16 * other than those described here, or to purchase support for this
17 * software, please contact iptel.org by e-mail at the following addresses:
18 * info@iptel.org
19 *
20 * SEMS is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 */
29 #include "ModXml.h"
30 #include "log.h"
31 #include "AmUtils.h"
32
33 SC_EXPORT(MOD_CLS_NAME);
34
35 void xml_err_func(void *ctx, const char *msg, ...);
36 xmlGenericErrorFunc handler = (xmlGenericErrorFunc)xml_err_func;
37 int xml_log_level = L_ERR;
38
preload()39 int MOD_CLS_NAME::preload() {
40 DBG("initializing libxml2...\n");
41 xmlInitParser();
42 initGenericErrorDefaultFunc(&handler);
43 handler = (xmlGenericErrorFunc)xml_err_func;
44 xmlSetGenericErrorFunc(NULL, &xml_err_func);
45 xmlKeepBlanksDefault(0);
46 xmlIndentTreeOutput = 1; // doesn't seem to have effect :/
47 return 0;
48 }
49
MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME)50 MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
51 DEF_CMD("xml.parse", MODXMLParseAction);
52 DEF_CMD("xml.parseSIPMsgBody", MODXMLParseSIPMsgBodyAction);
53
54 DEF_CMD("xml.evalXPath", MODXMLEvalXPathAction);
55 DEF_CMD("xml.XPathResultCount", MODXMLXPathResultNodeCount);
56 DEF_CMD("xml.getXPathResult", MODXMLgetXPathResult);
57 DEF_CMD("xml.printXPathResult", MODXMLprintXPathResult);
58 DEF_CMD("xml.updateXPathResult", MODXMLupdateXPathResult);
59
60 DEF_CMD("xml.docDump", MODXMLdocDump);
61
62 DEF_CMD("xml.setLoglevel", MODXMLSetLogLevelAction);
63
64 } MOD_ACTIONEXPORT_END;
65
66 MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME);
67
~ModXmlDoc()68 ModXmlDoc::~ModXmlDoc() {
69 if (NULL != doc) {
70 DBG("freeing XML document [%p]\n", doc);
71 xmlFreeDoc(doc);
72 }
73 }
74
~ModXmlXPathObj()75 ModXmlXPathObj::~ModXmlXPathObj() {
76 if (NULL != xpathObj) {
77 DBG("freeing XML xpath obj [%p]\n", xpathObj);
78 xmlXPathFreeObject(xpathObj);
79 }
80 if (NULL != xpathCtx) {
81 DBG("freeing XML xpath ctx [%p]\n", xpathCtx);
82 xmlXPathFreeContext(xpathCtx);
83 }
84 }
85
86 #define TMP_BUF_SIZE 256
xml_err_func(void * ctx,const char * msg,...)87 void xml_err_func(void *ctx, const char *msg, ...) {
88 char _string[TMP_BUF_SIZE];
89 va_list arg_ptr;
90 va_start(arg_ptr, msg);
91 vsnprintf(_string, TMP_BUF_SIZE, msg, arg_ptr);
92 va_end(arg_ptr);
93
94 _LOG(xml_log_level, "%s", _string);
95 }
96
97 CONST_ACTION_2P(MODXMLParseSIPMsgBodyAction, ',', false);
EXEC_ACTION_START(MODXMLParseSIPMsgBodyAction)98 EXEC_ACTION_START(MODXMLParseSIPMsgBodyAction) {
99 string msgbody_var = resolveVars(par1, sess, sc_sess, event_params);
100 string dstname = resolveVars(par2, sess, sc_sess, event_params);
101 AVarMapT::iterator it = sc_sess->avar.find(msgbody_var);
102 if (it==sc_sess->avar.end()) {
103 DBG("no message body in avar '%s'\n", msgbody_var.c_str());
104 sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
105 sc_sess->SET_STRERROR("no message body in avar " + msgbody_var);
106 EXEC_ACTION_STOP;
107 }
108 AmMimeBody* msgbody = dynamic_cast<AmMimeBody*>(it->second.asObject());
109 if (NULL == msgbody) {
110 DBG("no AmMimeBody in avar '%s'\n", msgbody_var.c_str());
111 sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
112 sc_sess->SET_STRERROR("no AmMimeBody in avar " + msgbody_var);
113 EXEC_ACTION_STOP;
114 }
115 const unsigned char* b = msgbody->getPayload();
116 if (b==NULL) {
117 DBG("empty AmMimeBody in avar '%s'\n", msgbody_var.c_str());
118 sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
119 sc_sess->SET_STRERROR("no AmMimeBody in avar " + msgbody_var);
120 EXEC_ACTION_STOP;
121 }
122
123 xmlSetGenericErrorFunc(NULL, &xml_err_func);
124
125 xmlDocPtr doc =
126 xmlReadMemory((const char*)b, msgbody->getLen(), "noname.xml", NULL, 0);
127 if (doc == NULL) {
128 DBG("failed parsing XML document from '%s'\n", msgbody_var.c_str());
129 sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
130 sc_sess->SET_STRERROR("failed parsing XML document from " + msgbody_var);
131 EXEC_ACTION_STOP;
132 }
133
134 xmlSetGenericErrorFunc(doc, &xml_err_func);
135
136 ModXmlDoc* xml_doc = new ModXmlDoc(doc);
137 sc_sess->avar[dstname] = xml_doc;
138 DBG("parsed XML body document to '%s'\n", dstname.c_str());
139
140 // string basedir = resolveVars(par2, sess, sc_sess, event_params);
141 } EXEC_ACTION_END;
142
143 CONST_ACTION_2P(MODXMLParseAction, ',', false);
EXEC_ACTION_START(MODXMLParseAction)144 EXEC_ACTION_START(MODXMLParseAction) {
145 string xml_doc = resolveVars(par1, sess, sc_sess, event_params);
146 string dstname = resolveVars(par2, sess, sc_sess, event_params);
147
148 xmlSetGenericErrorFunc(NULL, &xml_err_func);
149
150 xmlDocPtr doc =
151 xmlReadMemory(xml_doc.c_str(), xml_doc.length(), "noname.xml", NULL, 0);
152 if (doc == NULL) {
153 DBG("failed parsing XML document from '%s'\n", xml_doc.c_str());
154 sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
155 sc_sess->SET_STRERROR("failed parsing XML document from " + xml_doc);
156 EXEC_ACTION_STOP;
157 }
158 xmlSetGenericErrorFunc(doc, &xml_err_func);
159
160 ModXmlDoc* xml_doc_var = new ModXmlDoc(doc);
161 sc_sess->avar[dstname] = xml_doc_var;
162 DBG("parsed XML body document to '%s'\n", dstname.c_str());
163 } EXEC_ACTION_END;
164
165 template<class T>
getXMLElemFromVariable(DSMSession * sc_sess,const string & var_name)166 T* getXMLElemFromVariable(DSMSession* sc_sess, const string& var_name) {
167 AVarMapT::iterator it = sc_sess->avar.find(var_name);
168 if (it == sc_sess->avar.end()) {
169 DBG("object '%s' not found\n", var_name.c_str());
170 sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
171 sc_sess->SET_STRERROR("object '"+var_name+"' not found\n");
172 return NULL;
173 }
174
175 T* doc = dynamic_cast<T*>(it->second.asObject());
176 if (NULL == doc) {
177 DBG("object '%s' is not the right type\n", var_name.c_str());
178 sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
179 sc_sess->SET_STRERROR("object '"+var_name+"' is not the right type\n");
180 return NULL;
181 }
182 return doc;
183 }
184
185 CONST_ACTION_2P(MODXMLEvalXPathAction, ',', false);
EXEC_ACTION_START(MODXMLEvalXPathAction)186 EXEC_ACTION_START(MODXMLEvalXPathAction) {
187 string xpath_expr = resolveVars(par1, sess, sc_sess, event_params);
188 string xml_doc_var = resolveVars(par2, sess, sc_sess, event_params);
189
190 xmlSetGenericErrorFunc(NULL, &xml_err_func);
191
192 ModXmlDoc* xml_doc = getXMLElemFromVariable<ModXmlDoc>(sc_sess, xml_doc_var);
193 if (NULL == xml_doc)
194 EXEC_ACTION_STOP;
195
196 xmlDocPtr doc = xml_doc->doc;
197
198 xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
199 if(xpathCtx == NULL) {
200 DBG("unable to create new XPath context\n");
201 sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
202 sc_sess->SET_STRERROR("unable to create new XPath context");
203 EXEC_ACTION_STOP;
204 }
205 xmlSetGenericErrorFunc(xpathCtx, &xml_err_func);
206
207 string xml_doc_ns = sc_sess->var[xml_doc_var+".ns"];
208 vector<string> ns_entries = explode(xml_doc_ns, " ");
209 for (vector<string>::iterator it=ns_entries.begin(); it != ns_entries.end(); it++) {
210 vector<string> ns = explode(*it, "=");
211 if (ns.size() != 2) {
212 DBG("script writer error: namespace entry must be prefix=href (got '%s')\n",
213 it->c_str());
214 sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
215 sc_sess->SET_STRERROR("script writer error: namespace entry must be prefix=href\n");
216 xmlXPathFreeContext(xpathCtx);
217 EXEC_ACTION_STOP;
218 }
219
220 if(xmlXPathRegisterNs(xpathCtx, (const xmlChar*)ns[0].c_str(),
221 (const xmlChar*)ns[1].c_str()) != 0) {
222 DBG("unable to register namespace %s=%s\n", ns[0].c_str(), ns[1].c_str());
223 sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
224 sc_sess->SET_STRERROR("unable to register namespace\n");
225 xmlXPathFreeContext(xpathCtx);
226 EXEC_ACTION_STOP;
227 }
228 DBG("registered namespace %s=%s\n", ns[0].c_str(), ns[1].c_str());
229 }
230
231 xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((const xmlChar*)xpath_expr.c_str(),
232 xpathCtx);
233 if(xpathObj == NULL) {
234 DBG("unable to evaluate xpath expression \"%s\"\n", xpath_expr.c_str());
235 sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
236 sc_sess->SET_STRERROR("unable to evaluate xpath expression");
237 xmlXPathFreeContext(xpathCtx);
238 EXEC_ACTION_STOP;
239 }
240
241 ModXmlXPathObj* xpath_obj = new ModXmlXPathObj(xpathObj, xpathCtx);
242 sc_sess->avar[xml_doc_var+".xpath"] = xpath_obj;
243 DBG("evaluated XPath expression on '%s' to '%s'\n",
244 xml_doc_var.c_str(), (xml_doc_var+".xpath").c_str());
245
246 } EXEC_ACTION_END;
247
248 CONST_ACTION_2P(MODXMLXPathResultNodeCount, '=', false);
EXEC_ACTION_START(MODXMLXPathResultNodeCount)249 EXEC_ACTION_START(MODXMLXPathResultNodeCount) {
250 string cnt_var = par1;
251 string xpath_res_var = resolveVars(par2, sess, sc_sess, event_params);
252
253 if (cnt_var.size() && cnt_var[0]=='$') {
254 cnt_var.erase(0,1);
255 }
256
257 ModXmlXPathObj* xpath_obj =
258 getXMLElemFromVariable<ModXmlXPathObj>(sc_sess, xpath_res_var);
259 if (NULL == xpath_obj){
260 DBG("no xpath result found in '%s'\n", xpath_res_var.c_str());
261 sc_sess->var[cnt_var] = "0";
262 EXEC_ACTION_STOP;
263 }
264
265 unsigned int res = (xpath_obj->xpathObj->nodesetval) ?
266 xpath_obj->xpathObj->nodesetval->nodeNr : 0;
267
268 sc_sess->var[cnt_var] = int2str(res);
269 DBG("set count $%s=%u\n", cnt_var.c_str(), res);
270
271 } EXEC_ACTION_END;
272
273
274 CONST_ACTION_2P(MODXMLgetXPathResult, '=', false);
EXEC_ACTION_START(MODXMLgetXPathResult)275 EXEC_ACTION_START(MODXMLgetXPathResult) {
276 string cnt_var = par1;
277 string xpath_res_var = resolveVars(par2, sess, sc_sess, event_params);
278
279 if (cnt_var.size() && cnt_var[0]=='$') {
280 cnt_var.erase(0,1);
281 }
282
283 ModXmlXPathObj* xpath_obj =
284 getXMLElemFromVariable<ModXmlXPathObj>(sc_sess, xpath_res_var);
285 if (NULL == xpath_obj){
286 DBG("no xpath result found in '%s'\n", xpath_res_var.c_str());
287 sc_sess->var[cnt_var] = "0";
288 EXEC_ACTION_STOP;
289 }
290
291 vector<string> res;
292
293 if (NULL == xpath_obj->xpathObj->nodesetval){
294 res.push_back(string());
295 } else {
296 xmlNodeSetPtr nodes = xpath_obj->xpathObj->nodesetval;
297 xmlNodePtr cur;
298
299 for (int i=0;i<xpath_obj->xpathObj->nodesetval->nodeNr;i++) {
300 if(nodes->nodeTab[i]->type == XML_NAMESPACE_DECL) {
301 xmlNsPtr ns;
302
303 ns = (xmlNsPtr)nodes->nodeTab[i];
304 cur = (xmlNodePtr)ns->next;
305 res.push_back(string(string((const char*) ns->prefix)+"="+string((const char*) ns->href)));
306
307 } else if(nodes->nodeTab[i]->type == XML_ELEMENT_NODE) {
308 cur = nodes->nodeTab[i];
309 xmlChar* c = xmlNodeGetContent(cur);
310 res.push_back(c ? string((const char*)c) : string());
311 } else {
312 cur = nodes->nodeTab[i];
313 res.push_back(string((const char*) cur->name)+"\": type "+int2str(cur->type));
314 }
315 }
316 }
317
318 if (res.size() == 1) {
319 sc_sess->var[cnt_var] = res[0];
320 DBG("set $%s='%s'\n", cnt_var.c_str(), res[0].c_str());
321 } else {
322 unsigned int p =0;
323 for (vector<string>::iterator it = res.begin(); it!= res.end(); it++) {
324 sc_sess->var[cnt_var+"["+int2str(p)+"]"] = *it;
325 DBG("set $%s='%s'\n", (cnt_var+"["+int2str(p)+"]").c_str(), it->c_str());
326 p++;
327 }
328 }
329
330
331 } EXEC_ACTION_END;
332
333 CONST_ACTION_2P(MODXMLprintXPathResult, '=', false);
EXEC_ACTION_START(MODXMLprintXPathResult)334 EXEC_ACTION_START(MODXMLprintXPathResult) {
335 string cnt_var = par1;
336 string xpath_res_var = resolveVars(par2, sess, sc_sess, event_params);
337
338 if (cnt_var.size() && cnt_var[0]=='$') {
339 cnt_var.erase(0,1);
340 }
341
342 ModXmlXPathObj* xpath_obj =
343 getXMLElemFromVariable<ModXmlXPathObj>(sc_sess, xpath_res_var);
344 if (NULL == xpath_obj){
345 DBG("no xpath result found in '%s'\n", xpath_res_var.c_str());
346 sc_sess->var[cnt_var] = "0";
347 EXEC_ACTION_STOP;
348 }
349
350 string& res = sc_sess->var[cnt_var];
351 if (NULL == xpath_obj->xpathObj->nodesetval){
352 res = "";
353 } else {
354 xmlNodeSetPtr nodes = xpath_obj->xpathObj->nodesetval;
355 xmlNodePtr cur;
356
357 for (int i=0;i<xpath_obj->xpathObj->nodesetval->nodeNr;i++) {
358 if(nodes->nodeTab[i]->type == XML_NAMESPACE_DECL) {
359 xmlNsPtr ns;
360
361 ns = (xmlNsPtr)nodes->nodeTab[i];
362 cur = (xmlNodePtr)ns->next;
363 if(cur->ns) {
364 res += "namespace \""+string((const char*) ns->prefix)+"\"=\""+string((const char*) ns->href)+"\" for node "+
365 string((const char*) cur->ns->href)+":"+string((const char*) cur->name)+"\n";
366 } else {
367 res += "namespace \""+ string((const char*) ns->prefix) +"\"=\""+ string((const char*) ns->href) +"\" for node "+
368 string((const char*) cur->name)+"\n";
369 }
370 } else if(nodes->nodeTab[i]->type == XML_ELEMENT_NODE) {
371 cur = nodes->nodeTab[i];
372 if(cur->ns) {
373 xmlChar* c = xmlNodeGetContent(cur);
374 res += "element node \""+string((const char*) cur->ns->href)+":"+string((const char*) cur->name)+"\" content: \""+
375 (c?string((const char*)c):string("NULL"))+ "\"\n";
376 } else {
377 xmlChar* c = xmlNodeGetContent(cur);
378 res += "element node \""+string((const char*) cur->name)+"\" content: \""+ (c?string((const char*)c):string("NULL"))+"\n";
379 }
380 } else {
381 cur = nodes->nodeTab[i];
382 res += "node \""+string((const char*) cur->name)+"\": type "+int2str(cur->type)+"\n";
383 }
384 }
385 }
386
387 DBG("set $%s='%s'\n", cnt_var.c_str(), res.c_str());
388
389 } EXEC_ACTION_END;
390
391
392 /**
393 modified from http://www.xmlsoft.org/examples/xpath2.c (MIT license)
394 * update_xpath_nodes:
395 * @nodes: the nodes set.
396 * @value: the new value for the node(s)
397 *
398 * Prints the @nodes content to @output.
399 */
400 static void
update_xpath_nodes(xmlNodeSetPtr nodes,const xmlChar * value,int index)401 update_xpath_nodes(xmlNodeSetPtr nodes, const xmlChar* value, int index) {
402 int size;
403 int i;
404
405 assert(value);
406 size = (nodes) ? nodes->nodeNr : 0;
407
408 if (index < 0) {
409 // update all
410 /*
411 * NOTE: the nodes are processed in reverse order, i.e. reverse document
412 * order because xmlNodeSetContent can actually free up descendant
413 * of the node and such nodes may have been selected too ! Handling
414 * in reverse order ensure that descendant are accessed first, before
415 * they get removed. Mixing XPath and modifications on a tree must be
416 * done carefully !
417 */
418 for(i = size - 1; i >= 0; i--) {
419 if (NULL == nodes->nodeTab[i])
420 continue;
421
422 xmlNodeSetContent(nodes->nodeTab[i], value);
423 /*
424 * All the elements returned by an XPath query are pointers to
425 * elements from the tree *except* namespace nodes where the XPath
426 * semantic is different from the implementation in libxml2 tree.
427 * As a result when a returned node set is freed when
428 * xmlXPathFreeObject() is called, that routine must check the
429 * element type. But node from the returned set may have been removed
430 * by xmlNodeSetContent() resulting in access to freed data.
431 * This can be exercised by running
432 * valgrind xpath2 test3.xml '//discarded' discarded
433 * There is 2 ways around it:
434 * - make a copy of the pointers to the nodes from the result set
435 * then call xmlXPathFreeObject() and then modify the nodes
436 * or
437 * - remove the reference to the modified nodes from the node set
438 * as they are processed, if they are not namespace nodes.
439 */
440 if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL)
441 nodes->nodeTab[i] = NULL;
442 }
443 } else {
444 if (index >= size) {
445 ERROR("trying to update XML node %d, size is %d\n", index, size);
446 return;
447 }
448
449 if (NULL == nodes->nodeTab[index]) {
450 ERROR("trying to update XML node %d which is NULL\n", index);
451 }
452
453 xmlNodeSetContent(nodes->nodeTab[index], value);
454 /*
455 * All the elements returned by an XPath query are pointers to
456 * elements from the tree *except* namespace nodes where the XPath
457 * semantic is different from the implementation in libxml2 tree.
458 * As a result when a returned node set is freed when
459 * xmlXPathFreeObject() is called, that routine must check the
460 * element type. But node from the returned set may have been removed
461 * by xmlNodeSetContent() resulting in access to freed data.
462 * This can be exercised by running
463 * valgrind xpath2 test3.xml '//discarded' discarded
464 * There is 2 ways around it:
465 * - make a copy of the pointers to the nodes from the result set
466 * then call xmlXPathFreeObject() and then modify the nodes
467 * or
468 * - remove the reference to the modified nodes from the node set
469 * as they are processed, if they are not namespace nodes.
470 */
471 if (nodes->nodeTab[index]->type != XML_NAMESPACE_DECL)
472 nodes->nodeTab[index] = NULL;
473 }
474 }
475
476 CONST_ACTION_2P(MODXMLupdateXPathResult, '=', false);
EXEC_ACTION_START(MODXMLupdateXPathResult)477 EXEC_ACTION_START(MODXMLupdateXPathResult) {
478 string xpath_res_var = resolveVars(par1, sess, sc_sess, event_params);
479 string value = resolveVars(par2, sess, sc_sess, event_params);
480
481 // support index
482 int index = -1;
483 if (xpath_res_var.size()>2 && xpath_res_var[xpath_res_var.size()-1]==']') {
484 size_t p = xpath_res_var.rfind('[');
485 if (p != string::npos) {
486 str2int(xpath_res_var.substr(p+1, xpath_res_var.size()-p-2), index);
487 xpath_res_var.erase(p);
488 }
489 }
490
491 DBG("index %d, var '%s'\n", index, xpath_res_var.c_str());
492 ModXmlXPathObj* xpath_obj =
493 getXMLElemFromVariable<ModXmlXPathObj>(sc_sess, xpath_res_var);
494 if (NULL == xpath_obj){
495 DBG("no xpath result found in '%s'\n", xpath_res_var.c_str());
496 EXEC_ACTION_STOP;
497 }
498 // todo: call xmlEncodeSpecialChars with doc
499
500
501 update_xpath_nodes(xpath_obj->xpathObj->nodesetval, (const xmlChar*) value.c_str(), index);
502
503 } EXEC_ACTION_END;
504
505 CONST_ACTION_2P(MODXMLdocDump, '=', false);
EXEC_ACTION_START(MODXMLdocDump)506 EXEC_ACTION_START(MODXMLdocDump) {
507 string res_var = par1;
508 string xml_doc_var = resolveVars(par2, sess, sc_sess, event_params);
509
510 if (res_var.size() && res_var[0]=='$') {
511 res_var.erase(0,1);
512 }
513
514 ModXmlDoc* xml_doc = getXMLElemFromVariable<ModXmlDoc>(sc_sess, xml_doc_var);
515 if (NULL == xml_doc) {
516 DBG("XML document not found t variable '%s'\n", xml_doc_var.c_str());
517 sc_sess->var[res_var] = "";
518 EXEC_ACTION_STOP;
519 }
520
521 xmlChar* mem;
522 int size;
523 xmlDocDumpFormatMemory(xml_doc->doc, &mem, &size, /* indent=*/1);
524 sc_sess->var[res_var] = string((const char*)mem, size);
525 xmlFree(mem);
526 DBG("set $%s to XML of size %d\n", res_var.c_str(), size);
527
528 } EXEC_ACTION_END;
529
530
EXEC_ACTION_START(MODXMLSetLogLevelAction)531 EXEC_ACTION_START(MODXMLSetLogLevelAction) {
532 string xml_log_level_s = resolveVars(arg, sess, sc_sess, event_params);
533 if (xml_log_level_s == "error")
534 xml_log_level = L_ERR;
535 else if (xml_log_level_s == "warn")
536 xml_log_level = L_WARN;
537 else if (xml_log_level_s == "info")
538 xml_log_level = L_INFO;
539 else if (xml_log_level_s == "debug")
540 xml_log_level = L_DBG;
541 else {
542 ERROR("script writer error: '%s' is no valid log level (error, warn, info, debug)\n",
543 xml_log_level_s.c_str());
544 }
545 } EXEC_ACTION_END;
546