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