Remove build warning
[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,
294                           const char *fault_format,
295                           va_list     args) G_GNUC_PRINTF (2, 0);
296
297 static char *
298 soup_xmlrpc_build_faultv (int fault_code, const char *fault_format, va_list args)
299 {
300         xmlDoc *doc;
301         xmlNode *node, *member;
302         GValue value;
303         xmlChar *xmlbody;
304         char *fault_string, *body;
305         int len;
306
307         fault_string = g_strdup_vprintf (fault_format, args);
308
309         doc = xmlNewDoc ((const xmlChar *)"1.0");
310         doc->standalone = FALSE;
311         doc->encoding = xmlCharStrdup ("UTF-8");
312
313         node = xmlNewDocNode (doc, NULL,
314                               (const xmlChar *)"methodResponse", NULL);
315         xmlDocSetRootElement (doc, node);
316         node = xmlNewChild (node, NULL, (const xmlChar *)"fault", NULL);
317         node = xmlNewChild (node, NULL, (const xmlChar *)"value", NULL);
318         node = xmlNewChild (node, NULL, (const xmlChar *)"struct", NULL);
319
320         memset (&value, 0, sizeof (value));
321
322         member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
323         xmlNewChild (member, NULL,
324                      (const xmlChar *)"name", (const xmlChar *)"faultCode");
325         g_value_init (&value, G_TYPE_INT);
326         g_value_set_int (&value, fault_code);
327         insert_value (member, &value);
328         g_value_unset (&value);
329
330         member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
331         xmlNewChild (member, NULL,
332                      (const xmlChar *)"name", (const xmlChar *)"faultString");
333         g_value_init (&value, G_TYPE_STRING);
334         g_value_take_string (&value, fault_string);
335         insert_value (member, &value);
336         g_value_unset (&value);
337
338         xmlDocDumpMemory (doc, &xmlbody, &len);
339         body = g_strndup ((char *)xmlbody, len);
340         xmlFree (xmlbody);
341         xmlFreeDoc (doc);
342
343         return body;
344 }
345
346 /**
347  * soup_xmlrpc_build_fault:
348  * @fault_code: the fault code
349  * @fault_format: a printf()-style format string
350  * @...: the parameters to @fault_format
351  *
352  * This creates an XML-RPC fault response and returns it as a string.
353  * (To create a successful response, use
354  * soup_xmlrpc_build_method_response().)
355  *
356  * Return value: the text of the fault
357  **/
358 char *
359 soup_xmlrpc_build_fault (int fault_code, const char *fault_format, ...)
360 {
361         va_list args;
362         char *body;
363
364         va_start (args, fault_format);
365         body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
366         va_end (args);
367         return body;
368 }
369
370 /**
371  * soup_xmlrpc_set_response:
372  * @msg: an XML-RPC request
373  * @type: the type of the response value
374  * @...: the response value
375  *
376  * Sets the status code and response body of @msg to indicate a
377  * successful XML-RPC call, with a return value given by @type and the
378  * following varargs argument, of the type indicated by @type.
379  **/
380 void
381 soup_xmlrpc_set_response (SoupMessage *msg, GType type, ...)
382 {
383         va_list args;
384         GValue value;
385         char *body;
386
387         va_start (args, type);
388         SOUP_VALUE_SETV (&value, type, args);
389         va_end (args);
390
391         body = soup_xmlrpc_build_method_response (&value);
392         g_value_unset (&value);
393         soup_message_set_status (msg, SOUP_STATUS_OK);
394         soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
395                                    body, strlen (body));
396 }
397
398 /**
399  * soup_xmlrpc_set_fault:
400  * @msg: an XML-RPC request
401  * @fault_code: the fault code
402  * @fault_format: a printf()-style format string
403  * @...: the parameters to @fault_format
404  *
405  * Sets the status code and response body of @msg to indicate an
406  * unsuccessful XML-RPC call, with the error described by @fault_code
407  * and @fault_format.
408  **/
409 void
410 soup_xmlrpc_set_fault (SoupMessage *msg, int fault_code,
411                        const char *fault_format, ...)
412 {
413         va_list args;
414         char *body;
415
416         va_start (args, fault_format);
417         body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
418         va_end (args);
419
420         soup_message_set_status (msg, SOUP_STATUS_OK);
421         soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
422                                    body, strlen (body));
423 }
424
425
426
427 static gboolean
428 parse_value (xmlNode *xmlvalue, GValue *value)
429 {
430         xmlNode *typenode;
431         const char *typename;
432         xmlChar *content;
433
434         memset (value, 0, sizeof (GValue));
435
436         typenode = find_real_node (xmlvalue->children);
437         if (!typenode) {
438                 /* If no type node, it's a string */
439                 content = xmlNodeGetContent (typenode);
440                 g_value_init (value, G_TYPE_STRING);
441                 g_value_set_string (value, (char *)content);
442                 xmlFree (content);
443                 return TRUE;
444         }
445
446         typename = (const char *)typenode->name;
447
448         if (!strcmp (typename, "i4") || !strcmp (typename, "int")) {
449                 content = xmlNodeGetContent (typenode);
450                 g_value_init (value, G_TYPE_INT);
451                 g_value_set_int (value, atoi ((char *)content));
452                 xmlFree (content);
453         } else if (!strcmp (typename, "boolean")) {
454                 content = xmlNodeGetContent (typenode);
455                 g_value_init (value, G_TYPE_BOOLEAN);
456                 g_value_set_boolean (value, atoi ((char *)content));
457                 xmlFree (content);
458         } else if (!strcmp (typename, "string")) {
459                 content = xmlNodeGetContent (typenode);
460                 g_value_init (value, G_TYPE_STRING);
461                 g_value_set_string (value, (char *)content);
462                 xmlFree (content);
463         } else if (!strcmp (typename, "double")) {
464                 content = xmlNodeGetContent (typenode);
465                 g_value_init (value, G_TYPE_DOUBLE);
466                 g_value_set_double (value, g_ascii_strtod ((char *)content, NULL));
467                 xmlFree (content);
468         } else if (!strcmp (typename, "dateTime.iso8601")) {
469                 content = xmlNodeGetContent (typenode);
470                 g_value_init (value, SOUP_TYPE_DATE);
471                 g_value_take_boxed (value, soup_date_new_from_string ((char *)content));
472                 xmlFree (content);
473         } else if (!strcmp (typename, "base64")) {
474                 GByteArray *ba;
475                 guchar *decoded;
476                 gsize len;
477
478                 content = xmlNodeGetContent (typenode);
479                 decoded = g_base64_decode ((char *)content, &len);
480                 ba = g_byte_array_sized_new (len);
481                 g_byte_array_append (ba, decoded, len);
482                 g_free (decoded);
483                 xmlFree (content);
484                 g_value_init (value, SOUP_TYPE_BYTE_ARRAY);
485                 g_value_take_boxed (value, ba);
486         } else if (!strcmp (typename, "struct")) {
487                 xmlNode *member, *child, *mname, *mxval;
488                 GHashTable *hash;
489                 GValue mgval;
490                 
491                 hash = soup_value_hash_new ();
492                 for (member = find_real_node (typenode->children);
493                      member;
494                      member = find_real_node (member->next)) {
495                         if (strcmp ((const char *)member->name, "member") != 0) {
496                                 g_hash_table_destroy (hash);
497                                 return FALSE;
498                         }
499                         mname = mxval = NULL;
500                         memset (&mgval, 0, sizeof (mgval));
501
502                         for (child = find_real_node (member->children);
503                              child;
504                              child = find_real_node (child->next)) {
505                                 if (!strcmp ((const char *)child->name, "name"))
506                                         mname = child;
507                                 else if (!strcmp ((const char *)child->name, "value"))
508                                         mxval = child;
509                                 else
510                                         break;
511                         }
512
513                         if (!mname || !mxval || !parse_value (mxval, &mgval)) {
514                                 g_hash_table_destroy (hash);
515                                 return FALSE;
516                         }
517
518                         content = xmlNodeGetContent (mname);
519                         soup_value_hash_insert_value (hash, (char *)content, &mgval);
520                         xmlFree (content);
521                         g_value_unset (&mgval);
522                 }
523                 g_value_init (value, G_TYPE_HASH_TABLE);
524                 g_value_take_boxed (value, hash);
525         } else if (!strcmp (typename, "array")) {
526                 xmlNode *data, *xval;
527                 GValueArray *array;
528                 GValue gval;
529
530                 data = find_real_node (typenode->children);
531                 if (!data || strcmp ((const char *)data->name, "data") != 0)
532                         return FALSE;
533
534 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
535 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
536 #endif
537                 array = g_value_array_new (1);
538                 for (xval = find_real_node (data->children);
539                      xval;
540                      xval = find_real_node (xval->next)) {
541                         memset (&gval, 0, sizeof (gval));
542                         if (strcmp ((const char *)xval->name, "value") != 0 ||
543                             !parse_value (xval, &gval)) {
544                                 g_value_array_free (array);
545                                 return FALSE;
546                         }
547
548                         g_value_array_append (array, &gval);
549                         g_value_unset (&gval);
550                 }
551                 g_value_init (value, G_TYPE_VALUE_ARRAY);
552                 g_value_take_boxed (value, array);
553 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
554 G_GNUC_END_IGNORE_DEPRECATIONS
555 #endif
556         } else
557                 return FALSE;
558
559         return TRUE;
560 }
561
562 /**
563  * soup_xmlrpc_parse_method_call:
564  * @method_call: the XML-RPC methodCall string
565  * @length: the length of @method_call, or -1 if it is NUL-terminated
566  * @method_name: (out): on return, the methodName from @method_call
567  * @params: (out): on return, the parameters from @method_call
568  *
569  * Parses @method_call to get the name and parameters, and returns the
570  * parameter values in a #GValueArray; see also
571  * soup_xmlrpc_extract_method_call(), which is more convenient if you
572  * know in advance what the types of the parameters will be.
573  *
574  * Return value: success or failure.
575  **/
576 gboolean
577 soup_xmlrpc_parse_method_call (const char *method_call, int length,
578                                char **method_name, GValueArray **params)
579 {
580         xmlDoc *doc;
581         xmlNode *node, *param, *xval;
582         xmlChar *xmlMethodName = NULL;
583         gboolean success = FALSE;
584         GValue value;
585
586         doc = xmlParseMemory (method_call,
587                               length == -1 ? strlen (method_call) : length);
588         if (!doc)
589                 return FALSE;
590
591         node = xmlDocGetRootElement (doc);
592         if (!node || strcmp ((const char *)node->name, "methodCall") != 0)
593                 goto fail;
594
595         node = find_real_node (node->children);
596         if (!node || strcmp ((const char *)node->name, "methodName") != 0)
597                 goto fail;
598         xmlMethodName = xmlNodeGetContent (node);
599
600         node = find_real_node (node->next);
601         if (node) {
602                 if (strcmp ((const char *)node->name, "params") != 0)
603                         goto fail;
604
605 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
606 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
607 #endif
608                 *params = soup_value_array_new ();
609                 param = find_real_node (node->children);
610                 while (param && !strcmp ((const char *)param->name, "param")) {
611                         xval = find_real_node (param->children);
612                         if (!xval || strcmp ((const char *)xval->name, "value") != 0 ||
613                             !parse_value (xval, &value)) {
614                                 g_value_array_free (*params);
615                                 goto fail;
616                         }
617                         g_value_array_append (*params, &value);
618                         g_value_unset (&value);
619
620                         param = find_real_node (param->next);
621                 }
622 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
623 G_GNUC_END_IGNORE_DEPRECATIONS
624 #endif
625         } else
626                 *params = soup_value_array_new ();
627
628         success = TRUE;
629         *method_name = g_strdup ((char *)xmlMethodName);
630
631 fail:
632         xmlFreeDoc (doc);
633         if (xmlMethodName)
634                 xmlFree (xmlMethodName);
635         return success;
636 }
637
638 /**
639  * soup_xmlrpc_extract_method_call:
640  * @method_call: the XML-RPC methodCall string
641  * @length: the length of @method_call, or -1 if it is NUL-terminated
642  * @method_name: (out): on return, the methodName from @method_call
643  * @...: return types and locations for parameters
644  *
645  * Parses @method_call to get the name and parameters, and puts
646  * the parameters into variables of the appropriate types.
647  *
648  * The parameters are handled similarly to
649  * @soup_xmlrpc_build_method_call, with pairs of types and values,
650  * terminated by %G_TYPE_INVALID, except that values are pointers to
651  * variables of the indicated type, rather than values of the type.
652  *
653  * See also soup_xmlrpc_parse_method_call(), which can be used if
654  * you don't know the types of the parameters.
655  *
656  * Return value: success or failure.
657  **/
658 gboolean
659 soup_xmlrpc_extract_method_call (const char *method_call, int length,
660                                  char **method_name, ...)
661 {
662         GValueArray *params;
663         gboolean success;
664         va_list args;
665
666         if (!soup_xmlrpc_parse_method_call (method_call, length,
667                                             method_name, &params))
668                 return FALSE;
669
670         va_start (args, method_name);
671         success = soup_value_array_to_args (params, args);
672         va_end (args);
673
674 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
675 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
676 #endif
677         g_value_array_free (params);
678 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
679 G_GNUC_END_IGNORE_DEPRECATIONS
680 #endif
681         return success;
682 }
683
684 /**
685  * soup_xmlrpc_parse_method_response:
686  * @method_response: the XML-RPC methodResponse string
687  * @length: the length of @method_response, or -1 if it is NUL-terminated
688  * @value: (out): on return, the return value from @method_call
689  * @error: error return value
690  *
691  * Parses @method_response and returns the return value in @value. If
692  * @method_response is a fault, @value will be unchanged, and @error
693  * will be set to an error of type %SOUP_XMLRPC_FAULT, with the error
694  * #code containing the fault code, and the error #message containing
695  * the fault string. (If @method_response cannot be parsed at all,
696  * soup_xmlrpc_parse_method_response() will return %FALSE, but @error
697  * will be unset.)
698  *
699  * Return value: %TRUE if a return value was parsed, %FALSE if the
700  * response could not be parsed, or contained a fault.
701  **/
702 gboolean
703 soup_xmlrpc_parse_method_response (const char *method_response, int length,
704                                    GValue *value, GError **error)
705 {
706         xmlDoc *doc;
707         xmlNode *node;
708         gboolean success = FALSE;
709
710         doc = xmlParseMemory (method_response,
711                               length == -1 ? strlen (method_response) : length);
712         if (!doc)
713                 return FALSE;
714
715         node = xmlDocGetRootElement (doc);
716         if (!node || strcmp ((const char *)node->name, "methodResponse") != 0)
717                 goto fail;
718
719         node = find_real_node (node->children);
720         if (!node)
721                 goto fail;
722
723         if (!strcmp ((const char *)node->name, "fault") && error) {
724                 int fault_code;
725                 char *fault_string;
726                 GValue fault_val;
727                 GHashTable *fault_hash;
728
729                 node = find_real_node (node->children);
730                 if (!node || strcmp ((const char *)node->name, "value") != 0)
731                         goto fail;
732                 if (!parse_value (node, &fault_val))
733                         goto fail;
734                 if (!G_VALUE_HOLDS (&fault_val, G_TYPE_HASH_TABLE)) {
735                         g_value_unset (&fault_val);
736                         goto fail;
737                 }
738                 fault_hash = g_value_get_boxed (&fault_val);
739                 if (!soup_value_hash_lookup (fault_hash, "faultCode",
740                                              G_TYPE_INT, &fault_code) ||
741                     !soup_value_hash_lookup (fault_hash, "faultString",
742                                              G_TYPE_STRING, &fault_string)) {
743                         g_value_unset (&fault_val);
744                         goto fail;
745                 }
746
747                 g_set_error (error, SOUP_XMLRPC_FAULT,
748                              fault_code, "%s", fault_string);
749                 g_value_unset (&fault_val);
750         } else if (!strcmp ((const char *)node->name, "params")) {
751                 node = find_real_node (node->children);
752                 if (!node || strcmp ((const char *)node->name, "param") != 0)
753                         goto fail;
754                 node = find_real_node (node->children);
755                 if (!node || strcmp ((const char *)node->name, "value") != 0)
756                         goto fail;
757                 if (!parse_value (node, value))
758                         goto fail;
759                 success = TRUE;
760         }
761
762 fail:
763         xmlFreeDoc (doc);
764         return success;
765 }
766
767 /**
768  * soup_xmlrpc_extract_method_response:
769  * @method_response: the XML-RPC methodResponse string
770  * @length: the length of @method_response, or -1 if it is NUL-terminated
771  * @error: error return value
772  * @type: the expected type of the return value
773  * @...: location for return value
774  *
775  * Parses @method_response and extracts the return value into
776  * a variable of the correct type.
777  *
778  * If @method_response is a fault, the return value will be unset,
779  * and @error will be set to an error of type %SOUP_XMLRPC_FAULT, with
780  * the error #code containing the fault code, and the error #message
781  * containing the fault string. (If @method_response cannot be parsed
782  * at all, soup_xmlrpc_extract_method_response() will return %FALSE,
783  * but @error will be unset.)
784  *
785  * Return value: %TRUE if a return value was parsed, %FALSE if the
786  * response was of the wrong type, or contained a fault.
787  **/
788 gboolean
789 soup_xmlrpc_extract_method_response (const char *method_response, int length,
790                                      GError **error, GType type, ...)
791 {
792         GValue value;
793         va_list args;
794
795         if (!soup_xmlrpc_parse_method_response (method_response, length,
796                                                 &value, error))
797                 return FALSE;
798         if (!G_VALUE_HOLDS (&value, type))
799                 return FALSE;
800
801         va_start (args, type);
802         SOUP_VALUE_GETV (&value, type, args);
803         va_end (args);
804
805         return TRUE;
806 }
807
808
809 GQuark
810 soup_xmlrpc_error_quark (void)
811 {
812         static GQuark error;
813         if (!error)
814                 error = g_quark_from_static_string ("soup_xmlrpc_error_quark");
815         return error;
816 }
817
818 /**
819  * SOUP_XMLRPC_FAULT:
820  *
821  * A #GError domain representing an XML-RPC fault code. Used with
822  * #SoupXMLRPCFault (although servers may also return fault codes not
823  * in that enumeration).
824  */
825
826 /**
827  * SoupXMLRPCFault:
828  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED: request was not
829  *   well-formed
830  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_UNSUPPORTED_ENCODING: request was in
831  *   an unsupported encoding
832  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_INVALID_CHARACTER_FOR_ENCODING:
833  *   request contained an invalid character
834  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_XML_RPC: request was not
835  *   valid XML-RPC
836  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND: method
837  *   not found
838  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS: invalid
839  *   parameters
840  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INTERNAL_XML_RPC_ERROR: internal
841  *   error
842  * @SOUP_XMLRPC_FAULT_APPLICATION_ERROR: start of reserved range for
843  *   application error codes
844  * @SOUP_XMLRPC_FAULT_SYSTEM_ERROR: start of reserved range for
845  *   system error codes
846  * @SOUP_XMLRPC_FAULT_TRANSPORT_ERROR: start of reserved range for
847  *   transport error codes
848  *
849  * Pre-defined XML-RPC fault codes from <ulink
850  * url="http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php">http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php</ulink>.
851  * These are an extension, not part of the XML-RPC spec; you can't
852  * assume servers will use them.
853  */
854
855 GQuark
856 soup_xmlrpc_fault_quark (void)
857 {
858         static GQuark error;
859         if (!error)
860                 error = g_quark_from_static_string ("soup_xmlrpc_fault_quark");
861         return error;
862 }
863
864 static xmlNode *
865 find_real_node (xmlNode *node)
866 {
867         while (node && (node->type == XML_COMMENT_NODE ||
868                         xmlIsBlankNode (node)))
869                 node = node->next;
870         return node;
871 }