1281681Srpaulo/*
2281681Srpaulo * Generic XML helper functions
3281681Srpaulo * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
4281681Srpaulo *
5281681Srpaulo * This software may be distributed under the terms of the BSD license.
6281681Srpaulo * See README for more details.
7281681Srpaulo */
8281681Srpaulo
9281681Srpaulo#include "includes.h"
10281681Srpaulo
11281681Srpaulo#include "common.h"
12281681Srpaulo#include "xml-utils.h"
13281681Srpaulo
14281681Srpaulo
15281681Srpaulostatic xml_node_t * get_node_uri_iter(struct xml_node_ctx *ctx,
16281681Srpaulo				      xml_node_t *root, char *uri)
17281681Srpaulo{
18281681Srpaulo	char *end;
19281681Srpaulo	xml_node_t *node;
20281681Srpaulo	const char *name;
21281681Srpaulo
22281681Srpaulo	end = strchr(uri, '/');
23281681Srpaulo	if (end)
24281681Srpaulo		*end++ = '\0';
25281681Srpaulo
26281681Srpaulo	node = root;
27281681Srpaulo	xml_node_for_each_sibling(ctx, node) {
28281681Srpaulo		xml_node_for_each_check(ctx, node);
29281681Srpaulo		name = xml_node_get_localname(ctx, node);
30281681Srpaulo		if (strcasecmp(name, uri) == 0)
31281681Srpaulo			break;
32281681Srpaulo	}
33281681Srpaulo
34281681Srpaulo	if (node == NULL)
35281681Srpaulo		return NULL;
36281681Srpaulo
37281681Srpaulo	if (end) {
38281681Srpaulo		return get_node_uri_iter(ctx, xml_node_first_child(ctx, node),
39281681Srpaulo					 end);
40281681Srpaulo	}
41281681Srpaulo
42281681Srpaulo	return node;
43281681Srpaulo}
44281681Srpaulo
45281681Srpaulo
46281681Srpauloxml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root,
47281681Srpaulo			  const char *uri)
48281681Srpaulo{
49281681Srpaulo	char *search;
50281681Srpaulo	xml_node_t *node;
51281681Srpaulo
52281681Srpaulo	search = os_strdup(uri);
53281681Srpaulo	if (search == NULL)
54281681Srpaulo		return NULL;
55281681Srpaulo
56281681Srpaulo	node = get_node_uri_iter(ctx, root, search);
57281681Srpaulo
58281681Srpaulo	os_free(search);
59281681Srpaulo	return node;
60281681Srpaulo}
61281681Srpaulo
62281681Srpaulo
63281681Srpaulostatic xml_node_t * get_node_iter(struct xml_node_ctx *ctx,
64281681Srpaulo				  xml_node_t *root, const char *path)
65281681Srpaulo{
66281681Srpaulo	char *end;
67281681Srpaulo	xml_node_t *node;
68281681Srpaulo	const char *name;
69281681Srpaulo
70281681Srpaulo	end = os_strchr(path, '/');
71281681Srpaulo	if (end)
72281681Srpaulo		*end++ = '\0';
73281681Srpaulo
74281681Srpaulo	xml_node_for_each_child(ctx, node, root) {
75281681Srpaulo		xml_node_for_each_check(ctx, node);
76281681Srpaulo		name = xml_node_get_localname(ctx, node);
77281681Srpaulo		if (os_strcasecmp(name, path) == 0)
78281681Srpaulo			break;
79281681Srpaulo	}
80281681Srpaulo
81281681Srpaulo	if (node == NULL)
82281681Srpaulo		return NULL;
83281681Srpaulo	if (end)
84281681Srpaulo		return get_node_iter(ctx, node, end);
85281681Srpaulo	return node;
86281681Srpaulo}
87281681Srpaulo
88281681Srpaulo
89281681Srpauloxml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root,
90281681Srpaulo		      const char *path)
91281681Srpaulo{
92281681Srpaulo	char *search;
93281681Srpaulo	xml_node_t *node;
94281681Srpaulo
95281681Srpaulo	search = os_strdup(path);
96281681Srpaulo	if (search == NULL)
97281681Srpaulo		return NULL;
98281681Srpaulo
99281681Srpaulo	node = get_node_iter(ctx, root, search);
100281681Srpaulo
101281681Srpaulo	os_free(search);
102281681Srpaulo	return node;
103281681Srpaulo}
104281681Srpaulo
105281681Srpaulo
106281681Srpauloxml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root,
107281681Srpaulo			    const char *path)
108281681Srpaulo{
109281681Srpaulo	xml_node_t *node;
110281681Srpaulo	xml_node_t *match;
111281681Srpaulo
112281681Srpaulo	xml_node_for_each_child(ctx, node, root) {
113281681Srpaulo		xml_node_for_each_check(ctx, node);
114281681Srpaulo		match = get_node(ctx, node, path);
115281681Srpaulo		if (match)
116281681Srpaulo			return match;
117281681Srpaulo	}
118281681Srpaulo
119281681Srpaulo	return NULL;
120281681Srpaulo}
121281681Srpaulo
122281681Srpaulo
123281681Srpauloxml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name)
124281681Srpaulo{
125281681Srpaulo	xml_node_t *node;
126281681Srpaulo	char *buf, *buf2, *start;
127281681Srpaulo	size_t len;
128281681Srpaulo
129281681Srpaulo	buf = os_readfile(name, &len);
130281681Srpaulo	if (buf == NULL)
131281681Srpaulo		return NULL;
132281681Srpaulo	buf2 = os_realloc(buf, len + 1);
133281681Srpaulo	if (buf2 == NULL) {
134281681Srpaulo		os_free(buf);
135281681Srpaulo		return NULL;
136281681Srpaulo	}
137281681Srpaulo	buf = buf2;
138281681Srpaulo	buf[len] = '\0';
139281681Srpaulo
140281681Srpaulo	start = os_strstr(buf, "<!DOCTYPE ");
141281681Srpaulo	if (start) {
142281681Srpaulo		char *pos = start + 1;
143281681Srpaulo		int count = 1;
144281681Srpaulo		while (*pos) {
145281681Srpaulo			if (*pos == '<')
146281681Srpaulo				count++;
147281681Srpaulo			else if (*pos == '>') {
148281681Srpaulo				count--;
149281681Srpaulo				if (count == 0) {
150281681Srpaulo					pos++;
151281681Srpaulo					break;
152281681Srpaulo				}
153281681Srpaulo			}
154281681Srpaulo			pos++;
155281681Srpaulo		}
156281681Srpaulo		if (count == 0) {
157281681Srpaulo			/* Remove DOCTYPE to allow the file to be parsed */
158281681Srpaulo			os_memset(start, ' ', pos - start);
159281681Srpaulo		}
160281681Srpaulo	}
161281681Srpaulo
162281681Srpaulo	node = xml_node_from_buf(ctx, buf);
163281681Srpaulo	os_free(buf);
164281681Srpaulo
165281681Srpaulo	return node;
166281681Srpaulo}
167281681Srpaulo
168281681Srpaulo
169281681Srpauloint node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node)
170281681Srpaulo{
171281681Srpaulo	FILE *f;
172281681Srpaulo	char *str;
173281681Srpaulo
174281681Srpaulo	str = xml_node_to_str(ctx, node);
175281681Srpaulo	if (str == NULL)
176281681Srpaulo		return -1;
177281681Srpaulo
178281681Srpaulo	f = fopen(fname, "w");
179281681Srpaulo	if (!f) {
180281681Srpaulo		os_free(str);
181281681Srpaulo		return -1;
182281681Srpaulo	}
183281681Srpaulo
184281681Srpaulo	fprintf(f, "%s\n", str);
185281681Srpaulo	os_free(str);
186281681Srpaulo	fclose(f);
187281681Srpaulo
188281681Srpaulo	return 0;
189281681Srpaulo}
190281681Srpaulo
191281681Srpaulo
192281681Srpaulostatic char * get_val(struct xml_node_ctx *ctx, xml_node_t *node)
193281681Srpaulo{
194281681Srpaulo	char *val, *pos;
195281681Srpaulo
196281681Srpaulo	val = xml_node_get_text(ctx, node);
197281681Srpaulo	if (val == NULL)
198281681Srpaulo		return NULL;
199281681Srpaulo	pos = val;
200281681Srpaulo	while (*pos) {
201281681Srpaulo		if (*pos != ' ' && *pos != '\t' && *pos != '\r' && *pos != '\n')
202281681Srpaulo			return val;
203281681Srpaulo		pos++;
204281681Srpaulo	}
205281681Srpaulo
206281681Srpaulo	return NULL;
207281681Srpaulo}
208281681Srpaulo
209281681Srpaulo
210281681Srpaulostatic char * add_path(const char *prev, const char *leaf)
211281681Srpaulo{
212281681Srpaulo	size_t len;
213281681Srpaulo	char *new_uri;
214281681Srpaulo
215281681Srpaulo	if (prev == NULL)
216281681Srpaulo		return NULL;
217281681Srpaulo
218281681Srpaulo	len = os_strlen(prev) + 1 + os_strlen(leaf) + 1;
219281681Srpaulo	new_uri = os_malloc(len);
220281681Srpaulo	if (new_uri)
221281681Srpaulo		os_snprintf(new_uri, len, "%s/%s", prev, leaf);
222281681Srpaulo
223281681Srpaulo	return new_uri;
224281681Srpaulo}
225281681Srpaulo
226281681Srpaulo
227281681Srpaulostatic void node_to_tnds(struct xml_node_ctx *ctx, xml_node_t *out,
228281681Srpaulo			 xml_node_t *in, const char *uri)
229281681Srpaulo{
230281681Srpaulo	xml_node_t *node;
231281681Srpaulo	xml_node_t *tnds;
232281681Srpaulo	const char *name;
233281681Srpaulo	char *val;
234281681Srpaulo	char *new_uri;
235281681Srpaulo
236281681Srpaulo	xml_node_for_each_child(ctx, node, in) {
237281681Srpaulo		xml_node_for_each_check(ctx, node);
238281681Srpaulo		name = xml_node_get_localname(ctx, node);
239281681Srpaulo
240281681Srpaulo		tnds = xml_node_create(ctx, out, NULL, "Node");
241281681Srpaulo		if (tnds == NULL)
242281681Srpaulo			return;
243281681Srpaulo		xml_node_create_text(ctx, tnds, NULL, "NodeName", name);
244281681Srpaulo
245281681Srpaulo		if (uri)
246281681Srpaulo			xml_node_create_text(ctx, tnds, NULL, "Path", uri);
247281681Srpaulo
248281681Srpaulo		val = get_val(ctx, node);
249281681Srpaulo		if (val) {
250281681Srpaulo			xml_node_create_text(ctx, tnds, NULL, "Value", val);
251281681Srpaulo			xml_node_get_text_free(ctx, val);
252281681Srpaulo		}
253281681Srpaulo
254281681Srpaulo		new_uri = add_path(uri, name);
255281681Srpaulo		node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri);
256281681Srpaulo		os_free(new_uri);
257281681Srpaulo	}
258281681Srpaulo}
259281681Srpaulo
260281681Srpaulo
261281681Srpaulostatic int add_ddfname(struct xml_node_ctx *ctx, xml_node_t *parent,
262281681Srpaulo		       const char *urn)
263281681Srpaulo{
264281681Srpaulo	xml_node_t *node;
265281681Srpaulo
266281681Srpaulo	node = xml_node_create(ctx, parent, NULL, "RTProperties");
267281681Srpaulo	if (node == NULL)
268281681Srpaulo		return -1;
269281681Srpaulo	node = xml_node_create(ctx, node, NULL, "Type");
270281681Srpaulo	if (node == NULL)
271281681Srpaulo		return -1;
272281681Srpaulo	xml_node_create_text(ctx, node, NULL, "DDFName", urn);
273281681Srpaulo	return 0;
274281681Srpaulo}
275281681Srpaulo
276281681Srpaulo
277281681Srpauloxml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo,
278281681Srpaulo			int use_path, const char *urn, const char *ns_uri)
279281681Srpaulo{
280281681Srpaulo	xml_node_t *root;
281281681Srpaulo	xml_node_t *node;
282281681Srpaulo	const char *name;
283281681Srpaulo
284281681Srpaulo	root = xml_node_create_root(ctx, ns_uri, NULL, NULL, "MgmtTree");
285281681Srpaulo	if (root == NULL)
286281681Srpaulo		return NULL;
287281681Srpaulo
288281681Srpaulo	xml_node_create_text(ctx, root, NULL, "VerDTD", "1.2");
289281681Srpaulo
290281681Srpaulo	name = xml_node_get_localname(ctx, mo);
291281681Srpaulo
292281681Srpaulo	node = xml_node_create(ctx, root, NULL, "Node");
293281681Srpaulo	if (node == NULL)
294281681Srpaulo		goto fail;
295281681Srpaulo	xml_node_create_text(ctx, node, NULL, "NodeName", name);
296281681Srpaulo	if (urn)
297281681Srpaulo		add_ddfname(ctx, node, urn);
298281681Srpaulo
299281681Srpaulo	node_to_tnds(ctx, use_path ? root : node, mo, use_path ? name : NULL);
300281681Srpaulo
301281681Srpaulo	return root;
302281681Srpaulo
303281681Srpaulofail:
304281681Srpaulo	xml_node_free(ctx, root);
305281681Srpaulo	return NULL;
306281681Srpaulo}
307281681Srpaulo
308281681Srpaulo
309281681Srpaulostatic xml_node_t * get_first_child_node(struct xml_node_ctx *ctx,
310281681Srpaulo					 xml_node_t *node,
311281681Srpaulo					 const char *name)
312281681Srpaulo{
313281681Srpaulo	const char *lname;
314281681Srpaulo	xml_node_t *child;
315281681Srpaulo
316281681Srpaulo	xml_node_for_each_child(ctx, child, node) {
317281681Srpaulo		xml_node_for_each_check(ctx, child);
318281681Srpaulo		lname = xml_node_get_localname(ctx, child);
319281681Srpaulo		if (os_strcasecmp(lname, name) == 0)
320281681Srpaulo			return child;
321281681Srpaulo	}
322281681Srpaulo
323281681Srpaulo	return NULL;
324281681Srpaulo}
325281681Srpaulo
326281681Srpaulo
327281681Srpaulostatic char * get_node_text(struct xml_node_ctx *ctx, xml_node_t *node,
328281681Srpaulo			    const char *node_name)
329281681Srpaulo{
330281681Srpaulo	node = get_first_child_node(ctx, node, node_name);
331281681Srpaulo	if (node == NULL)
332281681Srpaulo		return NULL;
333281681Srpaulo	return xml_node_get_text(ctx, node);
334281681Srpaulo}
335281681Srpaulo
336281681Srpaulo
337281681Srpaulostatic xml_node_t * add_mo_node(struct xml_node_ctx *ctx, xml_node_t *root,
338281681Srpaulo				xml_node_t *node, const char *uri)
339281681Srpaulo{
340281681Srpaulo	char *nodename, *value, *path;
341281681Srpaulo	xml_node_t *parent;
342281681Srpaulo
343281681Srpaulo	nodename = get_node_text(ctx, node, "NodeName");
344281681Srpaulo	if (nodename == NULL)
345281681Srpaulo		return NULL;
346281681Srpaulo	value = get_node_text(ctx, node, "Value");
347281681Srpaulo
348281681Srpaulo	if (root == NULL) {
349281681Srpaulo		root = xml_node_create_root(ctx, NULL, NULL, NULL,
350281681Srpaulo					    nodename);
351281681Srpaulo		if (root && value)
352281681Srpaulo			xml_node_set_text(ctx, root, value);
353281681Srpaulo	} else {
354281681Srpaulo		if (uri == NULL) {
355281681Srpaulo			xml_node_get_text_free(ctx, nodename);
356281681Srpaulo			xml_node_get_text_free(ctx, value);
357281681Srpaulo			return NULL;
358281681Srpaulo		}
359281681Srpaulo		path = get_node_text(ctx, node, "Path");
360281681Srpaulo		if (path)
361281681Srpaulo			uri = path;
362281681Srpaulo		parent = get_node_uri(ctx, root, uri);
363281681Srpaulo		xml_node_get_text_free(ctx, path);
364281681Srpaulo		if (parent == NULL) {
365281681Srpaulo			printf("Could not find URI '%s'\n", uri);
366281681Srpaulo			xml_node_get_text_free(ctx, nodename);
367281681Srpaulo			xml_node_get_text_free(ctx, value);
368281681Srpaulo			return NULL;
369281681Srpaulo		}
370281681Srpaulo		if (value)
371281681Srpaulo			xml_node_create_text(ctx, parent, NULL, nodename,
372281681Srpaulo					     value);
373281681Srpaulo		else
374281681Srpaulo			xml_node_create(ctx, parent, NULL, nodename);
375281681Srpaulo	}
376281681Srpaulo
377281681Srpaulo	xml_node_get_text_free(ctx, nodename);
378281681Srpaulo	xml_node_get_text_free(ctx, value);
379281681Srpaulo
380281681Srpaulo	return root;
381281681Srpaulo}
382281681Srpaulo
383281681Srpaulo
384281681Srpaulostatic xml_node_t * tnds_to_mo_iter(struct xml_node_ctx *ctx, xml_node_t *root,
385281681Srpaulo				    xml_node_t *node, const char *uri)
386281681Srpaulo{
387281681Srpaulo	xml_node_t *child;
388281681Srpaulo	const char *name;
389281681Srpaulo	char *nodename;
390281681Srpaulo
391281681Srpaulo	xml_node_for_each_sibling(ctx, node) {
392281681Srpaulo		xml_node_for_each_check(ctx, node);
393281681Srpaulo
394281681Srpaulo		nodename = get_node_text(ctx, node, "NodeName");
395281681Srpaulo		if (nodename == NULL)
396281681Srpaulo			return NULL;
397281681Srpaulo
398281681Srpaulo		name = xml_node_get_localname(ctx, node);
399281681Srpaulo		if (strcmp(name, "Node") == 0) {
400281681Srpaulo			if (root && !uri) {
401281681Srpaulo				printf("Invalid TNDS tree structure - "
402281681Srpaulo				       "multiple top level nodes\n");
403281681Srpaulo				xml_node_get_text_free(ctx, nodename);
404281681Srpaulo				return NULL;
405281681Srpaulo			}
406281681Srpaulo			root = add_mo_node(ctx, root, node, uri);
407281681Srpaulo		}
408281681Srpaulo
409281681Srpaulo		child = get_first_child_node(ctx, node, "Node");
410281681Srpaulo		if (child) {
411281681Srpaulo			if (uri == NULL)
412281681Srpaulo				tnds_to_mo_iter(ctx, root, child, nodename);
413281681Srpaulo			else {
414281681Srpaulo				char *new_uri;
415281681Srpaulo				new_uri = add_path(uri, nodename);
416281681Srpaulo				tnds_to_mo_iter(ctx, root, child, new_uri);
417281681Srpaulo				os_free(new_uri);
418281681Srpaulo			}
419281681Srpaulo		}
420281681Srpaulo		xml_node_get_text_free(ctx, nodename);
421281681Srpaulo	}
422281681Srpaulo
423281681Srpaulo	return root;
424281681Srpaulo}
425281681Srpaulo
426281681Srpaulo
427281681Srpauloxml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds)
428281681Srpaulo{
429281681Srpaulo	const char *name;
430281681Srpaulo	xml_node_t *node;
431281681Srpaulo
432281681Srpaulo	name = xml_node_get_localname(ctx, tnds);
433281681Srpaulo	if (name == NULL || os_strcmp(name, "MgmtTree") != 0)
434281681Srpaulo		return NULL;
435281681Srpaulo
436281681Srpaulo	node = get_first_child_node(ctx, tnds, "Node");
437281681Srpaulo	if (!node)
438281681Srpaulo		return NULL;
439281681Srpaulo	return tnds_to_mo_iter(ctx, NULL, node, NULL);
440281681Srpaulo}
441281681Srpaulo
442281681Srpaulo
443281681Srpauloxml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node)
444281681Srpaulo{
445281681Srpaulo	xml_node_t *envelope, *body;
446281681Srpaulo	xml_namespace_t *ns;
447281681Srpaulo
448281681Srpaulo	envelope = xml_node_create_root(
449281681Srpaulo		ctx, "http://www.w3.org/2003/05/soap-envelope", "soap12", &ns,
450281681Srpaulo		"Envelope");
451281681Srpaulo	if (envelope == NULL)
452281681Srpaulo		return NULL;
453281681Srpaulo	body = xml_node_create(ctx, envelope, ns, "Body");
454281681Srpaulo	xml_node_add_child(ctx, body, node);
455281681Srpaulo	return envelope;
456281681Srpaulo}
457281681Srpaulo
458281681Srpaulo
459281681Srpauloxml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap)
460281681Srpaulo{
461281681Srpaulo	xml_node_t *body, *child;
462281681Srpaulo
463281681Srpaulo	body = get_node_uri(ctx, soap, "Envelope/Body");
464281681Srpaulo	if (body == NULL)
465281681Srpaulo		return NULL;
466281681Srpaulo	xml_node_for_each_child(ctx, child, body) {
467281681Srpaulo		xml_node_for_each_check(ctx, child);
468281681Srpaulo		return child;
469281681Srpaulo	}
470281681Srpaulo	return NULL;
471281681Srpaulo}
472