• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-xmlrpc.c: XML-RPC parser/generator
4  *
5  * Copyright 2007 Red Hat, Inc.
6  * Copyright 2007 OpenedHand Ltd.
7  * Copyright 2015 Collabora ltd.
8  *
9  * Author:
10  *   Eduardo Lima Mitev  <elima@igalia.com>
11  *   Xavier Claessens <xavier.claessens@collabora.com>
12  */
13 
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17 
18 #include <string.h>
19 #include <errno.h>
20 #include <libxml/tree.h>
21 #include "soup-xmlrpc.h"
22 #include "soup.h"
23 
24 static gboolean insert_value (xmlNode  *parent, GVariant *value, GError **error);
25 
26 static gboolean
insert_array(xmlNode * parent,GVariant * value,GError ** error)27 insert_array (xmlNode *parent, GVariant *value, GError **error)
28 {
29 	xmlNode *node;
30 	GVariantIter iter;
31 	GVariant *child;
32 
33 	node = xmlNewChild (parent, NULL,
34 	                    (const xmlChar *)"array", NULL);
35 	node = xmlNewChild (node, NULL,
36 	                    (const xmlChar *)"data", NULL);
37 
38 	g_variant_iter_init (&iter, value);
39 	while ((child = g_variant_iter_next_value (&iter))) {
40 		if (!insert_value (node, child, error)) {
41 			g_variant_unref (child);
42 			return FALSE;
43 		}
44 		g_variant_unref (child);
45 	}
46 
47 	return TRUE;
48 }
49 
50 static gboolean
insert_struct_member(xmlNode * parent,GVariant * value,GError ** error)51 insert_struct_member (xmlNode *parent, GVariant *value, GError **error)
52 {
53 	xmlNode *member;
54 	GVariant *mname;
55 	GVariant *mvalue;
56 	gboolean ret = FALSE;
57 
58 	mname = g_variant_get_child_value (value, 0);
59 	mvalue = g_variant_get_child_value (value, 1);
60 
61 	if (g_variant_classify (mname) != G_VARIANT_CLASS_STRING) {
62 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
63 			     "Only string keys are supported in dictionaries, got %s",
64 			     g_variant_get_type_string (mname));
65 		goto fail;
66 	}
67 
68 	member = xmlNewChild (parent, NULL,
69 	                      (const xmlChar *)"member", NULL);
70 
71 	xmlNewTextChild (member, NULL,
72 	                 (const xmlChar *)"name",
73 	                 (const xmlChar *)g_variant_get_string (mname, NULL));
74 
75 	ret = insert_value (member, mvalue, error);
76 
77 fail:
78 	g_variant_unref (mname);
79 	g_variant_unref (mvalue);
80 
81 	return ret;
82 }
83 
84 static gboolean
insert_struct(xmlNode * parent,GVariant * value,GError ** error)85 insert_struct (xmlNode *parent, GVariant *value, GError **error)
86 {
87 	xmlNode *struct_node;
88 	GVariantIter iter;
89 	GVariant *child;
90 
91 	struct_node = xmlNewChild (parent, NULL,
92 	                           (const xmlChar *)"struct", NULL);
93 
94 	g_variant_iter_init (&iter, value);
95 	while ((child = g_variant_iter_next_value (&iter))) {
96 		if (!insert_struct_member (struct_node, child, error)) {
97 			g_variant_unref (child);
98 			return FALSE;
99 		}
100 		g_variant_unref (child);
101 	}
102 
103 	return TRUE;
104 }
105 
106 static gboolean
insert_value(xmlNode * parent,GVariant * value,GError ** error)107 insert_value (xmlNode *parent, GVariant *value, GError **error)
108 {
109 	xmlNode *xvalue;
110 	const char *type_str = NULL;
111 	char buf[128];
112 
113 	xvalue = xmlNewChild (parent, NULL, (const xmlChar *)"value", NULL);
114 
115 	switch (g_variant_classify (value)) {
116 	case G_VARIANT_CLASS_BOOLEAN:
117 		g_snprintf (buf, sizeof (buf), "%d", g_variant_get_boolean (value));
118 		type_str = "boolean";
119 		break;
120 	case G_VARIANT_CLASS_BYTE:
121 		g_snprintf (buf, sizeof (buf), "%u", g_variant_get_byte (value));
122 		type_str = "int";
123 		break;
124 	case G_VARIANT_CLASS_INT16:
125 		g_snprintf (buf, sizeof (buf), "%d", g_variant_get_int16 (value));
126 		type_str = "int";
127 		break;
128 	case G_VARIANT_CLASS_UINT16:
129 		g_snprintf (buf, sizeof (buf), "%u", g_variant_get_uint16 (value));
130 		type_str = "int";
131 		break;
132 	case G_VARIANT_CLASS_INT32:
133 		g_snprintf (buf, sizeof (buf), "%d", g_variant_get_int32 (value));
134 		type_str = "int";
135 		break;
136 	case G_VARIANT_CLASS_UINT32:
137 		g_snprintf (buf, sizeof (buf), "%u", g_variant_get_uint32 (value));
138 		type_str = "i8";
139 		break;
140 	case G_VARIANT_CLASS_INT64:
141 		g_snprintf (buf, sizeof (buf), "%"G_GINT64_FORMAT, g_variant_get_int64 (value));
142 		type_str = "i8";
143 		break;
144 	case G_VARIANT_CLASS_DOUBLE:
145 		g_ascii_dtostr (buf, sizeof (buf), g_variant_get_double (value));
146 		type_str = "double";
147 		break;
148 	case G_VARIANT_CLASS_STRING:
149 		xmlNewTextChild (xvalue, NULL,
150 		                 (const xmlChar *)"string",
151 		                 (const xmlChar *)g_variant_get_string (value, NULL));
152 		break;
153 	case G_VARIANT_CLASS_VARIANT: {
154 		GVariant *child;
155 
156 		xmlUnlinkNode (xvalue);
157 		xmlFreeNode (xvalue);
158 
159 		child = g_variant_get_variant (value);
160 		if (!insert_value (parent, child, error)) {
161 			g_variant_unref (child);
162 			return FALSE;
163 		}
164 		g_variant_unref (child);
165 		break;
166 	}
167 	case G_VARIANT_CLASS_ARRAY: {
168 		if (g_variant_is_of_type (value, G_VARIANT_TYPE_BYTESTRING)) {
169 			char *encoded;
170 
171 			encoded = g_base64_encode (g_variant_get_data (value),
172 			                           g_variant_get_size (value));
173 			xmlNewChild (xvalue, NULL,
174 			             (const xmlChar *)"base64",
175 			             (const xmlChar *)encoded);
176 			g_free (encoded);
177 		} else if (g_variant_is_of_type (value, G_VARIANT_TYPE_DICTIONARY)) {
178 			if (!insert_struct (xvalue, value, error))
179 				return FALSE;
180 		} else {
181 			if (!insert_array (xvalue, value, error))
182 				return FALSE;
183 		}
184 
185 		break;
186 	}
187 	case G_VARIANT_CLASS_TUPLE: {
188 		/* Special case for custom types */
189 		if (g_variant_is_of_type (value, G_VARIANT_TYPE ("(oss)"))) {
190 			const char *path;
191 			const char *type;
192 			const char *v;
193 
194 			g_variant_get (value, "(&o&s&s)", &path, &type, &v);
195 			if (g_str_equal (path, "/org/gnome/libsoup/ExtensionType")) {
196 				xmlNewTextChild (xvalue, NULL,
197 				                 (const xmlChar *)type,
198 				                 (const xmlChar *)v);
199 				break;
200 			}
201 		}
202 
203 		if (!insert_array (xvalue, value, error))
204 			return FALSE;
205 		break;
206 	}
207 	case G_VARIANT_CLASS_DICT_ENTRY: {
208 		xmlNode *node;
209 
210 		node = xmlNewChild (xvalue, NULL,
211 		                    (const xmlChar *)"struct", NULL);
212 		if (!insert_struct_member (node, value, error))
213 			return FALSE;
214 		break;
215 	}
216 	case G_VARIANT_CLASS_HANDLE:
217 	case G_VARIANT_CLASS_MAYBE:
218 	case G_VARIANT_CLASS_UINT64:
219 	case G_VARIANT_CLASS_OBJECT_PATH:
220 	case G_VARIANT_CLASS_SIGNATURE:
221 	default:
222 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
223 			     "Unsupported type: %s", g_variant_get_type_string (value));
224 		goto fail;
225 	}
226 
227 	if (type_str != NULL) {
228 		xmlNewTextChild (xvalue, NULL,
229 		                 (const xmlChar *)type_str,
230 		                 (const xmlChar *)buf);
231 	}
232 
233 	return TRUE;
234 
235 fail:
236 	return FALSE;
237 }
238 
239 /**
240  * soup_xmlrpc_build_request:
241  * @method_name: the name of the XML-RPC method
242  * @params: a #GVariant tuple
243  * @error: a #GError, or %NULL
244  *
245  * This creates an XML-RPC methodCall and returns it as a string.
246  * This is the low-level method that soup_xmlrpc_message_new() is
247  * built on.
248  *
249  * @params is a #GVariant tuple representing the method parameters.
250  *
251  * Serialization details:
252  *  - "a{s*}" and "{s*}" are serialized as &lt;struct&gt;
253  *  - "ay" is serialized as &lt;base64&gt;
254  *  - Other arrays and tuples are serialized as &lt;array&gt;
255  *  - booleans are serialized as &lt;boolean&gt;
256  *  - byte, int16, uint16 and int32 are serialized as &lt;int&gt;
257  *  - uint32 and int64 are serialized as the nonstandard &lt;i8&gt; type
258  *  - doubles are serialized as &lt;double&gt;
259  *  - Strings are serialized as &lt;string&gt;
260  *  - Variants (i.e. "v" type) are unwrapped and their child is serialized.
261  *  - #GVariants created by soup_xmlrpc_variant_new_datetime() are serialized as
262  *    &lt;dateTime.iso8601&gt;
263  *  - Other types are not supported and will return %NULL and set @error.
264  *    This notably includes: object-paths, signatures, uint64, handles, maybes
265  *    and dictionaries with non-string keys.
266  *
267  * If @params is floating, it is consumed.
268  *
269  * Return value: the text of the methodCall, or %NULL on error.
270  * Since: 2.52
271  **/
272 char *
soup_xmlrpc_build_request(const char * method_name,GVariant * params,GError ** error)273 soup_xmlrpc_build_request (const char  *method_name,
274 			   GVariant    *params,
275 			   GError     **error)
276 {
277 	xmlDoc *doc;
278 	xmlNode *node, *param;
279 	xmlChar *xmlbody;
280 	GVariantIter iter;
281 	GVariant *child;
282 	int len;
283 	char *body = NULL;
284 
285 	g_return_val_if_fail (g_variant_is_of_type (params, G_VARIANT_TYPE_TUPLE), NULL);
286 
287 	g_variant_ref_sink (params);
288 
289 	doc = xmlNewDoc ((const xmlChar *)"1.0");
290 	doc->standalone = FALSE;
291 	doc->encoding = xmlCharStrdup ("UTF-8");
292 
293 	node = xmlNewDocNode (doc, NULL, (const xmlChar *)"methodCall", NULL);
294 	xmlDocSetRootElement (doc, node);
295 	xmlNewChild (node, NULL, (const xmlChar *)"methodName",
296 		     (const xmlChar *)method_name);
297 
298 	node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
299 	g_variant_iter_init (&iter, params);
300 	while ((child = g_variant_iter_next_value (&iter))) {
301 		param  = xmlNewChild (node, NULL,
302 				      (const xmlChar *)"param", NULL);
303 		if (!insert_value (param, child, error)) {
304 			xmlFreeDoc (doc);
305 			g_variant_unref (child);
306 			g_variant_unref (params);
307 			return NULL;
308 		}
309 		g_variant_unref (child);
310 	}
311 
312 	xmlDocDumpMemory (doc, &xmlbody, &len);
313 	body = g_strndup ((char *)xmlbody, len);
314 	xmlFree (xmlbody);
315 
316 	xmlFreeDoc (doc);
317 	g_variant_unref (params);
318 
319 	return body;
320 }
321 
322 /**
323  * soup_xmlrpc_message_new:
324  * @uri: URI of the XML-RPC service
325  * @method_name: the name of the XML-RPC method to invoke at @uri
326  * @params: a #GVariant tuple
327  * @error: a #GError, or %NULL
328  *
329  * Creates an XML-RPC methodCall and returns a #SoupMessage, ready
330  * to send, for that method call.
331  *
332  * See soup_xmlrpc_build_request() for serialization details.
333  *
334  * If @params is floating, it is consumed.
335  *
336  * Returns: (transfer full): a #SoupMessage encoding the
337  *   indicated XML-RPC request, or %NULL on error.
338  *
339  * Since: 2.52
340  **/
341 SoupMessage *
soup_xmlrpc_message_new(const char * uri,const char * method_name,GVariant * params,GError ** error)342 soup_xmlrpc_message_new (const char  *uri,
343 			 const char  *method_name,
344 			 GVariant    *params,
345 			 GError     **error)
346 {
347 	SoupMessage *msg;
348 	char *body;
349 
350 	body = soup_xmlrpc_build_request (method_name, params, error);
351 	if (!body)
352 		return NULL;
353 
354 	msg = soup_message_new ("POST", uri);
355 	soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
356 				  body, strlen (body));
357 	return msg;
358 }
359 
360 /**
361  * soup_xmlrpc_build_response:
362  * @value: the return value
363  * @error: a #GError, or %NULL
364  *
365  * This creates a (successful) XML-RPC methodResponse and returns it
366  * as a string. To create a fault response, use soup_xmlrpc_build_fault(). This
367  * is the low-level method that soup_xmlrpc_message_set_response() is built on.
368  *
369  * See soup_xmlrpc_build_request() for serialization details, but note
370  * that since a method can only have a single return value, @value
371  * should not be a tuple here (unless the return value is an array).
372  *
373  * If @value is floating, it is consumed.
374  *
375  * Returns: the text of the methodResponse, or %NULL on error.
376  *
377  * Since: 2.52
378  **/
379 char *
soup_xmlrpc_build_response(GVariant * value,GError ** error)380 soup_xmlrpc_build_response (GVariant *value, GError **error)
381 {
382 	xmlDoc *doc;
383 	xmlNode *node;
384 	xmlChar *xmlbody;
385 	char *body;
386 	int len;
387 
388 	g_variant_ref_sink (value);
389 
390 	doc = xmlNewDoc ((const xmlChar *)"1.0");
391 	doc->standalone = FALSE;
392 	doc->encoding = xmlCharStrdup ("UTF-8");
393 
394 	node = xmlNewDocNode (doc, NULL,
395 			      (const xmlChar *)"methodResponse", NULL);
396 	xmlDocSetRootElement (doc, node);
397 
398 	node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
399 	node = xmlNewChild (node, NULL, (const xmlChar *)"param", NULL);
400 	if (!insert_value (node, value, error)) {
401 		xmlFreeDoc (doc);
402 		g_variant_unref (value);
403 		return NULL;
404 	}
405 
406 	xmlDocDumpMemory (doc, &xmlbody, &len);
407 	body = g_strndup ((char *)xmlbody, len);
408 	xmlFree (xmlbody);
409 
410 	xmlFreeDoc (doc);
411 	g_variant_unref (value);
412 
413 	return body;
414 }
415 
416 char *
417 soup_xmlrpc_build_faultv (int         fault_code,
418                           const char *fault_format,
419                           va_list     args) G_GNUC_PRINTF (2, 0);
420 
421 char *
soup_xmlrpc_build_faultv(int fault_code,const char * fault_format,va_list args)422 soup_xmlrpc_build_faultv (int fault_code, const char *fault_format, va_list args)
423 {
424 	xmlDoc *doc;
425 	xmlNode *node, *member;
426 	GVariant *value;
427 	xmlChar *xmlbody;
428 	char *fault_string, *body;
429 	int len;
430 
431 	fault_string = g_strdup_vprintf (fault_format, args);
432 
433 	doc = xmlNewDoc ((const xmlChar *)"1.0");
434 	doc->standalone = FALSE;
435 	doc->encoding = xmlCharStrdup ("UTF-8");
436 
437 	node = xmlNewDocNode (doc, NULL,
438 			      (const xmlChar *)"methodResponse", NULL);
439 	xmlDocSetRootElement (doc, node);
440 	node = xmlNewChild (node, NULL, (const xmlChar *)"fault", NULL);
441 	node = xmlNewChild (node, NULL, (const xmlChar *)"value", NULL);
442 	node = xmlNewChild (node, NULL, (const xmlChar *)"struct", NULL);
443 
444 	member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
445 	xmlNewChild (member, NULL,
446 		     (const xmlChar *)"name", (const xmlChar *)"faultCode");
447 	value = g_variant_new_int32 (fault_code);
448 	insert_value (member, value, NULL);
449 	g_variant_unref (value);
450 
451 	member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
452 	xmlNewChild (member, NULL,
453 		     (const xmlChar *)"name", (const xmlChar *)"faultString");
454 	value = g_variant_new_take_string (fault_string);
455 	insert_value (member, value, NULL);
456 	g_variant_unref (value);
457 
458 	xmlDocDumpMemory (doc, &xmlbody, &len);
459 	body = g_strndup ((char *)xmlbody, len);
460 	xmlFree (xmlbody);
461 	xmlFreeDoc (doc);
462 
463 	return body;
464 }
465 
466 /**
467  * soup_xmlrpc_build_fault:
468  * @fault_code: the fault code
469  * @fault_format: a printf()-style format string
470  * @...: the parameters to @fault_format
471  *
472  * This creates an XML-RPC fault response and returns it as a string.
473  * (To create a successful response, use
474  * soup_xmlrpc_build_method_response().)
475  *
476  * Return value: the text of the fault
477  **/
478 char *
soup_xmlrpc_build_fault(int fault_code,const char * fault_format,...)479 soup_xmlrpc_build_fault (int fault_code, const char *fault_format, ...)
480 {
481 	va_list args;
482 	char *body;
483 
484 	va_start (args, fault_format);
485 	body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
486 	va_end (args);
487 	return body;
488 }
489 
490 /**
491  * soup_xmlrpc_message_set_fault:
492  * @msg: an XML-RPC request
493  * @fault_code: the fault code
494  * @fault_format: a printf()-style format string
495  * @...: the parameters to @fault_format
496  *
497  * Sets the status code and response body of @msg to indicate an
498  * unsuccessful XML-RPC call, with the error described by @fault_code
499  * and @fault_format.
500  *
501  * Since: 2.52
502  **/
503 void
soup_xmlrpc_message_set_fault(SoupMessage * msg,int fault_code,const char * fault_format,...)504 soup_xmlrpc_message_set_fault (SoupMessage *msg, int fault_code,
505 			       const char *fault_format, ...)
506 {
507 	va_list args;
508 	char *body;
509 
510 	va_start (args, fault_format);
511 	body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
512 	va_end (args);
513 
514 	soup_message_set_status (msg, SOUP_STATUS_OK);
515 	soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
516 				   body, strlen (body));
517 }
518 
519 /**
520  * soup_xmlrpc_message_set_response:
521  * @msg: an XML-RPC request
522  * @value: a #GVariant
523  * @error: a #GError, or %NULL
524  *
525  * Sets the status code and response body of @msg to indicate a
526  * successful XML-RPC call, with a return value given by @value. To set a
527  * fault response, use soup_xmlrpc_message_set_fault().
528  *
529  * See soup_xmlrpc_build_request() for serialization details.
530  *
531  * If @value is floating, it is consumed.
532  *
533  * Returns: %TRUE on success, %FALSE otherwise.
534  *
535  * Since: 2.52
536  **/
537 gboolean
soup_xmlrpc_message_set_response(SoupMessage * msg,GVariant * value,GError ** error)538 soup_xmlrpc_message_set_response (SoupMessage *msg, GVariant *value, GError **error)
539 {
540 	char *body;
541 
542 	body = soup_xmlrpc_build_response (value, error);
543 	if (!body)
544 		return FALSE;
545 
546 	soup_message_set_status (msg, SOUP_STATUS_OK);
547 	soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
548 				   body, strlen (body));
549 	return TRUE;
550 }
551 
552 static GVariant *parse_value (xmlNode *node, const char **signature, GError **error);
553 
554 static xmlNode *
find_real_node(xmlNode * node)555 find_real_node (xmlNode *node)
556 {
557 	while (node && (node->type == XML_COMMENT_NODE ||
558 			xmlIsBlankNode (node)))
559 		node = node->next;
560 	return node;
561 }
562 
563 static char *
signature_get_next_complete_type(const char ** signature)564 signature_get_next_complete_type (const char **signature)
565 {
566 	GVariantClass class;
567 	const char *initial_signature;
568 	char *result;
569 
570 	/* here it is assumed that 'signature' is a valid type string */
571 
572 	initial_signature = *signature;
573 	class = (*signature)[0];
574 
575 	if (class == G_VARIANT_CLASS_TUPLE || class == G_VARIANT_CLASS_DICT_ENTRY) {
576 		char stack[256] = {0};
577 		guint stack_len = 0;
578 
579 		do {
580 			if ((*signature)[0] == G_VARIANT_CLASS_TUPLE) {
581 				stack[stack_len] = ')';
582 				stack_len++;
583 			}
584 			else if ( (*signature)[0] == G_VARIANT_CLASS_DICT_ENTRY) {
585 				stack[stack_len] = '}';
586 				stack_len++;
587 			}
588 
589 			(*signature)++;
590 
591 			if ( (*signature)[0] == stack[stack_len - 1])
592 				stack_len--;
593 		} while (stack_len > 0);
594 
595 		(*signature)++;
596 	} else if (class == G_VARIANT_CLASS_ARRAY || class == G_VARIANT_CLASS_MAYBE) {
597 		char *tmp_sig;
598 
599 		(*signature)++;
600 		tmp_sig = signature_get_next_complete_type (signature);
601 		g_free (tmp_sig);
602 	} else {
603 		(*signature)++;
604 	}
605 
606 	result = g_strndup (initial_signature, (*signature) - initial_signature);
607 
608 	return result;
609 }
610 
611 static GVariant *
parse_array(xmlNode * node,const char ** signature,GError ** error)612 parse_array (xmlNode *node, const char **signature, GError **error)
613 {
614 	GVariant *variant = NULL;
615 	char *child_signature = NULL;
616 	char *array_signature = NULL;
617 	const char *tmp_signature;
618 	gboolean is_tuple = FALSE;
619 	xmlNode *member;
620 	GVariantBuilder builder;
621 	gboolean is_params = FALSE;
622 
623 	if (signature && *signature[0] == G_VARIANT_CLASS_VARIANT)
624 		signature = NULL;
625 
626 	if (g_str_equal (node->name, "array")) {
627 		node = find_real_node (node->children);
628 		if (!g_str_equal (node->name, "data")) {
629 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
630 				     "<data> expected but got '%s'", node->name);
631 			goto fail;
632 		}
633 	} else if (g_str_equal (node->name, "params")) {
634 		is_params = TRUE;
635 	} else {
636 		g_assert_not_reached ();
637 	}
638 
639 	if (signature != NULL) {
640 		if ((*signature)[0] == G_VARIANT_CLASS_TUPLE) {
641 			tmp_signature = *signature;
642 			array_signature = signature_get_next_complete_type (&tmp_signature);
643 			is_tuple = TRUE;
644 		}
645 		(*signature)++;
646 		child_signature = signature_get_next_complete_type (signature);
647 	} else {
648 		child_signature = g_strdup ("v");
649 	}
650 
651 	if (!array_signature)
652 		array_signature = g_strdup_printf ("a%s", child_signature);
653 	g_variant_builder_init (&builder, G_VARIANT_TYPE (array_signature));
654 
655 	for (member = find_real_node (node->children);
656 	     member;
657 	     member = find_real_node (member->next)) {
658 		GVariant *child;
659 		xmlNode *xval = member;
660 
661 		if (is_params) {
662 			if (!g_str_equal (member->name, "param")) {
663 				g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
664 					     "<param> expected but got '%s'", member->name);
665 				goto fail;
666 			}
667 			xval = find_real_node (member->children);
668 		}
669 
670 		if (strcmp ((const char *)xval->name, "value") != 0) {
671 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
672 				     "<value> expected but got '%s'", xval->name);
673 			goto fail;
674 		}
675 
676 		if (is_tuple && child_signature[0] == ')') {
677 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
678 				     "Too many values for tuple");
679 			goto fail;
680 		}
681 
682 		tmp_signature = child_signature;
683 		child = parse_value (xval, &tmp_signature, error);
684 		if (child == NULL)
685 			goto fail;
686 
687 		if (is_tuple) {
688 			g_free (child_signature),
689 			child_signature = signature_get_next_complete_type (signature);
690 		}
691 
692 		g_variant_builder_add_value (&builder, child);
693 	}
694 
695 	if (is_tuple && child_signature[0] != ')') {
696 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
697 			     "Too few values for tuple");
698 		goto fail;
699 	}
700 
701 	variant = g_variant_builder_end (&builder);
702 
703 fail:
704 	g_variant_builder_clear (&builder);
705 	g_free (child_signature);
706 	g_free (array_signature);
707 
708 	/* compensate the (*signature)++ call at the end of 'recurse()' */
709 	if (signature)
710 		(*signature)--;
711 
712 	return variant;
713 }
714 
715 static void
parse_dict_entry_signature(const char ** signature,char ** entry_signature,char ** key_signature,char ** value_signature)716 parse_dict_entry_signature (const char **signature,
717 			    char       **entry_signature,
718 			    char       **key_signature,
719 			    char       **value_signature)
720 {
721 	const char *tmp_sig;
722 
723 	if (signature)
724 		*entry_signature = signature_get_next_complete_type (signature);
725 	else
726 		*entry_signature = g_strdup ("{sv}");
727 
728 	tmp_sig = (*entry_signature) + 1;
729 	*key_signature = signature_get_next_complete_type (&tmp_sig);
730 	*value_signature = signature_get_next_complete_type (&tmp_sig);
731 }
732 
733 static GVariant *
parse_dictionary(xmlNode * node,const char ** signature,GError ** error)734 parse_dictionary (xmlNode *node, const char **signature, GError **error)
735 {
736 	GVariant *variant = NULL;
737 	char *dict_signature;
738 	char *entry_signature;
739 	char *key_signature;
740 	char *value_signature;
741 	GVariantBuilder builder;
742 	xmlNode *member;
743 
744 	if (signature && *signature[0] == G_VARIANT_CLASS_VARIANT)
745 		signature = NULL;
746 
747 	if (signature)
748 		(*signature)++;
749 
750 	parse_dict_entry_signature (signature,
751 				    &entry_signature,
752 				    &key_signature,
753 				    &value_signature);
754 
755 	dict_signature = g_strdup_printf ("a%s", entry_signature);
756 	g_variant_builder_init (&builder, G_VARIANT_TYPE (dict_signature));
757 
758 	if (!g_str_equal (key_signature, "s")) {
759 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
760 			     "Dictionary key must be string but got '%s'", key_signature);
761 		goto fail;
762 	}
763 
764 	for (member = find_real_node (node->children);
765 	     member;
766 	     member = find_real_node (member->next)) {
767 		xmlNode *child, *mname, *mxval;
768 		const char *tmp_signature;
769 		GVariant *value;
770 		xmlChar *content;
771 
772 		if (strcmp ((const char *)member->name, "member") != 0) {
773 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
774 				     "<member> expected but got '%s'", member->name);
775 			goto fail;
776 		}
777 
778 		mname = mxval = NULL;
779 
780 		for (child = find_real_node (member->children);
781 		     child;
782 		     child = find_real_node (child->next)) {
783 			if (!strcmp ((const char *)child->name, "name"))
784 				mname = child;
785 			else if (!strcmp ((const char *)child->name, "value"))
786 				mxval = child;
787 			else {
788 				g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
789 					     "<name> or <value> expected but got '%s'", child->name);
790 				goto fail;
791 			}
792 		}
793 
794 		if (!mname || !mxval) {
795 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
796 				     "Missing name or value in <member>");
797 			goto fail;
798 		}
799 
800 		tmp_signature = value_signature;
801 		value = parse_value (mxval, &tmp_signature, error);
802 		if (!value)
803 			goto fail;
804 
805 		content = xmlNodeGetContent (mname);
806 		g_variant_builder_open (&builder, G_VARIANT_TYPE (entry_signature));
807 		g_variant_builder_add (&builder, "s", content);
808 		g_variant_builder_add_value (&builder, value);
809 		g_variant_builder_close (&builder);
810 		xmlFree (content);
811 	}
812 
813 	variant = g_variant_builder_end (&builder);
814 
815 fail:
816 	g_variant_builder_clear (&builder);
817 	g_free (value_signature);
818 	g_free (key_signature);
819 	g_free (entry_signature);
820 	g_free (dict_signature);
821 
822 	/* compensate the (*signature)++ call at the end of 'recurse()' */
823 	if (signature != NULL)
824 		(*signature)--;
825 
826 	return variant;
827 }
828 
829 static GVariant *
parse_number(xmlNode * typenode,GVariantClass class,GError ** error)830 parse_number (xmlNode *typenode, GVariantClass class, GError **error)
831 {
832 	xmlChar *content;
833 	const char *str;
834 	char *endptr;
835 	gint64 num = 0;
836 	guint64 unum = 0;
837 	GVariant *variant = NULL;
838 
839 	content = xmlNodeGetContent (typenode);
840 	str = (const char *) content;
841 
842 	errno = 0;
843 
844 	if (class == G_VARIANT_CLASS_UINT64)
845 		unum = g_ascii_strtoull (str, &endptr, 10);
846 	else
847 		num = g_ascii_strtoll (str, &endptr, 10);
848 
849 	if (errno || endptr == str) {
850 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
851 			     "Couldn't parse number '%s'", str);
852 		goto fail;
853 	}
854 
855 #define RANGE(v, min, max) \
856 G_STMT_START{ \
857 	if (v < min || v > max) { \
858 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS, \
859 			     "Number out of range '%s'", str); \
860 		goto fail; \
861 	} \
862 } G_STMT_END
863 
864 	switch (class) {
865 	case G_VARIANT_CLASS_BOOLEAN:
866 		RANGE (num, 0, 1);
867 		variant = g_variant_new_boolean (num);
868 		break;
869 	case G_VARIANT_CLASS_BYTE:
870 		RANGE (num, 0, G_MAXUINT8);
871 		variant = g_variant_new_byte (num);
872 		break;
873 	case G_VARIANT_CLASS_INT16:
874 		RANGE (num, G_MININT16, G_MAXINT16);
875 		variant = g_variant_new_int16 (num);
876 		break;
877 	case G_VARIANT_CLASS_UINT16:
878 		RANGE (num, 0, G_MAXUINT16);
879 		variant = g_variant_new_uint16 (num);
880 		break;
881 	case G_VARIANT_CLASS_INT32:
882 		RANGE (num, G_MININT32, G_MAXINT32);
883 		variant = g_variant_new_int32 (num);
884 		break;
885 	case G_VARIANT_CLASS_UINT32:
886 		RANGE (num, 0, G_MAXUINT32);
887 		variant = g_variant_new_uint32 (num);
888 		break;
889 	case G_VARIANT_CLASS_INT64:
890 		RANGE (num, G_MININT64, G_MAXINT64);
891 		variant = g_variant_new_int64 (num);
892 		break;
893 	case G_VARIANT_CLASS_UINT64:
894 		RANGE (unum, 0, G_MAXUINT64);
895 		variant = g_variant_new_uint64 (unum);
896 		break;
897 	default:
898 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
899 			     "<%s> node does not match signature",
900 			     (const char *)typenode->name);
901 		goto fail;
902 	}
903 
904 fail:
905 	xmlFree (content);
906 
907 	return variant;
908 }
909 
910 static GVariant *
parse_double(xmlNode * typenode,GError ** error)911 parse_double (xmlNode *typenode, GError **error)
912 {
913 	GVariant *variant = NULL;
914 	xmlChar *content;
915 	const char *str;
916 	char *endptr;
917 	gdouble d;
918 
919 	content = xmlNodeGetContent (typenode);
920 	str = (const char *) content;
921 
922 	errno = 0;
923 	d = g_ascii_strtod (str, &endptr);
924 	if (errno || endptr == str) {
925 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
926 			     "Couldn't parse double '%s'", str);
927 		goto fail;
928 	}
929 
930 	variant = g_variant_new_double (d);
931 
932 fail:
933 	xmlFree (content);
934 
935 	return variant;
936 }
937 
938 static GVariant *
parse_base64(xmlNode * typenode,GError ** error)939 parse_base64 (xmlNode *typenode, GError **error)
940 {
941 	GVariant *variant;
942 	xmlChar *content;
943 	guchar *decoded;
944 	gsize len;
945 
946 	content = xmlNodeGetContent (typenode);
947 	decoded = g_base64_decode ((char *)content, &len);
948 	variant = g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
949 					   decoded, len,
950 					   TRUE,
951 					   g_free, decoded);
952 	xmlFree (content);
953 
954 	return variant;
955 }
956 
957 static GVariant *
soup_xmlrpc_variant_new_custom(const char * type,const char * v)958 soup_xmlrpc_variant_new_custom (const char *type, const char *v)
959 {
960 	return g_variant_new ("(oss)", "/org/gnome/libsoup/ExtensionType",
961 			      type, v);
962 }
963 
964 static GVariant *
parse_value(xmlNode * node,const char ** signature,GError ** error)965 parse_value (xmlNode *node, const char **signature, GError **error)
966 {
967 	xmlNode *typenode;
968 	const char *typename;
969 	xmlChar *content = NULL;
970 	GVariant *variant = NULL;
971 	GVariantClass class = G_VARIANT_CLASS_VARIANT;
972 
973 	if (signature)
974 		class = *signature[0];
975 
976 	if (g_str_equal ((const char *)node->name, "value")) {
977 		typenode = find_real_node (node->children);
978 		if (!typenode) {
979 			/* If no typenode, assume value's content is string */
980 			typename = "string";
981 			typenode = node;
982 		} else {
983 			typename = (const char *)typenode->name;
984 		}
985 	} else if (g_str_equal ((const char *)node->name, "params")) {
986 		typenode = node;
987 		typename = "params";
988 	} else {
989 		g_assert_not_reached ();
990 	}
991 
992 	if (g_str_equal (typename, "boolean")) {
993 		if (class != G_VARIANT_CLASS_VARIANT && class != G_VARIANT_CLASS_BOOLEAN) {
994 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
995 				     "<boolean> node does not match signature");
996 			goto fail;
997 		}
998 		variant = parse_number (typenode, G_VARIANT_CLASS_BOOLEAN, error);
999 	} else if (g_str_equal (typename, "int") || g_str_equal (typename, "i4")) {
1000 		if (class == G_VARIANT_CLASS_VARIANT)
1001 			variant = parse_number (typenode, G_VARIANT_CLASS_INT32, error);
1002 		else
1003 			variant = parse_number (typenode, class, error);
1004 	} else if (g_str_equal (typename, "i8")) {
1005 		if (class == G_VARIANT_CLASS_VARIANT)
1006 			variant = parse_number (typenode, G_VARIANT_CLASS_INT64, error);
1007 		else
1008 			variant = parse_number (typenode, class, error);
1009 	} else  if (g_str_equal (typename, "double")) {
1010 		if (class != G_VARIANT_CLASS_VARIANT && class != G_VARIANT_CLASS_DOUBLE) {
1011 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1012 				     "<double> node does not match signature");
1013 			goto fail;
1014 		}
1015 		variant = parse_double (typenode, error);
1016 	} else  if (g_str_equal (typename, "string")) {
1017 		if (class != G_VARIANT_CLASS_VARIANT && class != G_VARIANT_CLASS_STRING) {
1018 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1019 				     "<string> node does not match signature");
1020 			goto fail;
1021 		}
1022 		content = xmlNodeGetContent (typenode);
1023 		variant = g_variant_new_string ((const char *)content);
1024 	} else if (g_str_equal (typename, "base64")) {
1025 		if (class != G_VARIANT_CLASS_VARIANT) {
1026 			if (!g_str_has_prefix (*signature, "ay")) {
1027 				g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1028 					     "<base64> node does not match signature");
1029 				goto fail;
1030 			}
1031 			(*signature)++;
1032 		}
1033 		variant = parse_base64 (typenode, error);
1034 	} else if (g_str_equal (typename, "struct")) {
1035 		if (class != G_VARIANT_CLASS_VARIANT &&
1036 		    !g_str_has_prefix (*signature, "a{")) {
1037 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1038 				     "<struct> node does not match signature");
1039 			goto fail;
1040 		}
1041 		variant = parse_dictionary (typenode, signature, error);
1042 	} else if (g_str_equal (typename, "array") || g_str_equal (typename, "params")) {
1043 		if (class != G_VARIANT_CLASS_VARIANT &&
1044 		    class != G_VARIANT_CLASS_ARRAY &&
1045 		    class != G_VARIANT_CLASS_TUPLE) {
1046 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1047 				     "<%s> node does not match signature", typename);
1048 			goto fail;
1049 		}
1050 		variant = parse_array (typenode, signature, error);
1051 	} else if (g_str_equal (typename, "dateTime.iso8601")) {
1052 		if (class != G_VARIANT_CLASS_VARIANT) {
1053 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1054 				     "<dateTime.iso8601> node does not match signature");
1055 			goto fail;
1056 		}
1057 
1058 		content = xmlNodeGetContent (typenode);
1059 		variant = soup_xmlrpc_variant_new_custom ("dateTime.iso8601",
1060 							  (const char *)content);
1061 	} else {
1062 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1063 			     "Unknown node name %s", typename);
1064 		goto fail;
1065 	}
1066 
1067 	if (variant && signature) {
1068 		if (class == G_VARIANT_CLASS_VARIANT)
1069 			variant = g_variant_new_variant (variant);
1070 		(*signature)++;
1071 	}
1072 
1073 fail:
1074 	if (content)
1075 		xmlFree (content);
1076 
1077 	return variant;
1078 }
1079 
1080 /**
1081  * SoupXMLRPCParams:
1082  *
1083  * Opaque structure containing XML-RPC methodCall parameter values.
1084  * Can be parsed using soup_xmlrpc_params_parse() and freed with
1085  * soup_xmlrpc_params_free().
1086  *
1087  * Since: 2.52
1088  */
1089 struct _SoupXMLRPCParams
1090 {
1091   xmlNode *node;
1092 };
1093 
1094 /**
1095  * soup_xmlrpc_params_free:
1096  * @self: a SoupXMLRPCParams
1097  *
1098  * Free a #SoupXMLRPCParams returned by soup_xmlrpc_parse_request().
1099  *
1100  * Since: 2.52
1101  */
1102 void
soup_xmlrpc_params_free(SoupXMLRPCParams * self)1103 soup_xmlrpc_params_free (SoupXMLRPCParams *self)
1104 {
1105 	g_return_if_fail (self != NULL);
1106 
1107 	if (self->node)
1108 		xmlFreeDoc (self->node->doc);
1109 	g_slice_free (SoupXMLRPCParams, self);
1110 }
1111 
1112 static SoupXMLRPCParams *
soup_xmlrpc_params_new(xmlNode * node)1113 soup_xmlrpc_params_new (xmlNode *node)
1114 {
1115 	SoupXMLRPCParams *self;
1116 
1117 	self = g_slice_new (SoupXMLRPCParams);
1118 	self->node = node;
1119 
1120 	return self;
1121 }
1122 
1123 /**
1124  * soup_xmlrpc_params_parse:
1125  * @self: A #SoupXMLRPCParams
1126  * @signature: (allow-none): A valid #GVariant type string, or %NULL
1127  * @error: a #GError, or %NULL
1128  *
1129  * Parse method parameters returned by soup_xmlrpc_parse_request().
1130  *
1131  * Deserialization details:
1132  *  - If @signature is provided, &lt;int&gt; and &lt;i4&gt; can be deserialized
1133  *    to byte, int16, uint16, int32, uint32, int64 or uint64. Otherwise
1134  *    it will be deserialized to int32. If the value is out of range
1135  *    for the target type it will return an error.
1136  *  - &lt;struct&gt; will be deserialized to "a{sv}". @signature could define
1137  *    another value type (e.g. "a{ss}").
1138  *  - &lt;array&gt; will be deserialized to "av". @signature could define
1139  *    another element type (e.g. "as") or could be a tuple (e.g. "(ss)").
1140  *  - &lt;base64&gt; will be deserialized to "ay".
1141  *  - &lt;string&gt; will be deserialized to "s".
1142  *  - &lt;dateTime.iso8601&gt; will be deserialized to an unspecified variant
1143  *    type. If @signature is provided it must have the generic "v" type, which
1144  *    means there is no guarantee that it's actually a datetime that has been
1145  *    received. soup_xmlrpc_variant_get_datetime() must be used to parse and
1146  *    type check this special variant.
1147  *  - @signature must not have maybes, otherwise an error is returned.
1148  *  - Dictionaries must have string keys, otherwise an error is returned.
1149  *
1150  * Returns: (transfer full): a new (non-floating) #GVariant, or %NULL
1151  *
1152  * Since: 2.52
1153  */
1154 GVariant *
soup_xmlrpc_params_parse(SoupXMLRPCParams * self,const char * signature,GError ** error)1155 soup_xmlrpc_params_parse (SoupXMLRPCParams  *self,
1156 			  const char        *signature,
1157 			  GError           **error)
1158 {
1159 	GVariant *value = NULL;
1160 
1161 	g_return_val_if_fail (self, NULL);
1162 	g_return_val_if_fail (!signature || g_variant_type_string_is_valid (signature), NULL);
1163 
1164 	if (!self->node) {
1165 		if (!signature || g_variant_type_equal (G_VARIANT_TYPE (signature), G_VARIANT_TYPE ("av")))
1166 			value = g_variant_new_array (G_VARIANT_TYPE_VARIANT, NULL, 0);
1167 		else if (g_variant_type_equal (G_VARIANT_TYPE (signature), G_VARIANT_TYPE_UNIT))
1168 			value = g_variant_new_tuple (NULL, 0);
1169 		else {
1170 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1171 				     "Invalid signature '%s', was expecting '()'", signature);
1172 			goto fail;
1173 		}
1174 	} else {
1175 		value = parse_value (self->node, signature ? &signature : NULL, error);
1176 	}
1177 
1178 fail:
1179 	return value ? g_variant_ref_sink (value) : NULL;
1180 }
1181 
1182 /**
1183  * soup_xmlrpc_parse_request:
1184  * @method_call: the XML-RPC methodCall string
1185  * @length: the length of @method_call, or -1 if it is NUL-terminated
1186  * @params: (out): on success, a new #SoupXMLRPCParams
1187  * @error: a #GError, or %NULL
1188  *
1189  * Parses @method_call and return the method name. Method parameters can be
1190  * parsed later using soup_xmlrpc_params_parse().
1191  *
1192  * Returns: (transfer full): method's name, or %NULL on error.
1193  * Since: 2.52
1194  **/
1195 char *
soup_xmlrpc_parse_request(const char * method_call,int length,SoupXMLRPCParams ** params,GError ** error)1196 soup_xmlrpc_parse_request (const char        *method_call,
1197 			   int                length,
1198 			   SoupXMLRPCParams **params,
1199 			   GError           **error)
1200 {
1201 	xmlDoc *doc = NULL;
1202 	xmlNode *node;
1203 	xmlChar *xmlMethodName = NULL;
1204 	char *method_name = NULL;
1205 
1206 	doc = xmlParseMemory (method_call, length == -1 ? strlen (method_call) : length);
1207 	if (!doc) {
1208 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1209 			     "Could not parse XML document");
1210 		goto fail;
1211 	}
1212 
1213 	node = xmlDocGetRootElement (doc);
1214 	if (!node || strcmp ((const char *)node->name, "methodCall") != 0) {
1215 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1216 			     "<methodCall> node expected");
1217 		goto fail;
1218 	}
1219 
1220 	node = find_real_node (node->children);
1221 	if (!node || strcmp ((const char *)node->name, "methodName") != 0) {
1222 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1223 			     "<methodName> node expected");
1224 		goto fail;
1225 	}
1226 	xmlMethodName = xmlNodeGetContent (node);
1227 
1228 	if (params) {
1229 		node = find_real_node (node->next);
1230 		if (node) {
1231 			if (strcmp ((const char *)node->name, "params") != 0) {
1232 				g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1233 					     "<params> node expected");
1234 				goto fail;
1235 			}
1236 			*params = soup_xmlrpc_params_new (node);
1237 			doc = NULL;
1238 		} else {
1239 			*params = soup_xmlrpc_params_new (NULL);
1240 		}
1241 	}
1242 
1243 	method_name = g_strdup ((char *)xmlMethodName);
1244 
1245 fail:
1246 	if (doc)
1247 		xmlFreeDoc (doc);
1248 	if (xmlMethodName)
1249 		xmlFree (xmlMethodName);
1250 
1251 	return method_name;
1252 }
1253 
1254 /**
1255  * soup_xmlrpc_parse_response:
1256  * @method_response: the XML-RPC methodResponse string
1257  * @length: the length of @method_response, or -1 if it is NUL-terminated
1258  * @signature: (allow-none): A valid #GVariant type string, or %NULL
1259  * @error: a #GError, or %NULL
1260  *
1261  * Parses @method_response and returns the return value. If
1262  * @method_response is a fault, %NULL is returned, and @error
1263  * will be set to an error in the %SOUP_XMLRPC_FAULT domain, with the error
1264  * code containing the fault code, and the error message containing
1265  * the fault string. If @method_response cannot be parsed, %NULL is returned,
1266  * and @error will be set to an error in the %SOUP_XMLRPC_ERROR domain.
1267  *
1268  * See soup_xmlrpc_params_parse() for deserialization details.
1269  *
1270  * Returns: (transfer full): a new (non-floating) #GVariant, or %NULL
1271  *
1272  * Since: 2.52
1273  **/
1274 GVariant *
soup_xmlrpc_parse_response(const char * method_response,int length,const char * signature,GError ** error)1275 soup_xmlrpc_parse_response (const char *method_response,
1276 			    int length,
1277 			    const char *signature,
1278 			    GError **error)
1279 {
1280 	xmlDoc *doc = NULL;
1281 	xmlNode *node;
1282 	GVariant *value = NULL;
1283 
1284 	g_return_val_if_fail (!signature || g_variant_type_string_is_valid (signature), NULL);
1285 
1286 	doc = xmlParseMemory (method_response,
1287 				  length == -1 ? strlen (method_response) : length);
1288 	if (!doc) {
1289 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1290 			     "Failed to parse response XML");
1291 		goto fail;
1292 	}
1293 
1294 	node = xmlDocGetRootElement (doc);
1295 	if (!node || strcmp ((const char *)node->name, "methodResponse") != 0) {
1296 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1297 			     "Missing 'methodResponse' node");
1298 		goto fail;
1299 	}
1300 
1301 	node = find_real_node (node->children);
1302 	if (!node) {
1303 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1304 			     "'methodResponse' has no child");
1305 		goto fail;
1306 	}
1307 
1308 	if (!strcmp ((const char *)node->name, "fault")) {
1309 		int fault_code;
1310 		const char *fault_string;
1311 		const char *fault_sig = "a{sv}";
1312 		GVariant *fault_val;
1313 
1314 		node = find_real_node (node->children);
1315 		if (!node || strcmp ((const char *)node->name, "value") != 0) {
1316 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1317 				     "'fault' has no 'value' child");
1318 			goto fail;
1319 		}
1320 
1321 		fault_val = parse_value (node, &fault_sig, error);
1322 		if (!fault_val)
1323 			goto fail;
1324 
1325 		if (!g_variant_lookup (fault_val, "faultCode", "i", &fault_code) ||
1326 		    !g_variant_lookup (fault_val, "faultString", "&s", &fault_string))  {
1327 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1328 				     "'fault' missing 'faultCode' or 'faultString'");
1329 			goto fail;
1330 		}
1331 		g_set_error (error, SOUP_XMLRPC_FAULT,
1332 		             fault_code, "%s", fault_string);
1333 		g_variant_unref (fault_val);
1334 	} else if (!strcmp ((const char *)node->name, "params")) {
1335 		node = find_real_node (node->children);
1336 		if (!node || strcmp ((const char *)node->name, "param") != 0) {
1337 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1338 				     "'params' has no 'param' child");
1339 			goto fail;
1340 		}
1341 		node = find_real_node (node->children);
1342 		if (!node || strcmp ((const char *)node->name, "value") != 0) {
1343 			g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1344 				     "'param' has no 'value' child");
1345 			goto fail;
1346 		}
1347 		value = parse_value (node, signature ? &signature : NULL, error);
1348 	}
1349 
1350 fail:
1351 	if (doc)
1352 		xmlFreeDoc (doc);
1353 	return value ? g_variant_ref_sink (value) : NULL;
1354 }
1355 
1356 /**
1357  * soup_xmlrpc_variant_new_datetime:
1358  * @date: a #SoupDate
1359  *
1360  * Construct a special #GVariant used to serialize a &lt;dateTime.iso8601&gt;
1361  * node. See soup_xmlrpc_build_request().
1362  *
1363  * The actual type of the returned #GVariant is unspecified and "v" or "*"
1364  * should be used in variant format strings. For example:
1365  * <informalexample><programlisting>
1366  *	args = g_variant_new ("(v)", soup_xmlrpc_variant_new_datetime (date));
1367  * </programlisting></informalexample>
1368  *
1369  * Returns: a floating #GVariant.
1370  *
1371  * Since: 2.52
1372  */
1373 GVariant *
soup_xmlrpc_variant_new_datetime(SoupDate * date)1374 soup_xmlrpc_variant_new_datetime (SoupDate *date)
1375 {
1376 	GVariant *variant;
1377 	char *str;
1378 
1379 	str = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
1380 	variant = soup_xmlrpc_variant_new_custom ("dateTime.iso8601", str);
1381 	g_free (str);
1382 
1383 	return variant;
1384 }
1385 
1386 /**
1387  * soup_xmlrpc_variant_get_datetime:
1388  * @variant: a #GVariant
1389  * @error: a #GError, or %NULL
1390  *
1391  * Get the #SoupDate from special #GVariant created by
1392  * soup_xmlrpc_variant_new_datetime() or by parsing a &lt;dateTime.iso8601&gt;
1393  * node. See soup_xmlrpc_params_parse().
1394  *
1395  * If @variant does not contain a datetime it will return an error but it is not
1396  * considered a programmer error because it generally means parameters received
1397  * are not in the expected type.
1398  *
1399  * Returns: a new #SoupDate, or %NULL on error.
1400  *
1401  * Since: 2.52
1402  */
1403 SoupDate *
soup_xmlrpc_variant_get_datetime(GVariant * variant,GError ** error)1404 soup_xmlrpc_variant_get_datetime (GVariant *variant, GError **error)
1405 {
1406 	SoupDate *date = NULL;
1407 	const char *path;
1408 	const char *type;
1409 	const char *v;
1410 
1411 	if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("(oss)"))) {
1412 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1413 			     "Variant is of type '%s' which is not expected for a datetime",
1414 			     g_variant_get_type_string (variant));
1415 		return NULL;
1416 	}
1417 
1418 	g_variant_get (variant, "(&o&s&s)", &path, &type, &v);
1419 
1420 	if (!g_str_equal (path, "/org/gnome/libsoup/ExtensionType") ||
1421 	    !g_str_equal (type, "dateTime.iso8601")) {
1422 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1423 			     "Variant doesn't represent a datetime: %s",
1424 			     g_variant_get_type_string (variant));
1425 		return NULL;
1426 	}
1427 
1428 	date = soup_date_new_from_string (v);
1429 
1430 	if (date == NULL) {
1431 		g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1432 			     "Can't parse datetime string: %s", v);
1433 		return NULL;
1434 	}
1435 
1436 	return date;
1437 
1438 }
1439 
1440 /**
1441  * SOUP_XMLRPC_FAULT:
1442  *
1443  * A #GError domain representing an XML-RPC fault code. Used with
1444  * #SoupXMLRPCFault (although servers may also return fault codes not
1445  * in that enumeration).
1446  */
1447 
1448 /**
1449  * SoupXMLRPCFault:
1450  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED: request was not
1451  *   well-formed
1452  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_UNSUPPORTED_ENCODING: request was in
1453  *   an unsupported encoding
1454  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_INVALID_CHARACTER_FOR_ENCODING:
1455  *   request contained an invalid character
1456  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_XML_RPC: request was not
1457  *   valid XML-RPC
1458  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND: method
1459  *   not found
1460  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS: invalid
1461  *   parameters
1462  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INTERNAL_XML_RPC_ERROR: internal
1463  *   error
1464  * @SOUP_XMLRPC_FAULT_APPLICATION_ERROR: start of reserved range for
1465  *   application error codes
1466  * @SOUP_XMLRPC_FAULT_SYSTEM_ERROR: start of reserved range for
1467  *   system error codes
1468  * @SOUP_XMLRPC_FAULT_TRANSPORT_ERROR: start of reserved range for
1469  *   transport error codes
1470  *
1471  * Pre-defined XML-RPC fault codes from <ulink
1472  * url="http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php">http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php</ulink>.
1473  * These are an extension, not part of the XML-RPC spec; you can't
1474  * assume servers will use them.
1475  */
1476 
1477 G_DEFINE_QUARK (soup_xmlrpc_fault_quark, soup_xmlrpc_fault);
1478 G_DEFINE_QUARK (soup_xmlrpc_error_quark, soup_xmlrpc_error);
1479