• 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 (C) 2007 Red Hat, Inc.
6  */
7 
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11 
12 #include <string.h>
13 
14 #include <libxml/tree.h>
15 
16 #include "soup-xmlrpc-old.h"
17 #include "soup.h"
18 
19 /**
20  * SECTION:soup-xmlrpc
21  * @short_description: XML-RPC support
22  *
23  **/
24 
25 /* This whole file is deprecated and replaced by soup-xmlrpc.c */
26 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
27 
28 static xmlNode *find_real_node (xmlNode *node);
29 
30 static gboolean insert_value (xmlNode *parent, GValue *value);
31 
32 static gboolean
insert_value(xmlNode * parent,GValue * value)33 insert_value (xmlNode *parent, GValue *value)
34 {
35 	GType type = G_VALUE_TYPE (value);
36 	xmlNode *xvalue;
37 	char buf[128];
38 
39 	xvalue = xmlNewChild (parent, NULL, (const xmlChar *)"value", NULL);
40 
41 	if (type == G_TYPE_INT) {
42 		g_snprintf (buf, sizeof (buf), "%d", g_value_get_int (value));
43 		xmlNewChild (xvalue, NULL,
44 			     (const xmlChar *)"int",
45 			     (const xmlChar *)buf);
46 	} else if (type == G_TYPE_BOOLEAN) {
47 		g_snprintf (buf, sizeof (buf), "%d", g_value_get_boolean (value));
48 		xmlNewChild (xvalue, NULL,
49 			     (const xmlChar *)"boolean",
50 			     (const xmlChar *)buf);
51 	} else if (type == G_TYPE_STRING) {
52 		xmlNewTextChild (xvalue, NULL,
53 				 (const xmlChar *)"string",
54 				 (const xmlChar *)g_value_get_string (value));
55 	} else if (type == G_TYPE_DOUBLE) {
56 		g_ascii_dtostr (buf, sizeof (buf), g_value_get_double (value));
57 		xmlNewChild (xvalue, NULL,
58 			     (const xmlChar *)"double",
59 			     (const xmlChar *)buf);
60 	} else if (type == SOUP_TYPE_DATE) {
61 		SoupDate *date = g_value_get_boxed (value);
62 		char *timestamp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
63 		xmlNewChild (xvalue, NULL,
64 			     (const xmlChar *)"dateTime.iso8601",
65 			     (const xmlChar *)timestamp);
66 		g_free (timestamp);
67 	} else if (type == SOUP_TYPE_BYTE_ARRAY) {
68 		GByteArray *ba = g_value_get_boxed (value);
69 		char *encoded;
70 
71 		encoded = g_base64_encode (ba->data, ba->len);
72 		xmlNewChild (xvalue, NULL,
73 			     (const xmlChar *)"base64",
74 			     (const xmlChar *)encoded);
75 		g_free (encoded);
76 	} else if (type == G_TYPE_HASH_TABLE) {
77 		GHashTable *hash = g_value_get_boxed (value);
78 		GHashTableIter iter;
79 		gpointer mname, mvalue;
80 		xmlNode *struct_node, *member;
81 
82 		struct_node = xmlNewChild (xvalue, NULL,
83 					   (const xmlChar *)"struct", NULL);
84 
85 		g_hash_table_iter_init (&iter, hash);
86 
87 		while (g_hash_table_iter_next (&iter, &mname, &mvalue)) {
88 			member = xmlNewChild (struct_node, NULL,
89 					      (const xmlChar *)"member", NULL);
90 			xmlNewTextChild (member, NULL,
91 					 (const xmlChar *)"name",
92 					 (const xmlChar *)mname);
93 			if (!insert_value (member, mvalue)) {
94 				xmlFreeNode (struct_node);
95 				struct_node = NULL;
96 				break;
97 			}
98 		}
99 
100 		if (!struct_node)
101 			return FALSE;
102 	} else if (type == G_TYPE_VALUE_ARRAY) {
103 		GValueArray *va = g_value_get_boxed (value);
104 		xmlNode *node;
105 		guint i;
106 
107 		node = xmlNewChild (xvalue, NULL,
108 				    (const xmlChar *)"array", NULL);
109 		node = xmlNewChild (node, NULL,
110 				    (const xmlChar *)"data", NULL);
111 		for (i = 0; i < va->n_values; i++) {
112 			if (!insert_value (node, &va->values[i]))
113 				return FALSE;
114 		}
115 	} else
116 		return FALSE;
117 
118 	return TRUE;
119 }
120 
121 /**
122  * soup_xmlrpc_build_method_call:
123  * @method_name: the name of the XML-RPC method
124  * @params: (array length=n_params): arguments to @method
125  * @n_params: length of @params
126  *
127  * This creates an XML-RPC methodCall and returns it as a string.
128  * This is the low-level method that soup_xmlrpc_request_new() is
129  * built on.
130  *
131  * @params is an array of #GValue representing the parameters to
132  * @method. (It is *not* a #GValueArray, although if you have a
133  * #GValueArray, you can just pass its <literal>values</literal>f and
134  * <literal>n_values</literal> fields.)
135  *
136  * The correspondence between glib types and XML-RPC types is:
137  *
138  *   int: #int (%G_TYPE_INT)
139  *   boolean: #gboolean (%G_TYPE_BOOLEAN)
140  *   string: #char* (%G_TYPE_STRING)
141  *   double: #double (%G_TYPE_DOUBLE)
142  *   datetime.iso8601: #SoupDate (%SOUP_TYPE_DATE)
143  *   base64: #GByteArray (%SOUP_TYPE_BYTE_ARRAY)
144  *   struct: #GHashTable (%G_TYPE_HASH_TABLE)
145  *   array: #GValueArray (%G_TYPE_VALUE_ARRAY)
146  *
147  * For structs, use a #GHashTable that maps strings to #GValue;
148  * soup_value_hash_new() and related methods can help with this.
149  *
150  * Return value: (nullable): the text of the methodCall, or %NULL on
151  * error
152  *
153  * Deprecated: Use soup_xmlrpc_build_request() instead.
154  **/
155 char *
soup_xmlrpc_build_method_call(const char * method_name,GValue * params,int n_params)156 soup_xmlrpc_build_method_call (const char *method_name,
157 			       GValue *params, int n_params)
158 {
159 	xmlDoc *doc;
160 	xmlNode *node, *param;
161 	xmlChar *xmlbody;
162 	int i, len;
163 	char *body;
164 
165 	doc = xmlNewDoc ((const xmlChar *)"1.0");
166 	doc->standalone = FALSE;
167 	doc->encoding = xmlCharStrdup ("UTF-8");
168 
169 	node = xmlNewDocNode (doc, NULL, (const xmlChar *)"methodCall", NULL);
170 	xmlDocSetRootElement (doc, node);
171 	xmlNewChild (node, NULL, (const xmlChar *)"methodName",
172 		     (const xmlChar *)method_name);
173 
174 	node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
175 	for (i = 0; i < n_params; i++) {
176 		param  = xmlNewChild (node, NULL,
177 				      (const xmlChar *)"param", NULL);
178 		if (!insert_value (param, &params[i])) {
179 			xmlFreeDoc (doc);
180 			return NULL;
181 		}
182 	}
183 
184 	xmlDocDumpMemory (doc, &xmlbody, &len);
185 	body = g_strndup ((char *)xmlbody, len);
186 	xmlFree (xmlbody);
187 	xmlFreeDoc (doc);
188 	return body;
189 }
190 
191 static SoupMessage *
soup_xmlrpc_request_newv(const char * uri,const char * method_name,va_list args)192 soup_xmlrpc_request_newv (const char *uri, const char *method_name, va_list args)
193 {
194 	SoupMessage *msg;
195 	GValueArray *params;
196 	char *body;
197 
198 	params = soup_value_array_from_args (args);
199 	if (!params)
200 		return NULL;
201 
202 	body = soup_xmlrpc_build_method_call (method_name, params->values,
203 					      params->n_values);
204 	g_value_array_free (params);
205 
206 	if (!body)
207 		return NULL;
208 
209 	msg = soup_message_new ("POST", uri);
210 	soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
211 				  body, strlen (body));
212 	return msg;
213 }
214 
215 /**
216  * soup_xmlrpc_request_new:
217  * @uri: URI of the XML-RPC service
218  * @method_name: the name of the XML-RPC method to invoke at @uri
219  * @...: parameters for @method
220  *
221  * Creates an XML-RPC methodCall and returns a #SoupMessage, ready
222  * to send, for that method call.
223  *
224  * The parameters are passed as type/value pairs; ie, first a #GType,
225  * and then a value of the appropriate type, finally terminated by
226  * %G_TYPE_INVALID.
227  *
228  * Return value: (transfer full): a #SoupMessage encoding the
229  * indicated XML-RPC request.
230  *
231  * Deprecated: Use soup_xmlrpc_message_new() instead.
232  **/
233 SoupMessage *
soup_xmlrpc_request_new(const char * uri,const char * method_name,...)234 soup_xmlrpc_request_new (const char *uri, const char *method_name, ...)
235 {
236 	SoupMessage *msg;
237 	va_list args;
238 
239 	va_start (args, method_name);
240 	msg = soup_xmlrpc_request_newv (uri, method_name, args);
241 	va_end (args);
242 	return msg;
243 }
244 
245 /**
246  * soup_xmlrpc_build_method_response:
247  * @value: the return value
248  *
249  * This creates a (successful) XML-RPC methodResponse and returns it
250  * as a string. To create a fault response, use
251  * soup_xmlrpc_build_fault().
252  *
253  * The glib type to XML-RPC type mapping is as with
254  * soup_xmlrpc_build_method_call(), qv.
255  *
256  * Return value: (nullable): the text of the methodResponse, or %NULL
257  * on error
258  *
259  * Deprecated: Use soup_xmlrpc_build_response() instead.
260  **/
261 char *
soup_xmlrpc_build_method_response(GValue * value)262 soup_xmlrpc_build_method_response (GValue *value)
263 {
264 	xmlDoc *doc;
265 	xmlNode *node;
266 	xmlChar *xmlbody;
267 	char *body;
268 	int len;
269 
270 	doc = xmlNewDoc ((const xmlChar *)"1.0");
271 	doc->standalone = FALSE;
272 	doc->encoding = xmlCharStrdup ("UTF-8");
273 
274 	node = xmlNewDocNode (doc, NULL,
275 			      (const xmlChar *)"methodResponse", NULL);
276 	xmlDocSetRootElement (doc, node);
277 
278 	node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
279 	node = xmlNewChild (node, NULL, (const xmlChar *)"param", NULL);
280 	if (!insert_value (node, value)) {
281 		xmlFreeDoc (doc);
282 		return NULL;
283 	}
284 
285 	xmlDocDumpMemory (doc, &xmlbody, &len);
286 	body = g_strndup ((char *)xmlbody, len);
287 	xmlFree (xmlbody);
288 	xmlFreeDoc (doc);
289 	return body;
290 }
291 
292 /**
293  * soup_xmlrpc_set_response:
294  * @msg: an XML-RPC request
295  * @type: the type of the response value
296  * @...: the response value
297  *
298  * Sets the status code and response body of @msg to indicate a
299  * successful XML-RPC call, with a return value given by @type and the
300  * following varargs argument, of the type indicated by @type.
301  *
302  * Deprecated: Use soup_xmlrpc_message_set_response() instead.
303  **/
304 void
soup_xmlrpc_set_response(SoupMessage * msg,GType type,...)305 soup_xmlrpc_set_response (SoupMessage *msg, GType type, ...)
306 {
307 	va_list args;
308 	GValue value;
309 	char *body;
310 
311 	va_start (args, type);
312 	SOUP_VALUE_SETV (&value, type, args);
313 	va_end (args);
314 
315 	body = soup_xmlrpc_build_method_response (&value);
316 	g_value_unset (&value);
317 	soup_message_set_status (msg, SOUP_STATUS_OK);
318 	soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
319 				   body, strlen (body));
320 }
321 
322 char *soup_xmlrpc_build_faultv (int         fault_code,
323 				const char *fault_format,
324 				va_list     args) G_GNUC_PRINTF (2, 0);
325 
326 /**
327  * soup_xmlrpc_set_fault:
328  * @msg: an XML-RPC request
329  * @fault_code: the fault code
330  * @fault_format: a printf()-style format string
331  * @...: the parameters to @fault_format
332  *
333  * Sets the status code and response body of @msg to indicate an
334  * unsuccessful XML-RPC call, with the error described by @fault_code
335  * and @fault_format.
336  *
337  * Deprecated: Use soup_xmlrpc_message_set_fault() instead.
338  **/
339 void
soup_xmlrpc_set_fault(SoupMessage * msg,int fault_code,const char * fault_format,...)340 soup_xmlrpc_set_fault (SoupMessage *msg, int fault_code,
341 		       const char *fault_format, ...)
342 {
343 	va_list args;
344 	char *body;
345 
346 	va_start (args, fault_format);
347 	body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
348 	va_end (args);
349 
350 	soup_message_set_status (msg, SOUP_STATUS_OK);
351 	soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
352 				   body, strlen (body));
353 }
354 
355 
356 
357 static gboolean
parse_value(xmlNode * xmlvalue,GValue * value)358 parse_value (xmlNode *xmlvalue, GValue *value)
359 {
360 	xmlNode *typenode;
361 	const char *typename;
362 	xmlChar *content;
363 
364 	memset (value, 0, sizeof (GValue));
365 
366 	typenode = find_real_node (xmlvalue->children);
367 	if (!typenode) {
368 		/* If no type node, it's a string */
369 		content = xmlNodeGetContent (typenode);
370 		g_value_init (value, G_TYPE_STRING);
371 		g_value_set_string (value, (char *)content);
372 		xmlFree (content);
373 		return TRUE;
374 	}
375 
376 	typename = (const char *)typenode->name;
377 
378 	if (!strcmp (typename, "i4") || !strcmp (typename, "int")) {
379 		content = xmlNodeGetContent (typenode);
380 		g_value_init (value, G_TYPE_INT);
381 		g_value_set_int (value, atoi ((char *)content));
382 		xmlFree (content);
383 	} else if (!strcmp (typename, "boolean")) {
384 		content = xmlNodeGetContent (typenode);
385 		g_value_init (value, G_TYPE_BOOLEAN);
386 		g_value_set_boolean (value, atoi ((char *)content));
387 		xmlFree (content);
388 	} else if (!strcmp (typename, "string")) {
389 		content = xmlNodeGetContent (typenode);
390 		g_value_init (value, G_TYPE_STRING);
391 		g_value_set_string (value, (char *)content);
392 		xmlFree (content);
393 	} else if (!strcmp (typename, "double")) {
394 		content = xmlNodeGetContent (typenode);
395 		g_value_init (value, G_TYPE_DOUBLE);
396 		g_value_set_double (value, g_ascii_strtod ((char *)content, NULL));
397 		xmlFree (content);
398 	} else if (!strcmp (typename, "dateTime.iso8601")) {
399 		content = xmlNodeGetContent (typenode);
400 		g_value_init (value, SOUP_TYPE_DATE);
401 		g_value_take_boxed (value, soup_date_new_from_string ((char *)content));
402 		xmlFree (content);
403 	} else if (!strcmp (typename, "base64")) {
404 		GByteArray *ba;
405 		guchar *decoded;
406 		gsize len;
407 
408 		content = xmlNodeGetContent (typenode);
409 		decoded = g_base64_decode ((char *)content, &len);
410 		ba = g_byte_array_sized_new (len);
411 		g_byte_array_append (ba, decoded, len);
412 		g_free (decoded);
413 		xmlFree (content);
414 		g_value_init (value, SOUP_TYPE_BYTE_ARRAY);
415 		g_value_take_boxed (value, ba);
416 	} else if (!strcmp (typename, "struct")) {
417 		xmlNode *member, *child, *mname, *mxval;
418 		GHashTable *hash;
419 		GValue mgval;
420 
421 		hash = soup_value_hash_new ();
422 		for (member = find_real_node (typenode->children);
423 		     member;
424 		     member = find_real_node (member->next)) {
425 			if (strcmp ((const char *)member->name, "member") != 0) {
426 				g_hash_table_destroy (hash);
427 				return FALSE;
428 			}
429 			mname = mxval = NULL;
430 			memset (&mgval, 0, sizeof (mgval));
431 
432 			for (child = find_real_node (member->children);
433 			     child;
434 			     child = find_real_node (child->next)) {
435 				if (!strcmp ((const char *)child->name, "name"))
436 					mname = child;
437 				else if (!strcmp ((const char *)child->name, "value"))
438 					mxval = child;
439 				else
440 					break;
441 			}
442 
443 			if (!mname || !mxval || !parse_value (mxval, &mgval)) {
444 				g_hash_table_destroy (hash);
445 				return FALSE;
446 			}
447 
448 			content = xmlNodeGetContent (mname);
449 			soup_value_hash_insert_value (hash, (char *)content, &mgval);
450 			xmlFree (content);
451 			g_value_unset (&mgval);
452 		}
453 		g_value_init (value, G_TYPE_HASH_TABLE);
454 		g_value_take_boxed (value, hash);
455 	} else if (!strcmp (typename, "array")) {
456 		xmlNode *data, *xval;
457 		GValueArray *array;
458 		GValue gval;
459 
460 		data = find_real_node (typenode->children);
461 		if (!data || strcmp ((const char *)data->name, "data") != 0)
462 			return FALSE;
463 
464 		array = g_value_array_new (1);
465 		for (xval = find_real_node (data->children);
466 		     xval;
467 		     xval = find_real_node (xval->next)) {
468 			memset (&gval, 0, sizeof (gval));
469 			if (strcmp ((const char *)xval->name, "value") != 0 ||
470 			    !parse_value (xval, &gval)) {
471 				g_value_array_free (array);
472 				return FALSE;
473 			}
474 
475 			g_value_array_append (array, &gval);
476 			g_value_unset (&gval);
477 		}
478 		g_value_init (value, G_TYPE_VALUE_ARRAY);
479 		g_value_take_boxed (value, array);
480 	} else
481 		return FALSE;
482 
483 	return TRUE;
484 }
485 
486 /**
487  * soup_xmlrpc_parse_method_call:
488  * @method_call: the XML-RPC methodCall string
489  * @length: the length of @method_call, or -1 if it is NUL-terminated
490  * @method_name: (out): on return, the methodName from @method_call
491  * @params: (out): on return, the parameters from @method_call
492  *
493  * Parses @method_call to get the name and parameters, and returns the
494  * parameter values in a #GValueArray; see also
495  * soup_xmlrpc_extract_method_call(), which is more convenient if you
496  * know in advance what the types of the parameters will be.
497  *
498  * Return value: success or failure.
499  *
500  * Deprecated: Use soup_xmlrpc_parse_request_full() instead.
501  **/
502 gboolean
soup_xmlrpc_parse_method_call(const char * method_call,int length,char ** method_name,GValueArray ** params)503 soup_xmlrpc_parse_method_call (const char *method_call, int length,
504 			       char **method_name, GValueArray **params)
505 {
506 	xmlDoc *doc;
507 	xmlNode *node, *param, *xval;
508 	xmlChar *xmlMethodName = NULL;
509 	gboolean success = FALSE;
510 	GValue value;
511 
512 	doc = xmlParseMemory (method_call,
513 			      length == -1 ? strlen (method_call) : length);
514 	if (!doc)
515 		return FALSE;
516 
517 	node = xmlDocGetRootElement (doc);
518 	if (!node || strcmp ((const char *)node->name, "methodCall") != 0)
519 		goto fail;
520 
521 	node = find_real_node (node->children);
522 	if (!node || strcmp ((const char *)node->name, "methodName") != 0)
523 		goto fail;
524 	xmlMethodName = xmlNodeGetContent (node);
525 
526 	node = find_real_node (node->next);
527 	if (node) {
528 		if (strcmp ((const char *)node->name, "params") != 0)
529 			goto fail;
530 
531 		*params = soup_value_array_new ();
532 		param = find_real_node (node->children);
533 		while (param && !strcmp ((const char *)param->name, "param")) {
534 			xval = find_real_node (param->children);
535 			if (!xval || strcmp ((const char *)xval->name, "value") != 0 ||
536 			    !parse_value (xval, &value)) {
537 				g_value_array_free (*params);
538 				goto fail;
539 			}
540 			g_value_array_append (*params, &value);
541 			g_value_unset (&value);
542 
543 			param = find_real_node (param->next);
544 		}
545 	} else
546 		*params = soup_value_array_new ();
547 
548 	success = TRUE;
549 	*method_name = g_strdup ((char *)xmlMethodName);
550 
551 fail:
552 	xmlFreeDoc (doc);
553 	if (xmlMethodName)
554 		xmlFree (xmlMethodName);
555 	return success;
556 }
557 
558 /**
559  * soup_xmlrpc_extract_method_call:
560  * @method_call: the XML-RPC methodCall string
561  * @length: the length of @method_call, or -1 if it is NUL-terminated
562  * @method_name: (out): on return, the methodName from @method_call
563  * @...: return types and locations for parameters
564  *
565  * Parses @method_call to get the name and parameters, and puts
566  * the parameters into variables of the appropriate types.
567  *
568  * The parameters are handled similarly to
569  * @soup_xmlrpc_build_method_call, with pairs of types and values,
570  * terminated by %G_TYPE_INVALID, except that values are pointers to
571  * variables of the indicated type, rather than values of the type.
572  *
573  * See also soup_xmlrpc_parse_method_call(), which can be used if
574  * you don't know the types of the parameters.
575  *
576  * Return value: success or failure.
577  *
578  * Deprecated: Use soup_xmlrpc_parse_request_full() instead.
579  **/
580 gboolean
soup_xmlrpc_extract_method_call(const char * method_call,int length,char ** method_name,...)581 soup_xmlrpc_extract_method_call (const char *method_call, int length,
582 				 char **method_name, ...)
583 {
584 	GValueArray *params;
585 	gboolean success;
586 	va_list args;
587 
588 	if (!soup_xmlrpc_parse_method_call (method_call, length,
589 					    method_name, &params))
590 		return FALSE;
591 
592 	va_start (args, method_name);
593 	success = soup_value_array_to_args (params, args);
594 	va_end (args);
595 
596 	g_value_array_free (params);
597 
598 	return success;
599 }
600 
601 /**
602  * soup_xmlrpc_parse_method_response:
603  * @method_response: the XML-RPC methodResponse string
604  * @length: the length of @method_response, or -1 if it is NUL-terminated
605  * @value: (out): on return, the return value from @method_call
606  * @error: error return value
607  *
608  * Parses @method_response and returns the return value in @value. If
609  * @method_response is a fault, @value will be unchanged, and @error
610  * will be set to an error of type %SOUP_XMLRPC_FAULT, with the error
611  * #code containing the fault code, and the error #message containing
612  * the fault string. (If @method_response cannot be parsed at all,
613  * soup_xmlrpc_parse_method_response() will return %FALSE, but @error
614  * will be unset.)
615  *
616  * Return value: %TRUE if a return value was parsed, %FALSE if the
617  * response could not be parsed, or contained a fault.
618  *
619  * Deprecated: Use soup_xmlrpc_parse_response() instead.
620  **/
621 gboolean
soup_xmlrpc_parse_method_response(const char * method_response,int length,GValue * value,GError ** error)622 soup_xmlrpc_parse_method_response (const char *method_response, int length,
623 				   GValue *value, GError **error)
624 {
625 	xmlDoc *doc;
626 	xmlNode *node;
627 	gboolean success = FALSE;
628 
629 	doc = xmlParseMemory (method_response,
630 			      length == -1 ? strlen (method_response) : length);
631 	if (!doc)
632 		return FALSE;
633 
634 	node = xmlDocGetRootElement (doc);
635 	if (!node || strcmp ((const char *)node->name, "methodResponse") != 0)
636 		goto fail;
637 
638 	node = find_real_node (node->children);
639 	if (!node)
640 		goto fail;
641 
642 	if (!strcmp ((const char *)node->name, "fault") && error) {
643 		int fault_code;
644 		char *fault_string;
645 		GValue fault_val;
646 		GHashTable *fault_hash;
647 
648 		node = find_real_node (node->children);
649 		if (!node || strcmp ((const char *)node->name, "value") != 0)
650 			goto fail;
651 		if (!parse_value (node, &fault_val))
652 			goto fail;
653 		if (!G_VALUE_HOLDS (&fault_val, G_TYPE_HASH_TABLE)) {
654 			g_value_unset (&fault_val);
655 			goto fail;
656 		}
657 		fault_hash = g_value_get_boxed (&fault_val);
658 		if (!soup_value_hash_lookup (fault_hash, "faultCode",
659 					     G_TYPE_INT, &fault_code) ||
660 		    !soup_value_hash_lookup (fault_hash, "faultString",
661 					     G_TYPE_STRING, &fault_string)) {
662 			g_value_unset (&fault_val);
663 			goto fail;
664 		}
665 
666 		g_set_error (error, SOUP_XMLRPC_FAULT,
667 			     fault_code, "%s", fault_string);
668 		g_value_unset (&fault_val);
669 	} else if (!strcmp ((const char *)node->name, "params")) {
670 		node = find_real_node (node->children);
671 		if (!node || strcmp ((const char *)node->name, "param") != 0)
672 			goto fail;
673 		node = find_real_node (node->children);
674 		if (!node || strcmp ((const char *)node->name, "value") != 0)
675 			goto fail;
676 		if (!parse_value (node, value))
677 			goto fail;
678 		success = TRUE;
679 	}
680 
681 fail:
682 	xmlFreeDoc (doc);
683 	return success;
684 }
685 
686 /**
687  * soup_xmlrpc_extract_method_response:
688  * @method_response: the XML-RPC methodResponse string
689  * @length: the length of @method_response, or -1 if it is NUL-terminated
690  * @error: error return value
691  * @type: the expected type of the return value
692  * @...: location for return value
693  *
694  * Parses @method_response and extracts the return value into
695  * a variable of the correct type.
696  *
697  * If @method_response is a fault, the return value will be unset,
698  * and @error will be set to an error of type %SOUP_XMLRPC_FAULT, with
699  * the error #code containing the fault code, and the error #message
700  * containing the fault string. (If @method_response cannot be parsed
701  * at all, soup_xmlrpc_extract_method_response() will return %FALSE,
702  * but @error will be unset.)
703  *
704  * Return value: %TRUE if a return value was parsed, %FALSE if the
705  * response was of the wrong type, or contained a fault.
706  *
707  * Deprecated: Use soup_xmlrpc_parse_response() instead.
708  **/
709 gboolean
soup_xmlrpc_extract_method_response(const char * method_response,int length,GError ** error,GType type,...)710 soup_xmlrpc_extract_method_response (const char *method_response, int length,
711 				     GError **error, GType type, ...)
712 {
713 	GValue value;
714 	va_list args;
715 
716 	if (!soup_xmlrpc_parse_method_response (method_response, length,
717 						&value, error))
718 		return FALSE;
719 	if (!G_VALUE_HOLDS (&value, type))
720 		return FALSE;
721 
722 	va_start (args, type);
723 	SOUP_VALUE_GETV (&value, type, args);
724 	va_end (args);
725 
726 	return TRUE;
727 }
728 
729 static xmlNode *
find_real_node(xmlNode * node)730 find_real_node (xmlNode *node)
731 {
732 	while (node && (node->type == XML_COMMENT_NODE ||
733 			xmlIsBlankNode (node)))
734 		node = node->next;
735 	return node;
736 }
737 
738 G_GNUC_END_IGNORE_DEPRECATIONS
739