1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  */
18 
19 #include "evolution-data-server-config.h"
20 
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 
25 #ifndef O_BINARY
26 #define O_BINARY 0
27 #endif
28 
29 #include <libxml/parser.h>
30 #include <libxml/catalog.h>
31 #include <libxml/tree.h>
32 #include <libxml/xpathInternals.h>
33 
34 #include <glib/gstdio.h>
35 
36 #include "e-xml-utils.h"
37 
38 #ifdef G_OS_WIN32
39 #define fsync(fd) 0
40 #endif
41 
42 /**
43  * e_xml_initialize_in_main: (skip)
44  *
45  * Initializes libxml library global memory. This should be called
46  * in the main thread. The function does nothing, when it had been
47  * called already.
48  *
49  * Since: 3.28
50  **/
51 void
e_xml_initialize_in_main(void)52 e_xml_initialize_in_main (void)
53 {
54 	static volatile guint called = 0;
55 
56 	if (!g_atomic_int_or (&called, 1)) {
57 		xmlInitMemory ();
58 		xmlInitThreads ();
59 		xmlInitGlobals ();
60 		xmlInitializeCatalog ();
61 		xmlInitParser ();
62 	}
63 }
64 
65 /**
66  * e_xml_parse_file: (skip)
67  * @filename: path to an XML file
68  *
69  * Reads a local XML file and parses the contents into an XML document
70  * structure.  If the XML file cannot be read or its contents are malformed,
71  * the function returns %NULL.
72  *
73  * Returns: (transfer full): an XML document structure, or %NULL
74  **/
75 xmlDoc *
e_xml_parse_file(const gchar * filename)76 e_xml_parse_file (const gchar *filename)
77 {
78 	xmlDocPtr result = NULL;
79 
80 	GMappedFile *mapped_file;
81 
82 	mapped_file = g_mapped_file_new (filename, FALSE, NULL);
83 	if (mapped_file) {
84 		result = xmlParseMemory (
85 			g_mapped_file_get_contents (mapped_file),
86 			g_mapped_file_get_length (mapped_file));
87 		g_mapped_file_unref (mapped_file);
88 	}
89 
90 	return result;
91 }
92 
93 /**
94  * e_xml_save_file:
95  * @filename: path to a file to save to
96  * @doc: an XML document structure
97  *
98  * Writes the given XML document structure to the file given by @filename.
99  * If an error occurs while saving, the function returns -1 and sets errno.
100  *
101  * Returns: 0 on success, -1 on failure
102  **/
103 gint
e_xml_save_file(const gchar * filename,xmlDoc * doc)104 e_xml_save_file (const gchar *filename,
105                  xmlDoc *doc)
106 {
107 	gchar *filesave;
108 	xmlChar *xmlbuf;
109 	gsize n, written = 0;
110 	gint ret, fd, size;
111 	gint errnosave;
112 	gssize w;
113 	gchar *dirname = g_path_get_dirname (filename);
114 	gchar *basename = g_path_get_basename (filename);
115 	gchar *savebasename = g_strconcat (".#", basename, NULL);
116 
117 	g_free (basename);
118 	filesave = g_build_filename (dirname, savebasename, NULL);
119 	g_free (savebasename);
120 	g_free (dirname);
121 
122 	fd = g_open (filesave, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
123 	if (fd == -1) {
124 		g_free (filesave);
125 		return -1;
126 	}
127 
128 	xmlDocDumpFormatMemory (doc, &xmlbuf, &size, TRUE);
129 	if (size <= 0) {
130 		close (fd);
131 		g_unlink (filesave);
132 		g_free (filesave);
133 		errno = ENOMEM;
134 		return -1;
135 	}
136 
137 	n = (gsize) size;
138 	do {
139 		do {
140 			w = write (fd, xmlbuf + written, n - written);
141 		} while (w == -1 && errno == EINTR);
142 
143 		if (w > 0)
144 			written += w;
145 	} while (w != -1 && written < n);
146 
147 	xmlFree (xmlbuf);
148 
149 	if (written < n || fsync (fd) == -1) {
150 		errnosave = errno;
151 		close (fd);
152 		g_unlink (filesave);
153 		g_free (filesave);
154 		errno = errnosave;
155 		return -1;
156 	}
157 
158 	while ((ret = close (fd)) == -1 && errno == EINTR)
159 		;
160 
161 	if (ret == -1) {
162 		g_free (filesave);
163 		return -1;
164 	}
165 
166 	if (g_rename (filesave, filename) == -1) {
167 		errnosave = errno;
168 		g_unlink (filesave);
169 		g_free (filesave);
170 		errno = errnosave;
171 		return -1;
172 	}
173 	g_free (filesave);
174 
175 	return 0;
176 }
177 
178 /**
179  * e_xml_get_child_by_name: (skip)
180  * @parent: an XML node structure
181  * @child_name: element name of a child node
182  *
183  * Attempts to find a child element of @parent named @child_name.
184  * If no such child exists, the function returns %NULL.
185  *
186  * Returns: (nullable): a child XML node structure, or %NULL
187  **/
188 xmlNode *
e_xml_get_child_by_name(const xmlNode * parent,const xmlChar * child_name)189 e_xml_get_child_by_name (const xmlNode *parent,
190                          const xmlChar *child_name)
191 {
192 	xmlNode *child;
193 
194 	g_return_val_if_fail (parent != NULL, NULL);
195 	g_return_val_if_fail (child_name != NULL, NULL);
196 
197 	for (child = parent->xmlChildrenNode; child != NULL; child = child->next) {
198 		if (xmlStrcmp (child->name, child_name) == 0) {
199 			return child;
200 		}
201 	}
202 	return NULL;
203 }
204 
205 /**
206  * e_xml_parse_data: (skip)
207  * @data: (array length=length) (element-type guint8): an XML data
208  * @length: length of data, should be greated than zero
209  *
210  * Parses XML data into an #xmlDocPtr. Free returned pointer
211  * with xmlFreeDoc(), when no longer needed.
212  *
213  * Returns: (nullable) (transfer full): a new #xmlDocPtr with parsed @data,
214  *    or %NULL on error.
215  *
216  * Since: 3.26
217  **/
218 xmlDoc *
e_xml_parse_data(gconstpointer data,gsize length)219 e_xml_parse_data (gconstpointer data,
220 		  gsize length)
221 {
222 	g_return_val_if_fail (data != NULL, NULL);
223 	g_return_val_if_fail (length > 0, NULL);
224 
225 	return xmlReadMemory (data, length, "data.xml", NULL, XML_PARSE_NOWARNING | XML_PARSE_RECOVER);
226 }
227 
228 /**
229  * e_xml_new_xpath_context_with_namespaces: (skip)
230  * @doc: an #xmlDocPtr
231  * @...: %NULL-terminated list of pairs (prefix, href) with namespaces
232  *
233  * Creates a new #xmlXPathContextPtr on @doc with preregistered
234  * namespaces. The namepsaces are pair of (prefix, href), terminated
235  * by %NULL.
236  *
237  * Returns: (transfer full): a new #xmlXPathContextPtr. Free the returned
238  *    pointer with xmlXPathFreeContext() when no longer needed.
239  *
240  * Since: 3.26
241  **/
242 xmlXPathContext *
e_xml_new_xpath_context_with_namespaces(xmlDoc * doc,...)243 e_xml_new_xpath_context_with_namespaces (xmlDoc *doc,
244 					 ...)
245 {
246 	xmlXPathContextPtr xpath_ctx;
247 	va_list va;
248 	const gchar *prefix;
249 
250 	g_return_val_if_fail (doc != NULL, NULL);
251 
252 	xpath_ctx = xmlXPathNewContext (doc);
253 	g_return_val_if_fail (xpath_ctx != NULL, NULL);
254 
255 	va_start (va, doc);
256 
257 	while (prefix = va_arg (va, const gchar *), prefix) {
258 		const gchar *href = va_arg (va, const gchar *);
259 
260 		if (!href) {
261 			g_warn_if_fail (href != NULL);
262 			break;
263 		}
264 
265 		xmlXPathRegisterNs (xpath_ctx, (const xmlChar *) prefix, (const xmlChar *) href);
266 	}
267 
268 	va_end (va);
269 
270 	return xpath_ctx;
271 }
272 
273 /**
274  * e_xml_xpath_context_register_namespaces: (skip)
275  * @xpath_ctx: an #xmlXPathContextPtr
276  * @prefix: namespace prefix
277  * @href: namespace href
278  * @...: %NULL-terminated list of pairs (prefix, href) with additional namespaces
279  *
280  * Registers one or more additional namespaces. It's a caller's error
281  * to try to register a namespace with the same prefix again, unless
282  * the prefix uses the same namespace href.
283  *
284  * Since: 3.26
285  **/
286 void
e_xml_xpath_context_register_namespaces(xmlXPathContext * xpath_ctx,const gchar * prefix,const gchar * href,...)287 e_xml_xpath_context_register_namespaces (xmlXPathContext *xpath_ctx,
288 					 const gchar *prefix,
289 					 const gchar *href,
290 					 ...)
291 {
292 	va_list va;
293 	const gchar *used_href;
294 
295 	g_return_if_fail (xpath_ctx != NULL);
296 	g_return_if_fail (prefix != NULL);
297 	g_return_if_fail (href != NULL);
298 
299 	used_href = (const gchar *) xmlXPathNsLookup (xpath_ctx, (const xmlChar *) prefix);
300 	if (used_href && g_strcmp0 (used_href, href) != 0) {
301 		g_warning ("%s: Trying to register prefix '%s' with href '%s', but it already points to '%s'",
302 			G_STRFUNC, prefix, href, used_href);
303 	} else if (!used_href) {
304 		xmlXPathRegisterNs (xpath_ctx, (const xmlChar *) prefix, (const xmlChar *) href);
305 	}
306 
307 	va_start (va, href);
308 
309 	while (prefix = va_arg (va, const gchar *), prefix) {
310 		href = va_arg (va, const gchar *);
311 
312 		if (!href) {
313 			g_warn_if_fail (href != NULL);
314 			break;
315 		}
316 
317 		used_href = (const gchar *) xmlXPathNsLookup (xpath_ctx, (const xmlChar *) prefix);
318 		if (used_href && g_strcmp0 (used_href, href) != 0) {
319 			g_warning ("%s: Trying to register prefix '%s' with href '%s', but it already points to '%s'",
320 				G_STRFUNC, prefix, href, used_href);
321 		} else if (!used_href) {
322 			xmlXPathRegisterNs (xpath_ctx, (const xmlChar *) prefix, (const xmlChar *) href);
323 		}
324 	}
325 
326 	va_end (va);
327 }
328 
329 /**
330  * e_xml_xpath_eval: (skip)
331  * @xpath_ctx: an #xmlXPathContextPtr
332  * @format: printf-like format specifier of path to evaluate
333  * @...: arguments for the @format
334  *
335  * Evaluates path specified by @format and returns its #xmlXPathObjectPtr,
336  * in case the path evaluates to a non-empty node set. See also
337  * e_xml_xpath_eval_as_string() which evaluates the path to string.
338  *
339  * Returns: (nullable) (transfer full): a new #xmlXPathObjectPtr which
340  *    references given path, or %NULL if path cannot be found or when
341  *    it evaluates to an empty nodeset. Free returned pointer with
342  *    xmlXPathFreeObject(), when no longer needed.
343  *
344  * Since: 3.26
345  **/
346 xmlXPathObject *
e_xml_xpath_eval(xmlXPathContext * xpath_ctx,const gchar * format,...)347 e_xml_xpath_eval (xmlXPathContext *xpath_ctx,
348 		  const gchar *format,
349 		  ...)
350 {
351 	xmlXPathObjectPtr object;
352 	va_list va;
353 	gchar *expr;
354 
355 	g_return_val_if_fail (xpath_ctx != NULL, NULL);
356 	g_return_val_if_fail (format != NULL, NULL);
357 
358 	va_start (va, format);
359 	expr = g_strdup_vprintf (format, va);
360 	va_end (va);
361 
362 	object = xmlXPathEvalExpression ((const xmlChar *) expr, xpath_ctx);
363 	g_free (expr);
364 
365 	if (!object)
366 		return NULL;
367 
368 	if (object->type == XPATH_NODESET &&
369 	    xmlXPathNodeSetIsEmpty (object->nodesetval)) {
370 		xmlXPathFreeObject (object);
371 		return NULL;
372 	}
373 
374 	return object;
375 }
376 
377 /**
378  * e_xml_xpath_eval_as_string: (skip)
379  * @xpath_ctx: an #xmlXPathContextPtr
380  * @format: printf-like format specifier of path to evaluate
381  * @...: arguments for the @format
382  *
383  * Evaluates path specified by @format and returns its result as string,
384  * in case the path evaluates to a non-empty node set. See also
385  * e_xml_xpath_eval() which evaluates the path to an #xmlXPathObjectPtr.
386  *
387  * Returns: (nullable) (transfer full): a new string which contains value
388  *    of the given path, or %NULL if path cannot be found or when
389  *    it evaluates to an empty nodeset. Free returned pointer with
390  *    g_free(), when no longer needed.
391  *
392  * Since: 3.26
393  **/
394 gchar *
e_xml_xpath_eval_as_string(xmlXPathContext * xpath_ctx,const gchar * format,...)395 e_xml_xpath_eval_as_string (xmlXPathContext *xpath_ctx,
396 			    const gchar *format,
397 			    ...)
398 {
399 	xmlXPathObjectPtr object;
400 	va_list va;
401 	gchar *expr, *value;
402 
403 	g_return_val_if_fail (xpath_ctx != NULL, NULL);
404 	g_return_val_if_fail (format != NULL, NULL);
405 
406 	va_start (va, format);
407 	expr = g_strdup_vprintf (format, va);
408 	va_end (va);
409 
410 	if (!g_str_has_prefix (format, "string(")) {
411 		gchar *tmp = expr;
412 
413 		expr = g_strconcat ("string(", expr, ")", NULL);
414 
415 		g_free (tmp);
416 	}
417 
418 	object = e_xml_xpath_eval (xpath_ctx, "%s", expr);
419 	g_free (expr);
420 
421 	if (!object)
422 		return NULL;
423 
424 	if (object->type == XPATH_STRING &&
425 	    *object->stringval)
426 		value = g_strdup ((const gchar *) object->stringval);
427 	else
428 		value = NULL;
429 
430 	xmlXPathFreeObject (object);
431 
432 	return value;
433 }
434 
435 /**
436  * e_xml_xpath_eval_exists: (skip)
437  * @xpath_ctx: an #xmlXPathContextPtr
438  * @format: printf-like format specifier of path to evaluate
439  * @...: arguments for the @format
440  *
441  * Evaluates path specified by @format and returns whether it exists.
442  *
443  * Returns: %TRUE, when the given XPath exists, %FALSE otherwise.
444  *
445  * Since: 3.26
446  **/
447 gboolean
e_xml_xpath_eval_exists(xmlXPathContext * xpath_ctx,const gchar * format,...)448 e_xml_xpath_eval_exists (xmlXPathContext *xpath_ctx,
449 			 const gchar *format,
450 			 ...)
451 {
452 	xmlXPathObjectPtr object;
453 	va_list va;
454 	gchar *expr;
455 
456 	g_return_val_if_fail (xpath_ctx != NULL, FALSE);
457 	g_return_val_if_fail (format != NULL, FALSE);
458 
459 	va_start (va, format);
460 	expr = g_strdup_vprintf (format, va);
461 	va_end (va);
462 
463 	object = e_xml_xpath_eval (xpath_ctx, "%s", expr);
464 	g_free (expr);
465 
466 	if (!object)
467 		return FALSE;
468 
469 	xmlXPathFreeObject (object);
470 
471 	return TRUE;
472 }
473 
474 /**
475  * e_xml_is_element_name: (skip)
476  * @node: (nullable): an #xmlNode
477  * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
478  * @name: an element name to search for
479  *
480  * Returns: Whether the @node is an element node of name @name and with a namespace href set to @ns_href
481  *
482  * Since: 3.38
483  **/
484 gboolean
e_xml_is_element_name(xmlNode * node,const gchar * ns_href,const gchar * name)485 e_xml_is_element_name (xmlNode *node,
486 		       const gchar *ns_href,
487 		       const gchar *name)
488 {
489 	if (!node || node->type != XML_ELEMENT_NODE)
490 		return FALSE;
491 
492 	if (g_strcmp0 ((const gchar *) node->name, name) == 0) {
493 		if (!ns_href) {
494 			if (!node->ns)
495 				return TRUE;
496 		} else if (node->ns) {
497 			xmlNsPtr nsPtr;
498 
499 			if (g_strcmp0 ((const gchar *) node->ns->href, ns_href) == 0)
500 				return TRUE;
501 
502 			nsPtr = xmlSearchNsByHref (node->doc, node, (const xmlChar *) ns_href);
503 
504 			if (nsPtr && node->ns == nsPtr)
505 				return TRUE;
506 		}
507 	}
508 
509 	return FALSE;
510 }
511 
512 /**
513  * e_xml_find_sibling: (skip)
514  * @sibling: (nullable): an #xmlNode, where to start searching
515  * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
516  * @name: an element name to search for
517  *
518  * Searches the sibling nodes of the @sibling for an element named @name in namespace @ns_href.
519  * It checks the @sibling itself too, but it doesn't check the previous siblings of the @sibling.
520  *
521  * Returns: (transfer none) (nullable): an #xmlNode of the given name, or %NULL, if not found
522  *    It also returns %NULL, when the @sibling is %NULL.
523  *
524  * See: e_xml_find_next_sibling(), e_xml_find_child()
525  *
526  * Since: 3.38
527  **/
528 xmlNode *
e_xml_find_sibling(xmlNode * sibling,const gchar * ns_href,const gchar * name)529 e_xml_find_sibling (xmlNode *sibling,
530 		    const gchar *ns_href,
531 		    const gchar *name)
532 {
533 	xmlNode *node;
534 
535 	for (node = sibling; node; node = xmlNextElementSibling (node)) {
536 		if (e_xml_is_element_name (node, ns_href, name))
537 			break;
538 	}
539 
540 	return node;
541 }
542 
543 /**
544  * e_xml_find_next_sibling: (skip)
545  * @sibling: (nullable): an #xmlNode, where to search from
546  * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
547  * @name: an element name to search for
548  *
549  * Searches for the next sibling node of the @sibling for an element named @name in namespace @ns_href.
550  * Unlike e_xml_find_sibling(), it skips the @sibling itself.
551  *
552  * Returns: (transfer none) (nullable): an #xmlNode of the given name, or %NULL, if not found
553  *    It also returns %NULL, when the @sibling is %NULL.
554  *
555  * See: e_xml_find_sibling(), e_xml_find_child()
556  *
557  * Since: 3.38
558  **/
559 xmlNode *
e_xml_find_next_sibling(xmlNode * sibling,const gchar * ns_href,const gchar * name)560 e_xml_find_next_sibling (xmlNode *sibling,
561 			 const gchar *ns_href,
562 			 const gchar *name)
563 {
564 	if (!sibling)
565 		return NULL;
566 
567 	return e_xml_find_sibling (sibling->next, ns_href, name);
568 }
569 
570 /**
571  * e_xml_find_child: (skip)
572  * @parent: (nullable): an #xmlNode, parent of which immediate children to search
573  * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
574  * @name: an element name to search for
575  *
576  * Searches the children nodes of the @parent for an element named @name in namespace @ns_href.
577  *
578  * Returns: (transfer none) (nullable): an #xmlNode of the given name, or %NULL, if not found.
579  *    It also returns %NULL, when the @parent is %NULL.
580  *
581  * See: e_xml_find_sibling(), e_xml_find_children_nodes()
582  *
583  * Since: 3.38
584  **/
585 xmlNode *
e_xml_find_child(xmlNode * parent,const gchar * ns_href,const gchar * name)586 e_xml_find_child (xmlNode *parent,
587 		  const gchar *ns_href,
588 		  const gchar *name)
589 {
590 	if (!parent)
591 		return NULL;
592 
593 	return e_xml_find_sibling (parent->children, ns_href, name);
594 }
595 
596 /**
597  * e_xml_dup_node_content: (skip)
598  * @node: (nullable): an #xmlNode
599  *
600  * Duplicates content of the @node. If the @node is %NULL, then the
601  * function does nothing and returns also %NULL.
602  *
603  * Unlike e_xml_get_node_text(), this includes also any element sub-structure
604  * of the @node, if any such exists.
605  *
606  * Returns: (transfer full) (nullable): the @node content as #xmlChar string,
607  *    or %NULL, when the content could not be read or was not set. Free
608  *    the non-%NULL value with xmlFree(), when no longer needed.
609  *
610  * See: e_xml_find_child_and_dup_content(), e_xml_get_node_text()
611  *
612  * Since: 3.38
613  **/
614 xmlChar *
e_xml_dup_node_content(const xmlNode * node)615 e_xml_dup_node_content (const xmlNode *node)
616 {
617 	if (!node)
618 		return NULL;
619 
620 	return xmlNodeGetContent (node);
621 }
622 
623 /**
624  * e_xml_find_child_and_dup_content: (skip)
625  * @parent: (nullable): an #xmlNode, parent of which immediate children to search
626  * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
627  * @name: an element name to search for
628  *
629  * Searches the children nodes of the @parent for an element named @name in namespace @ns_href
630  * and returns its content. This combines e_xml_find_child() and e_xml_dup_node_content() calls.
631  *
632  * Returns: (transfer full) (nullable): the found node content as #xmlChar string,
633  *    or %NULL, when the node could not be found or the content could not be read
634  *    or was not set. Free the non-%NULL value with xmlFree(), when no longer needed.
635  *
636  * See: e_xml_find_child_and_get_text()
637  *
638  * Since: 3.38
639  **/
640 xmlChar *
e_xml_find_child_and_dup_content(xmlNode * parent,const gchar * ns_href,const gchar * name)641 e_xml_find_child_and_dup_content (xmlNode *parent,
642 				  const gchar *ns_href,
643 				  const gchar *name)
644 {
645 	xmlNode *tmp;
646 
647 	tmp = e_xml_find_child (parent, ns_href, name);
648 
649 	if (!tmp)
650 		return NULL;
651 
652 	return e_xml_dup_node_content (tmp);
653 }
654 
655 /**
656  * e_xml_get_node_text: (skip)
657  * @node: (nullable): an #xmlNode
658  *
659  * Retrieves content of the @node. If the @node is %NULL, then the
660  * function does nothing and returns also %NULL.
661  *
662  * This is similar to e_xml_dup_node_content(), except it does not
663  * allocate new memory for the string. It also doesn't traverse
664  * the element structure, is returns the first text node's value
665  * only. It can be used to avoid unnecessary allocations, when
666  * reading element values with a single text node as a child.
667  *
668  * Returns: (transfer none) (nullable): The @node content, or %NULL.
669  *
670  * See: e_xml_dup_node_content()
671  *
672  * Since: 3.38
673  **/
674 const xmlChar *
e_xml_get_node_text(const xmlNode * node)675 e_xml_get_node_text (const xmlNode *node)
676 {
677 	xmlNode *child;
678 
679 	if (!node)
680 		return NULL;
681 
682 	if (node->type == XML_TEXT_NODE || node->type == XML_CDATA_SECTION_NODE)
683 		return node->content;
684 
685 	for (child = node->children; child; child = child->next) {
686 		if (child->type == XML_TEXT_NODE || child->type == XML_CDATA_SECTION_NODE)
687 			return child->content;
688 	}
689 
690 	return NULL;
691 }
692 
693 /**
694  * e_xml_find_child_and_get_text: (skip)
695  * @parent: (nullable): an #xmlNode, parent of which immediate children to search
696  * @ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
697  * @name: an element name to search for
698  *
699  * Searches the children nodes of the @parent for an element named @name in namespace @ns_href
700  * and returns its text content.
701  *
702  * It combines e_xml_find_child() and e_xml_get_node_text() calls.
703  *
704  * Returns: (transfer none) (nullable): the found node text as #xmlChar string,
705  *    or %NULL, when the node could not be found or the content could not be read
706  *    or was not set.
707  *
708  * See: e_xml_find_child_and_dup_content(), e_xml_find_children_nodes()
709  *
710  * Since: 3.38
711  **/
712 const xmlChar *
e_xml_find_child_and_get_text(xmlNode * parent,const gchar * ns_href,const gchar * name)713 e_xml_find_child_and_get_text (xmlNode *parent,
714 			       const gchar *ns_href,
715 			       const gchar *name)
716 {
717 	xmlNode *tmp;
718 
719 	tmp = e_xml_find_child (parent, ns_href, name);
720 
721 	if (!tmp)
722 		return NULL;
723 
724 	return e_xml_get_node_text (tmp);
725 }
726 
727 /**
728  * e_xml_find_children_nodes: (skip)
729  * @parent: an #xmlNode, whose children to search
730  * @count: how many nodes will be read
731  * @...: triple of arguments describing the nodes and their out variable
732  *
733  * Retrieve multiple nodes in one go, in an efficient way. It can be
734  * quicker than traversing the children of the @parent @count times
735  * in certain circumstances.
736  *
737  * The variable parameters expect triple of:
738  *   const gchar *ns_href;
739  *   const gchar *name;
740  *   xmlNode **out_node;
741  * where the ns_href is a namespace href the node should have set,
742  * or %NULL for none namespace; the name is an element name to search for.
743  * The names should not be included more than once.
744  *
745  * Since: 3.38
746  **/
747 void
e_xml_find_children_nodes(xmlNode * parent,guint count,...)748 e_xml_find_children_nodes (xmlNode *parent,
749 			   guint count,
750 			   ...)
751 {
752 	struct _data {
753 		const gchar *ns_href;
754 		const gchar *name;
755 		xmlNode **out_node;
756 	} *data;
757 	va_list args;
758 	xmlNode *node;
759 	guint ii;
760 
761 	g_return_if_fail (count > 0);
762 
763 	data = g_alloca (sizeof (struct _data) * count);
764 
765 	va_start (args, count);
766 
767 	for (ii = 0; ii < count; ii++) {
768 		data[ii].ns_href = va_arg (args, const gchar *);
769 		data[ii].name = va_arg (args, const gchar *);
770 		data[ii].out_node = va_arg (args, xmlNode **);
771 
772 		*(data[ii].out_node) = NULL;
773 	}
774 
775 	va_end (args);
776 
777 	for (node = parent->children; node; node = count ? xmlNextElementSibling (node) : NULL) {
778 		for (ii = 0; ii < count; ii++) {
779 			if (e_xml_is_element_name (node, data[ii].ns_href, data[ii].name)) {
780 				*(data[ii].out_node) = node;
781 				count--;
782 
783 				if (ii < count)
784 					data[ii] = data[count];
785 
786 				break;
787 			}
788 		}
789 	}
790 }
791 
792 /**
793  * e_xml_find_in_hierarchy: (skip)
794  * @parent: (nullable): an #xmlNode, or %NULL, in which case function does nothing and just returns %NULL
795  * @child_ns_href: (nullable): a namespace href the node should have set, or %NULL for none namespace
796  * @child_name: an element name to search for
797  * @...: a two-%NULL-terminated pair of hierarchy children
798  *
799  * Checks whether the @parent has a hierarchy of children described by pair
800  * of 'ns_href' and 'name'.
801  *
802  * Note: It requires two %NULL-s at the end of the arguments, because the `ns_href' can
803  *    be %NULL, thus it could not distinguish between no namespace href and the end of
804  *    the hierarchy children, thus it stops only on the 'name' being %NULL.
805  *
806  * Returns: (transfer none) (nullable): an #xmlNode referencing the node in the hierarchy
807  *    of the children of the @parent, or %NULL, when no such found.
808  *
809  * Since: 3.38
810  **/
811 xmlNode *
e_xml_find_in_hierarchy(xmlNode * parent,const gchar * child_ns_href,const gchar * child_name,...)812 e_xml_find_in_hierarchy (xmlNode *parent,
813 			 const gchar *child_ns_href,
814 			 const gchar *child_name,
815 			 ...)
816 {
817 	xmlNode *node;
818 	va_list va;
819 
820 	if (!parent)
821 		return NULL;
822 
823 	node = e_xml_find_child (parent, child_ns_href, child_name);
824 
825 	if (!node)
826 		return NULL;
827 
828 	va_start (va, child_name);
829 
830 	while (node) {
831 		child_ns_href = va_arg (va, const gchar *);
832 		child_name = va_arg (va, const gchar *);
833 
834 		if (!child_name)
835 			break;
836 
837 		node = e_xml_find_child (node, child_ns_href, child_name);
838 	}
839 
840 	va_end (va);
841 
842 	return child_name ? NULL : node;
843 }
844