1/* 2 * XML wrapper for libxml2 3 * Copyright (c) 2012-2013, Qualcomm Atheros, Inc. 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9#include "includes.h" 10#define LIBXML_VALID_ENABLED 11#include <libxml/tree.h> 12#include <libxml/xmlschemastypes.h> 13 14#include "common.h" 15#include "base64.h" 16#include "xml-utils.h" 17 18 19struct xml_node_ctx { 20 void *ctx; 21}; 22 23 24struct str_buf { 25 char *buf; 26 size_t len; 27}; 28 29#define MAX_STR 1000 30 31static void add_str(void *ctx_ptr, const char *fmt, ...) 32{ 33 struct str_buf *str = ctx_ptr; 34 va_list ap; 35 char *n; 36 int len; 37 38 n = os_realloc(str->buf, str->len + MAX_STR + 2); 39 if (n == NULL) 40 return; 41 str->buf = n; 42 43 va_start(ap, fmt); 44 len = vsnprintf(str->buf + str->len, MAX_STR, fmt, ap); 45 va_end(ap); 46 if (len >= MAX_STR) 47 len = MAX_STR - 1; 48 str->len += len; 49 str->buf[str->len] = '\0'; 50} 51 52 53int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node, 54 const char *xml_schema_fname, char **ret_err) 55{ 56 xmlDocPtr doc; 57 xmlNodePtr n; 58 xmlSchemaParserCtxtPtr pctx; 59 xmlSchemaValidCtxtPtr vctx; 60 xmlSchemaPtr schema; 61 int ret; 62 struct str_buf errors; 63 64 if (ret_err) 65 *ret_err = NULL; 66 67 doc = xmlNewDoc((xmlChar *) "1.0"); 68 if (doc == NULL) 69 return -1; 70 n = xmlDocCopyNode((xmlNodePtr) node, doc, 1); 71 if (n == NULL) { 72 xmlFreeDoc(doc); 73 return -1; 74 } 75 xmlDocSetRootElement(doc, n); 76 77 os_memset(&errors, 0, sizeof(errors)); 78 79 pctx = xmlSchemaNewParserCtxt(xml_schema_fname); 80 xmlSchemaSetParserErrors(pctx, (xmlSchemaValidityErrorFunc) add_str, 81 (xmlSchemaValidityWarningFunc) add_str, 82 &errors); 83 schema = xmlSchemaParse(pctx); 84 xmlSchemaFreeParserCtxt(pctx); 85 86 vctx = xmlSchemaNewValidCtxt(schema); 87 xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc) add_str, 88 (xmlSchemaValidityWarningFunc) add_str, 89 &errors); 90 91 ret = xmlSchemaValidateDoc(vctx, doc); 92 xmlSchemaFreeValidCtxt(vctx); 93 xmlFreeDoc(doc); 94 xmlSchemaFree(schema); 95 96 if (ret == 0) { 97 os_free(errors.buf); 98 return 0; 99 } else if (ret > 0) { 100 if (ret_err) 101 *ret_err = errors.buf; 102 else 103 os_free(errors.buf); 104 return -1; 105 } else { 106 if (ret_err) 107 *ret_err = errors.buf; 108 else 109 os_free(errors.buf); 110 return -1; 111 } 112} 113 114 115int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node, 116 const char *dtd_fname, char **ret_err) 117{ 118 xmlDocPtr doc; 119 xmlNodePtr n; 120 xmlValidCtxt vctx; 121 xmlDtdPtr dtd; 122 int ret; 123 struct str_buf errors; 124 125 if (ret_err) 126 *ret_err = NULL; 127 128 doc = xmlNewDoc((xmlChar *) "1.0"); 129 if (doc == NULL) 130 return -1; 131 n = xmlDocCopyNode((xmlNodePtr) node, doc, 1); 132 if (n == NULL) { 133 xmlFreeDoc(doc); 134 return -1; 135 } 136 xmlDocSetRootElement(doc, n); 137 138 os_memset(&errors, 0, sizeof(errors)); 139 140 dtd = xmlParseDTD(NULL, (const xmlChar *) dtd_fname); 141 if (dtd == NULL) { 142 xmlFreeDoc(doc); 143 return -1; 144 } 145 146 os_memset(&vctx, 0, sizeof(vctx)); 147 vctx.userData = &errors; 148 vctx.error = add_str; 149 vctx.warning = add_str; 150 ret = xmlValidateDtd(&vctx, doc, dtd); 151 xmlFreeDoc(doc); 152 xmlFreeDtd(dtd); 153 154 if (ret == 1) { 155 os_free(errors.buf); 156 return 0; 157 } else { 158 if (ret_err) 159 *ret_err = errors.buf; 160 else 161 os_free(errors.buf); 162 return -1; 163 } 164} 165 166 167void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node) 168{ 169 xmlFreeNode((xmlNodePtr) node); 170} 171 172 173xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node) 174{ 175 return (xml_node_t *) ((xmlNodePtr) node)->parent; 176} 177 178 179xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf) 180{ 181 xmlDocPtr doc; 182 xmlNodePtr node; 183 184 doc = xmlParseMemory(buf, strlen(buf)); 185 if (doc == NULL) 186 return NULL; 187 node = xmlDocGetRootElement(doc); 188 node = xmlCopyNode(node, 1); 189 xmlFreeDoc(doc); 190 191 return (xml_node_t *) node; 192} 193 194 195const char * xml_node_get_localname(struct xml_node_ctx *ctx, 196 xml_node_t *node) 197{ 198 return (const char *) ((xmlNodePtr) node)->name; 199} 200 201 202char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node) 203{ 204 xmlChar *buf; 205 int bufsiz; 206 char *ret, *pos; 207 xmlNodePtr n = (xmlNodePtr) node; 208 xmlDocPtr doc; 209 210 doc = xmlNewDoc((xmlChar *) "1.0"); 211 n = xmlDocCopyNode(n, doc, 1); 212 xmlDocSetRootElement(doc, n); 213 xmlDocDumpFormatMemory(doc, &buf, &bufsiz, 0); 214 xmlFreeDoc(doc); 215 if (!buf) 216 return NULL; 217 pos = (char *) buf; 218 if (strncmp(pos, "<?xml", 5) == 0) { 219 pos = strchr(pos, '>'); 220 if (pos) 221 pos++; 222 while (pos && (*pos == '\r' || *pos == '\n')) 223 pos++; 224 } 225 if (pos) 226 ret = os_strdup(pos); 227 else 228 ret = NULL; 229 xmlFree(buf); 230 231 if (ret) { 232 pos = ret; 233 if (pos[0]) { 234 while (pos[1]) 235 pos++; 236 } 237 while (pos >= ret && *pos == '\n') 238 *pos-- = '\0'; 239 } 240 241 return ret; 242} 243 244 245void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node) 246{ 247 xmlUnlinkNode((xmlNodePtr) node); 248} 249 250 251void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent, 252 xml_node_t *child) 253{ 254 xmlAddChild((xmlNodePtr) parent, (xmlNodePtr) child); 255} 256 257 258xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri, 259 const char *ns_prefix, 260 xml_namespace_t **ret_ns, const char *name) 261{ 262 xmlNodePtr node; 263 xmlNsPtr ns = NULL; 264 265 node = xmlNewNode(NULL, (const xmlChar *) name); 266 if (node == NULL) 267 return NULL; 268 if (ns_uri) { 269 ns = xmlNewNs(node, (const xmlChar *) ns_uri, 270 (const xmlChar *) ns_prefix); 271 xmlSetNs(node, ns); 272 } 273 274 if (ret_ns) 275 *ret_ns = (xml_namespace_t *) ns; 276 277 return (xml_node_t *) node; 278} 279 280 281xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent, 282 xml_namespace_t *ns, const char *name) 283{ 284 xmlNodePtr node; 285 node = xmlNewChild((xmlNodePtr) parent, (xmlNsPtr) ns, 286 (const xmlChar *) name, NULL); 287 return (xml_node_t *) node; 288} 289 290 291xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx, 292 xml_node_t *parent, xml_namespace_t *ns, 293 const char *name, const char *value) 294{ 295 xmlNodePtr node; 296 node = xmlNewTextChild((xmlNodePtr) parent, (xmlNsPtr) ns, 297 (const xmlChar *) name, (const xmlChar *) value); 298 return (xml_node_t *) node; 299} 300 301 302xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx, 303 xml_node_t *parent, const char *ns_uri, 304 const char *name, const char *value) 305{ 306 xmlNodePtr node; 307 xmlNsPtr ns; 308 309 node = xmlNewTextChild((xmlNodePtr) parent, NULL, 310 (const xmlChar *) name, (const xmlChar *) value); 311 ns = xmlNewNs(node, (const xmlChar *) ns_uri, NULL); 312 xmlSetNs(node, ns); 313 return (xml_node_t *) node; 314} 315 316 317void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node, 318 const char *value) 319{ 320 /* TODO: escape XML special chars in value */ 321 xmlNodeSetContent((xmlNodePtr) node, (xmlChar *) value); 322} 323 324 325int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node, 326 xml_namespace_t *ns, const char *name, const char *value) 327{ 328 xmlAttrPtr attr; 329 330 if (ns) { 331 attr = xmlNewNsProp((xmlNodePtr) node, (xmlNsPtr) ns, 332 (const xmlChar *) name, 333 (const xmlChar *) value); 334 } else { 335 attr = xmlNewProp((xmlNodePtr) node, (const xmlChar *) name, 336 (const xmlChar *) value); 337 } 338 339 return attr ? 0 : -1; 340} 341 342 343char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node, 344 char *name) 345{ 346 return (char *) xmlGetNoNsProp((xmlNodePtr) node, 347 (const xmlChar *) name); 348} 349 350 351char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node, 352 const char *ns_uri, char *name) 353{ 354 return (char *) xmlGetNsProp((xmlNodePtr) node, (const xmlChar *) name, 355 (const xmlChar *) ns_uri); 356} 357 358 359void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val) 360{ 361 if (val) 362 xmlFree((xmlChar *) val); 363} 364 365 366xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx, 367 xml_node_t *parent) 368{ 369 return (xml_node_t *) ((xmlNodePtr) parent)->children; 370} 371 372 373xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx, 374 xml_node_t *node) 375{ 376 return (xml_node_t *) ((xmlNodePtr) node)->next; 377} 378 379 380int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node) 381{ 382 return ((xmlNodePtr) node)->type == XML_ELEMENT_NODE; 383} 384 385 386char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node) 387{ 388 if (xmlChildElementCount((xmlNodePtr) node) > 0) 389 return NULL; 390 return (char *) xmlNodeGetContent((xmlNodePtr) node); 391} 392 393 394void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val) 395{ 396 if (val) 397 xmlFree((xmlChar *) val); 398} 399 400 401char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node, 402 int *ret_len) 403{ 404 char *txt; 405 unsigned char *ret; 406 size_t len; 407 408 txt = xml_node_get_text(ctx, node); 409 if (txt == NULL) 410 return NULL; 411 412 ret = base64_decode((unsigned char *) txt, strlen(txt), &len); 413 if (ret_len) 414 *ret_len = len; 415 xml_node_get_text_free(ctx, txt); 416 if (ret == NULL) 417 return NULL; 418 txt = os_malloc(len + 1); 419 if (txt == NULL) { 420 os_free(ret); 421 return NULL; 422 } 423 os_memcpy(txt, ret, len); 424 txt[len] = '\0'; 425 return txt; 426} 427 428 429xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node) 430{ 431 if (node == NULL) 432 return NULL; 433 return (xml_node_t *) xmlCopyNode((xmlNodePtr) node, 1); 434} 435 436 437struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx, 438 const void *env) 439{ 440 struct xml_node_ctx *xctx; 441 442 xctx = os_zalloc(sizeof(*xctx)); 443 if (xctx == NULL) 444 return NULL; 445 xctx->ctx = upper_ctx; 446 447 LIBXML_TEST_VERSION 448 449 return xctx; 450} 451 452 453void xml_node_deinit_ctx(struct xml_node_ctx *ctx) 454{ 455 xmlSchemaCleanupTypes(); 456 xmlCleanupParser(); 457 xmlMemoryDump(); 458 os_free(ctx); 459} 460