soup-auth-manager: add soup_auth_manager_use_auth()
[platform/upstream/libsoup.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
14 #include <libxml/tree.h>
15
16 #include "soup-xmlrpc.h"
17 #include "soup.h"
18
19 /**
20  * SECTION:soup-xmlrpc
21  * @short_description: XML-RPC support
22  *
23  **/
24
25 static xmlNode *find_real_node (xmlNode *node);
26
27 static gboolean insert_value (xmlNode *parent, GValue *value);
28
29 static gboolean
30 insert_value (xmlNode *parent, GValue *value)
31 {
32         GType type = G_VALUE_TYPE (value);
33         xmlNode *xvalue;
34         char buf[128];
35
36         xvalue = xmlNewChild (parent, NULL, (const xmlChar *)"value", NULL);
37
38         if (type == G_TYPE_INT) {
39                 snprintf (buf, sizeof (buf), "%d", g_value_get_int (value));
40                 xmlNewChild (xvalue, NULL,
41                              (const xmlChar *)"int",
42                              (const xmlChar *)buf);
43         } else if (type == G_TYPE_BOOLEAN) {
44                 snprintf (buf, sizeof (buf), "%d", g_value_get_boolean (value));
45                 xmlNewChild (xvalue, NULL,
46                              (const xmlChar *)"boolean",
47                              (const xmlChar *)buf);
48         } else if (type == G_TYPE_STRING) {
49                 xmlNewTextChild (xvalue, NULL,
50                                  (const xmlChar *)"string",
51                                  (const xmlChar *)g_value_get_string (value));
52         } else if (type == G_TYPE_DOUBLE) {
53                 g_ascii_dtostr (buf, sizeof (buf), g_value_get_double (value));
54                 xmlNewChild (xvalue, NULL,
55                              (const xmlChar *)"double",
56                              (const xmlChar *)buf);
57         } else if (type == SOUP_TYPE_DATE) {
58                 SoupDate *date = g_value_get_boxed (value);
59                 char *timestamp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
60                 xmlNewChild (xvalue, NULL,
61                              (const xmlChar *)"dateTime.iso8601",
62                              (const xmlChar *)timestamp);
63                 g_free (timestamp);
64         } else if (type == SOUP_TYPE_BYTE_ARRAY) {
65                 GByteArray *ba = g_value_get_boxed (value);
66                 char *encoded;
67
68                 encoded = g_base64_encode (ba->data, ba->len);
69                 xmlNewChild (xvalue, NULL,
70                              (const xmlChar *)"base64",
71                              (const xmlChar *)encoded);
72                 g_free (encoded);
73         } else if (type == G_TYPE_HASH_TABLE) {
74                 GHashTable *hash = g_value_get_boxed (value);
75                 GHashTableIter iter;
76                 gpointer mname, mvalue;
77                 xmlNode *struct_node, *member;
78
79                 struct_node = xmlNewChild (xvalue, NULL,
80                                            (const xmlChar *)"struct", NULL);
81
82                 g_hash_table_iter_init (&iter, hash);
83
84                 while (g_hash_table_iter_next (&iter, &mname, &mvalue)) {
85                         member = xmlNewChild (struct_node, NULL,
86                                               (const xmlChar *)"member", NULL);
87                         xmlNewTextChild (member, NULL,
88                                          (const xmlChar *)"name",
89                                          (const xmlChar *)mname);
90                         if (!insert_value (member, mvalue)) {
91                                 xmlFreeNode (struct_node);
92                                 struct_node = NULL;
93                                 break;
94                         }
95                 }
96
97                 if (!struct_node)
98                         return FALSE;
99 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
100 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
101 #endif
102         } else if (type == G_TYPE_VALUE_ARRAY) {
103 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
104 G_GNUC_END_IGNORE_DEPRECATIONS
105 #endif
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() is
132  * 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 <literal>values</literal>f and
137  * <literal>n_values</literal> fields.)
138  *
139  * The correspondence between glib types and XML-RPC types is:
140  *
141  *   int: #int (%G_TYPE_INT)
142  *   boolean: #gboolean (%G_TYPE_BOOLEAN)
143  *   string: #char* (%G_TYPE_STRING)
144  *   double: #double (%G_TYPE_DOUBLE)
145  *   datetime.iso8601: #SoupDate (%SOUP_TYPE_DATE)
146  *   base64: #GByteArray (%SOUP_TYPE_BYTE_ARRAY)
147  *   struct: #GHashTable (%G_TYPE_HASH_TABLE)
148  *   array: #GValueArray (%G_TYPE_VALUE_ARRAY)
149  *
150  * For structs, use a #GHashTable that maps strings to #GValue;
151  * soup_value_hash_new() and related methods can help with this.
152  *
153  * Return value: the text of the methodCall, or %NULL on error
154  **/
155 char *
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 *
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 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
205 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
206 #endif
207         g_value_array_free (params);
208 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
209 G_GNUC_END_IGNORE_DEPRECATIONS
210 #endif
211         if (!body)
212                 return NULL;
213
214         msg = soup_message_new ("POST", uri);
215         soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
216                                   body, strlen (body));
217         return msg;
218 }
219
220 /**
221  * soup_xmlrpc_request_new:
222  * @uri: URI of the XML-RPC service
223  * @method_name: the name of the XML-RPC method to invoke at @uri
224  * @...: parameters for @method
225  *
226  * Creates an XML-RPC methodCall and returns a #SoupMessage, ready
227  * to send, for that method call.
228  *
229  * The parameters are passed as type/value pairs; ie, first a #GType,
230  * and then a value of the appropriate type, finally terminated by
231  * %G_TYPE_INVALID.
232  *
233  * Return value: (transfer full): a #SoupMessage encoding the
234  * indicated XML-RPC request.
235  **/
236 SoupMessage *
237 soup_xmlrpc_request_new (const char *uri, const char *method_name, ...)
238 {
239         SoupMessage *msg;
240         va_list args;
241
242         va_start (args, method_name);
243         msg = soup_xmlrpc_request_newv (uri, method_name, args);
244         va_end (args);
245         return msg;
246 }
247
248 /**
249  * soup_xmlrpc_build_method_response:
250  * @value: the return value
251  *
252  * This creates a (successful) XML-RPC methodResponse and returns it
253  * as a string. To create a fault response, use
254  * soup_xmlrpc_build_fault().
255  *
256  * The glib type to XML-RPC type mapping is as with
257  * soup_xmlrpc_build_method_call(), qv.
258  *
259  * Return value: the text of the methodResponse, or %NULL on error
260  **/
261 char *
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 static char *
293 soup_xmlrpc_build_faultv (int fault_code, const char *fault_format, va_list args)
294 {
295         xmlDoc *doc;
296         xmlNode *node, *member;
297         GValue value;
298         xmlChar *xmlbody;
299         char *fault_string, *body;
300         int len;
301
302         fault_string = g_strdup_vprintf (fault_format, args);
303
304         doc = xmlNewDoc ((const xmlChar *)"1.0");
305         doc->standalone = FALSE;
306         doc->encoding = xmlCharStrdup ("UTF-8");
307
308         node = xmlNewDocNode (doc, NULL,
309                               (const xmlChar *)"methodResponse", NULL);
310         xmlDocSetRootElement (doc, node);
311         node = xmlNewChild (node, NULL, (const xmlChar *)"fault", NULL);
312         node = xmlNewChild (node, NULL, (const xmlChar *)"value", NULL);
313         node = xmlNewChild (node, NULL, (const xmlChar *)"struct", NULL);
314
315         memset (&value, 0, sizeof (value));
316
317         member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
318         xmlNewChild (member, NULL,
319                      (const xmlChar *)"name", (const xmlChar *)"faultCode");
320         g_value_init (&value, G_TYPE_INT);
321         g_value_set_int (&value, fault_code);
322         insert_value (member, &value);
323         g_value_unset (&value);
324
325         member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
326         xmlNewChild (member, NULL,
327                      (const xmlChar *)"name", (const xmlChar *)"faultString");
328         g_value_init (&value, G_TYPE_STRING);
329         g_value_take_string (&value, fault_string);
330         insert_value (member, &value);
331         g_value_unset (&value);
332
333         xmlDocDumpMemory (doc, &xmlbody, &len);
334         body = g_strndup ((char *)xmlbody, len);
335         xmlFree (xmlbody);
336         xmlFreeDoc (doc);
337
338         return body;
339 }
340
341 /**
342  * soup_xmlrpc_build_fault:
343  * @fault_code: the fault code
344  * @fault_format: a printf()-style format string
345  * @...: the parameters to @fault_format
346  *
347  * This creates an XML-RPC fault response and returns it as a string.
348  * (To create a successful response, use
349  * soup_xmlrpc_build_method_response().)
350  *
351  * Return value: the text of the fault
352  **/
353 char *
354 soup_xmlrpc_build_fault (int fault_code, const char *fault_format, ...)
355 {
356         va_list args;
357         char *body;
358
359         va_start (args, fault_format);
360         body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
361         va_end (args);
362         return body;
363 }
364
365 /**
366  * soup_xmlrpc_set_response:
367  * @msg: an XML-RPC request
368  * @type: the type of the response value
369  * @...: the response value
370  *
371  * Sets the status code and response body of @msg to indicate a
372  * successful XML-RPC call, with a return value given by @type and the
373  * following varargs argument, of the type indicated by @type.
374  **/
375 void
376 soup_xmlrpc_set_response (SoupMessage *msg, GType type, ...)
377 {
378         va_list args;
379         GValue value;
380         char *body;
381
382         va_start (args, type);
383         SOUP_VALUE_SETV (&value, type, args);
384         va_end (args);
385
386         body = soup_xmlrpc_build_method_response (&value);
387         g_value_unset (&value);
388         soup_message_set_status (msg, SOUP_STATUS_OK);
389         soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
390                                    body, strlen (body));
391 }
392
393 /**
394  * soup_xmlrpc_set_fault:
395  * @msg: an XML-RPC request
396  * @fault_code: the fault code
397  * @fault_format: a printf()-style format string
398  * @...: the parameters to @fault_format
399  *
400  * Sets the status code and response body of @msg to indicate an
401  * unsuccessful XML-RPC call, with the error described by @fault_code
402  * and @fault_format.
403  **/
404 void
405 soup_xmlrpc_set_fault (SoupMessage *msg, int fault_code,
406                        const char *fault_format, ...)
407 {
408         va_list args;
409         char *body;
410
411         va_start (args, fault_format);
412         body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
413         va_end (args);
414
415         soup_message_set_status (msg, SOUP_STATUS_OK);
416         soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
417                                    body, strlen (body));
418 }
419
420
421
422 static gboolean
423 parse_value (xmlNode *xmlvalue, GValue *value)
424 {
425         xmlNode *typenode;
426         const char *typename;
427         xmlChar *content;
428
429         memset (value, 0, sizeof (GValue));
430
431         typenode = find_real_node (xmlvalue->children);
432         if (!typenode) {
433                 /* If no type node, it's a string */
434                 content = xmlNodeGetContent (typenode);
435                 g_value_init (value, G_TYPE_STRING);
436                 g_value_set_string (value, (char *)content);
437                 xmlFree (content);
438                 return TRUE;
439         }
440
441         typename = (const char *)typenode->name;
442
443         if (!strcmp (typename, "i4") || !strcmp (typename, "int")) {
444                 content = xmlNodeGetContent (typenode);
445                 g_value_init (value, G_TYPE_INT);
446                 g_value_set_int (value, atoi ((char *)content));
447                 xmlFree (content);
448         } else if (!strcmp (typename, "boolean")) {
449                 content = xmlNodeGetContent (typenode);
450                 g_value_init (value, G_TYPE_BOOLEAN);
451                 g_value_set_boolean (value, atoi ((char *)content));
452                 xmlFree (content);
453         } else if (!strcmp (typename, "string")) {
454                 content = xmlNodeGetContent (typenode);
455                 g_value_init (value, G_TYPE_STRING);
456                 g_value_set_string (value, (char *)content);
457                 xmlFree (content);
458         } else if (!strcmp (typename, "double")) {
459                 content = xmlNodeGetContent (typenode);
460                 g_value_init (value, G_TYPE_DOUBLE);
461                 g_value_set_double (value, g_ascii_strtod ((char *)content, NULL));
462                 xmlFree (content);
463         } else if (!strcmp (typename, "dateTime.iso8601")) {
464                 content = xmlNodeGetContent (typenode);
465                 g_value_init (value, SOUP_TYPE_DATE);
466                 g_value_take_boxed (value, soup_date_new_from_string ((char *)content));
467                 xmlFree (content);
468         } else if (!strcmp (typename, "base64")) {
469                 GByteArray *ba;
470                 guchar *decoded;
471                 gsize len;
472
473                 content = xmlNodeGetContent (typenode);
474                 decoded = g_base64_decode ((char *)content, &len);
475                 ba = g_byte_array_sized_new (len);
476                 g_byte_array_append (ba, decoded, len);
477                 g_free (decoded);
478                 xmlFree (content);
479                 g_value_init (value, SOUP_TYPE_BYTE_ARRAY);
480                 g_value_take_boxed (value, ba);
481         } else if (!strcmp (typename, "struct")) {
482                 xmlNode *member, *child, *mname, *mxval;
483                 GHashTable *hash;
484                 GValue mgval;
485                 
486                 hash = soup_value_hash_new ();
487                 for (member = find_real_node (typenode->children);
488                      member;
489                      member = find_real_node (member->next)) {
490                         if (strcmp ((const char *)member->name, "member") != 0) {
491                                 g_hash_table_destroy (hash);
492                                 return FALSE;
493                         }
494                         mname = mxval = NULL;
495                         memset (&mgval, 0, sizeof (mgval));
496
497                         for (child = find_real_node (member->children);
498                              child;
499                              child = find_real_node (child->next)) {
500                                 if (!strcmp ((const char *)child->name, "name"))
501                                         mname = child;
502                                 else if (!strcmp ((const char *)child->name, "value"))
503                                         mxval = child;
504                                 else
505                                         break;
506                         }
507
508                         if (!mname || !mxval || !parse_value (mxval, &mgval)) {
509                                 g_hash_table_destroy (hash);
510                                 return FALSE;
511                         }
512
513                         content = xmlNodeGetContent (mname);
514                         soup_value_hash_insert_value (hash, (char *)content, &mgval);
515                         xmlFree (content);
516                         g_value_unset (&mgval);
517                 }
518                 g_value_init (value, G_TYPE_HASH_TABLE);
519                 g_value_take_boxed (value, hash);
520         } else if (!strcmp (typename, "array")) {
521                 xmlNode *data, *xval;
522                 GValueArray *array;
523                 GValue gval;
524
525                 data = find_real_node (typenode->children);
526                 if (!data || strcmp ((const char *)data->name, "data") != 0)
527                         return FALSE;
528
529 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
530 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
531 #endif
532                 array = g_value_array_new (1);
533                 for (xval = find_real_node (data->children);
534                      xval;
535                      xval = find_real_node (xval->next)) {
536                         memset (&gval, 0, sizeof (gval));
537                         if (strcmp ((const char *)xval->name, "value") != 0 ||
538                             !parse_value (xval, &gval)) {
539                                 g_value_array_free (array);
540                                 return FALSE;
541                         }
542
543                         g_value_array_append (array, &gval);
544                         g_value_unset (&gval);
545                 }
546                 g_value_init (value, G_TYPE_VALUE_ARRAY);
547                 g_value_take_boxed (value, array);
548 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
549 G_GNUC_END_IGNORE_DEPRECATIONS
550 #endif
551         } else
552                 return FALSE;
553
554         return TRUE;
555 }
556
557 /**
558  * soup_xmlrpc_parse_method_call:
559  * @method_call: the XML-RPC methodCall string
560  * @length: the length of @method_call, or -1 if it is NUL-terminated
561  * @method_name: (out): on return, the methodName from @method_call
562  * @params: (out): on return, the parameters from @method_call
563  *
564  * Parses @method_call to get the name and parameters, and returns the
565  * parameter values in a #GValueArray; see also
566  * soup_xmlrpc_extract_method_call(), which is more convenient if you
567  * know in advance what the types of the parameters will be.
568  *
569  * Return value: success or failure.
570  **/
571 gboolean
572 soup_xmlrpc_parse_method_call (const char *method_call, int length,
573                                char **method_name, GValueArray **params)
574 {
575         xmlDoc *doc;
576         xmlNode *node, *param, *xval;
577         xmlChar *xmlMethodName = NULL;
578         gboolean success = FALSE;
579         GValue value;
580
581         doc = xmlParseMemory (method_call,
582                               length == -1 ? strlen (method_call) : length);
583         if (!doc)
584                 return FALSE;
585
586         node = xmlDocGetRootElement (doc);
587         if (!node || strcmp ((const char *)node->name, "methodCall") != 0)
588                 goto fail;
589
590         node = find_real_node (node->children);
591         if (!node || strcmp ((const char *)node->name, "methodName") != 0)
592                 goto fail;
593         xmlMethodName = xmlNodeGetContent (node);
594
595         node = find_real_node (node->next);
596         if (node) {
597                 if (strcmp ((const char *)node->name, "params") != 0)
598                         goto fail;
599
600 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
601 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
602 #endif
603                 *params = soup_value_array_new ();
604                 param = find_real_node (node->children);
605                 while (param && !strcmp ((const char *)param->name, "param")) {
606                         xval = find_real_node (param->children);
607                         if (!xval || strcmp ((const char *)xval->name, "value") != 0 ||
608                             !parse_value (xval, &value)) {
609                                 g_value_array_free (*params);
610                                 goto fail;
611                         }
612                         g_value_array_append (*params, &value);
613                         g_value_unset (&value);
614
615                         param = find_real_node (param->next);
616                 }
617 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
618 G_GNUC_END_IGNORE_DEPRECATIONS
619 #endif
620         } else
621                 *params = soup_value_array_new ();
622
623         success = TRUE;
624         *method_name = g_strdup ((char *)xmlMethodName);
625
626 fail:
627         xmlFreeDoc (doc);
628         if (xmlMethodName)
629                 xmlFree (xmlMethodName);
630         return success;
631 }
632
633 /**
634  * soup_xmlrpc_extract_method_call:
635  * @method_call: the XML-RPC methodCall string
636  * @length: the length of @method_call, or -1 if it is NUL-terminated
637  * @method_name: (out): on return, the methodName from @method_call
638  * @...: return types and locations for parameters
639  *
640  * Parses @method_call to get the name and parameters, and puts
641  * the parameters into variables of the appropriate types.
642  *
643  * The parameters are handled similarly to
644  * @soup_xmlrpc_build_method_call, with pairs of types and values,
645  * terminated by %G_TYPE_INVALID, except that values are pointers to
646  * variables of the indicated type, rather than values of the type.
647  *
648  * See also soup_xmlrpc_parse_method_call(), which can be used if
649  * you don't know the types of the parameters.
650  *
651  * Return value: success or failure.
652  **/
653 gboolean
654 soup_xmlrpc_extract_method_call (const char *method_call, int length,
655                                  char **method_name, ...)
656 {
657         GValueArray *params;
658         gboolean success;
659         va_list args;
660
661         if (!soup_xmlrpc_parse_method_call (method_call, length,
662                                             method_name, &params))
663                 return FALSE;
664
665         va_start (args, method_name);
666         success = soup_value_array_to_args (params, args);
667         va_end (args);
668
669 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
670 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
671 #endif
672         g_value_array_free (params);
673 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
674 G_GNUC_END_IGNORE_DEPRECATIONS
675 #endif
676         return success;
677 }
678
679 /**
680  * soup_xmlrpc_parse_method_response:
681  * @method_response: the XML-RPC methodResponse string
682  * @length: the length of @method_response, or -1 if it is NUL-terminated
683  * @value: (out): on return, the return value from @method_call
684  * @error: error return value
685  *
686  * Parses @method_response and returns the return value in @value. If
687  * @method_response is a fault, @value will be unchanged, and @error
688  * will be set to an error of type %SOUP_XMLRPC_FAULT, with the error
689  * #code containing the fault code, and the error #message containing
690  * the fault string. (If @method_response cannot be parsed at all,
691  * soup_xmlrpc_parse_method_response() will return %FALSE, but @error
692  * will be unset.)
693  *
694  * Return value: %TRUE if a return value was parsed, %FALSE if the
695  * response could not be parsed, or contained a fault.
696  **/
697 gboolean
698 soup_xmlrpc_parse_method_response (const char *method_response, int length,
699                                    GValue *value, GError **error)
700 {
701         xmlDoc *doc;
702         xmlNode *node;
703         gboolean success = FALSE;
704
705         doc = xmlParseMemory (method_response,
706                               length == -1 ? strlen (method_response) : length);
707         if (!doc)
708                 return FALSE;
709
710         node = xmlDocGetRootElement (doc);
711         if (!node || strcmp ((const char *)node->name, "methodResponse") != 0)
712                 goto fail;
713
714         node = find_real_node (node->children);
715         if (!node)
716                 goto fail;
717
718         if (!strcmp ((const char *)node->name, "fault") && error) {
719                 int fault_code;
720                 char *fault_string;
721                 GValue fault_val;
722                 GHashTable *fault_hash;
723
724                 node = find_real_node (node->children);
725                 if (!node || strcmp ((const char *)node->name, "value") != 0)
726                         goto fail;
727                 if (!parse_value (node, &fault_val))
728                         goto fail;
729                 if (!G_VALUE_HOLDS (&fault_val, G_TYPE_HASH_TABLE)) {
730                         g_value_unset (&fault_val);
731                         goto fail;
732                 }
733                 fault_hash = g_value_get_boxed (&fault_val);
734                 if (!soup_value_hash_lookup (fault_hash, "faultCode",
735                                              G_TYPE_INT, &fault_code) ||
736                     !soup_value_hash_lookup (fault_hash, "faultString",
737                                              G_TYPE_STRING, &fault_string)) {
738                         g_value_unset (&fault_val);
739                         goto fail;
740                 }
741
742                 g_set_error (error, SOUP_XMLRPC_FAULT,
743                              fault_code, "%s", fault_string);
744                 g_value_unset (&fault_val);
745         } else if (!strcmp ((const char *)node->name, "params")) {
746                 node = find_real_node (node->children);
747                 if (!node || strcmp ((const char *)node->name, "param") != 0)
748                         goto fail;
749                 node = find_real_node (node->children);
750                 if (!node || strcmp ((const char *)node->name, "value") != 0)
751                         goto fail;
752                 if (!parse_value (node, value))
753                         goto fail;
754                 success = TRUE;
755         }
756
757 fail:
758         xmlFreeDoc (doc);
759         return success;
760 }
761
762 /**
763  * soup_xmlrpc_extract_method_response:
764  * @method_response: the XML-RPC methodResponse string
765  * @length: the length of @method_response, or -1 if it is NUL-terminated
766  * @error: error return value
767  * @type: the expected type of the return value
768  * @...: location for return value
769  *
770  * Parses @method_response and extracts the return value into
771  * a variable of the correct type.
772  *
773  * If @method_response is a fault, the return value will be unset,
774  * and @error will be set to an error of type %SOUP_XMLRPC_FAULT, with
775  * the error #code containing the fault code, and the error #message
776  * containing the fault string. (If @method_response cannot be parsed
777  * at all, soup_xmlrpc_extract_method_response() will return %FALSE,
778  * but @error will be unset.)
779  *
780  * Return value: %TRUE if a return value was parsed, %FALSE if the
781  * response was of the wrong type, or contained a fault.
782  **/
783 gboolean
784 soup_xmlrpc_extract_method_response (const char *method_response, int length,
785                                      GError **error, GType type, ...)
786 {
787         GValue value;
788         va_list args;
789
790         if (!soup_xmlrpc_parse_method_response (method_response, length,
791                                                 &value, error))
792                 return FALSE;
793         if (!G_VALUE_HOLDS (&value, type))
794                 return FALSE;
795
796         va_start (args, type);
797         SOUP_VALUE_GETV (&value, type, args);
798         va_end (args);
799
800         return TRUE;
801 }
802
803
804 GQuark
805 soup_xmlrpc_error_quark (void)
806 {
807         static GQuark error;
808         if (!error)
809                 error = g_quark_from_static_string ("soup_xmlrpc_error_quark");
810         return error;
811 }
812
813 /**
814  * SOUP_XMLRPC_FAULT:
815  *
816  * A #GError domain representing an XML-RPC fault code. Used with
817  * #SoupXMLRPCFault (although servers may also return fault codes not
818  * in that enumeration).
819  */
820
821 /**
822  * SoupXMLRPCFault:
823  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED: request was not
824  *   well-formed
825  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_UNSUPPORTED_ENCODING: request was in
826  *   an unsupported encoding
827  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_INVALID_CHARACTER_FOR_ENCODING:
828  *   request contained an invalid character
829  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_XML_RPC: request was not
830  *   valid XML-RPC
831  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND: method
832  *   not found
833  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS: invalid
834  *   parameters
835  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INTERNAL_XML_RPC_ERROR: internal
836  *   error
837  * @SOUP_XMLRPC_FAULT_APPLICATION_ERROR: start of reserved range for
838  *   application error codes
839  * @SOUP_XMLRPC_FAULT_SYSTEM_ERROR: start of reserved range for
840  *   system error codes
841  * @SOUP_XMLRPC_FAULT_TRANSPORT_ERROR: start of reserved range for
842  *   transport error codes
843  *
844  * Pre-defined XML-RPC fault codes from <ulink
845  * url="http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php">http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php</ulink>.
846  * These are an extension, not part of the XML-RPC spec; you can't
847  * assume servers will use them.
848  */
849
850 GQuark
851 soup_xmlrpc_fault_quark (void)
852 {
853         static GQuark error;
854         if (!error)
855                 error = g_quark_from_static_string ("soup_xmlrpc_fault_quark");
856         return error;
857 }
858
859 static xmlNode *
860 find_real_node (xmlNode *node)
861 {
862         while (node && (node->type == XML_COMMENT_NODE ||
863                         xmlIsBlankNode (node)))
864                 node = node->next;
865         return node;
866 }