Reapplying patch to disable attempts to use gtk-doc
[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 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
106 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
107 #endif
108         } else if (type == G_TYPE_VALUE_ARRAY) {
109 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
110 G_GNUC_END_IGNORE_DEPRECATIONS
111 #endif
112                 GValueArray *va = g_value_get_boxed (value);
113                 xmlNode *node;
114                 int i;
115
116                 node = xmlNewChild (xvalue, NULL,
117                                     (const xmlChar *)"array", NULL);
118                 node = xmlNewChild (node, NULL,
119                                     (const xmlChar *)"data", NULL);
120                 for (i = 0; i < va->n_values; i++) {
121                         if (!insert_value (node, &va->values[i]))
122                                 return FALSE;
123                 }
124         } else
125                 return FALSE;
126
127         return TRUE;
128 }
129
130 /**
131  * soup_xmlrpc_build_method_call:
132  * @method_name: the name of the XML-RPC method
133  * @params: (array length=n_params): arguments to @method
134  * @n_params: length of @params
135  *
136  * This creates an XML-RPC methodCall and returns it as a string.
137  * This is the low-level method that soup_xmlrpc_request_new() is
138  * built on.
139  *
140  * @params is an array of #GValue representing the parameters to
141  * @method. (It is *not* a #GValueArray, although if you have a
142  * #GValueArray, you can just pass its <literal>values</literal>f and
143  * <literal>n_values</literal> fields.)
144  *
145  * The correspondence between glib types and XML-RPC types is:
146  *
147  *   int: #int (%G_TYPE_INT)
148  *   boolean: #gboolean (%G_TYPE_BOOLEAN)
149  *   string: #char* (%G_TYPE_STRING)
150  *   double: #double (%G_TYPE_DOUBLE)
151  *   datetime.iso8601: #SoupDate (%SOUP_TYPE_DATE)
152  *   base64: #GByteArray (%SOUP_TYPE_BYTE_ARRAY)
153  *   struct: #GHashTable (%G_TYPE_HASH_TABLE)
154  *   array: #GValueArray (%G_TYPE_VALUE_ARRAY)
155  *
156  * For structs, use a #GHashTable that maps strings to #GValue;
157  * soup_value_hash_new() and related methods can help with this.
158  *
159  * Return value: the text of the methodCall, or %NULL on error
160  **/
161 char *
162 soup_xmlrpc_build_method_call (const char *method_name,
163                                GValue *params, int n_params)
164 {
165         xmlDoc *doc;
166         xmlNode *node, *param;
167         xmlChar *xmlbody;
168         int i, len;
169         char *body;
170
171         doc = xmlNewDoc ((const xmlChar *)"1.0");
172         doc->standalone = FALSE;
173         doc->encoding = xmlCharStrdup ("UTF-8");
174
175         node = xmlNewDocNode (doc, NULL, (const xmlChar *)"methodCall", NULL);
176         xmlDocSetRootElement (doc, node);
177         xmlNewChild (node, NULL, (const xmlChar *)"methodName",
178                      (const xmlChar *)method_name);
179
180         node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
181         for (i = 0; i < n_params; i++) {
182                 param  = xmlNewChild (node, NULL,
183                                       (const xmlChar *)"param", NULL);
184                 if (!insert_value (param, &params[i])) {
185                         xmlFreeDoc (doc);
186                         return NULL;
187                 }
188         }
189
190         xmlDocDumpMemory (doc, &xmlbody, &len);
191         body = g_strndup ((char *)xmlbody, len);
192         xmlFree (xmlbody);
193         xmlFreeDoc (doc);
194         return body;
195 }
196
197 static SoupMessage *
198 soup_xmlrpc_request_newv (const char *uri, const char *method_name, va_list args)
199 {
200         SoupMessage *msg;
201         GValueArray *params;
202         char *body;
203
204         params = soup_value_array_from_args (args);
205         if (!params)
206                 return NULL;
207
208         body = soup_xmlrpc_build_method_call (method_name, params->values,
209                                               params->n_values);
210 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
211 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
212 #endif
213         g_value_array_free (params);
214 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
215 G_GNUC_END_IGNORE_DEPRECATIONS
216 #endif
217         if (!body)
218                 return NULL;
219
220         msg = soup_message_new ("POST", uri);
221         soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
222                                   body, strlen (body));
223         return msg;
224 }
225
226 /**
227  * soup_xmlrpc_request_new:
228  * @uri: URI of the XML-RPC service
229  * @method_name: the name of the XML-RPC method to invoke at @uri
230  * @...: parameters for @method
231  *
232  * Creates an XML-RPC methodCall and returns a #SoupMessage, ready
233  * to send, for that method call.
234  *
235  * The parameters are passed as type/value pairs; ie, first a #GType,
236  * and then a value of the appropriate type, finally terminated by
237  * %G_TYPE_INVALID.
238  *
239  * Return value: (transfer full): a #SoupMessage encoding the
240  * indicated XML-RPC request.
241  **/
242 SoupMessage *
243 soup_xmlrpc_request_new (const char *uri, const char *method_name, ...)
244 {
245         SoupMessage *msg;
246         va_list args;
247
248         va_start (args, method_name);
249         msg = soup_xmlrpc_request_newv (uri, method_name, args);
250         va_end (args);
251         return msg;
252 }
253
254 /**
255  * soup_xmlrpc_build_method_response:
256  * @value: the return value
257  *
258  * This creates a (successful) XML-RPC methodResponse and returns it
259  * as a string. To create a fault response, use
260  * soup_xmlrpc_build_fault().
261  *
262  * The glib type to XML-RPC type mapping is as with
263  * soup_xmlrpc_build_method_call(), qv.
264  *
265  * Return value: the text of the methodResponse, or %NULL on error
266  **/
267 char *
268 soup_xmlrpc_build_method_response (GValue *value)
269 {
270         xmlDoc *doc;
271         xmlNode *node;
272         xmlChar *xmlbody;
273         char *body;
274         int len;
275
276         doc = xmlNewDoc ((const xmlChar *)"1.0");
277         doc->standalone = FALSE;
278         doc->encoding = xmlCharStrdup ("UTF-8");
279
280         node = xmlNewDocNode (doc, NULL,
281                               (const xmlChar *)"methodResponse", NULL);
282         xmlDocSetRootElement (doc, node);
283
284         node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
285         node = xmlNewChild (node, NULL, (const xmlChar *)"param", NULL);
286         if (!insert_value (node, value)) {
287                 xmlFreeDoc (doc);
288                 return NULL;
289         }
290
291         xmlDocDumpMemory (doc, &xmlbody, &len);
292         body = g_strndup ((char *)xmlbody, len);
293         xmlFree (xmlbody);
294         xmlFreeDoc (doc);
295         return body;
296 }
297
298 static char *
299 soup_xmlrpc_build_faultv (int fault_code, const char *fault_format, va_list args)
300 {
301         xmlDoc *doc;
302         xmlNode *node, *member;
303         GValue value;
304         xmlChar *xmlbody;
305         char *fault_string, *body;
306         int len;
307
308         fault_string = g_strdup_vprintf (fault_format, args);
309
310         doc = xmlNewDoc ((const xmlChar *)"1.0");
311         doc->standalone = FALSE;
312         doc->encoding = xmlCharStrdup ("UTF-8");
313
314         node = xmlNewDocNode (doc, NULL,
315                               (const xmlChar *)"methodResponse", NULL);
316         xmlDocSetRootElement (doc, node);
317         node = xmlNewChild (node, NULL, (const xmlChar *)"fault", NULL);
318         node = xmlNewChild (node, NULL, (const xmlChar *)"value", NULL);
319         node = xmlNewChild (node, NULL, (const xmlChar *)"struct", NULL);
320
321         memset (&value, 0, sizeof (value));
322
323         member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
324         xmlNewChild (member, NULL,
325                      (const xmlChar *)"name", (const xmlChar *)"faultCode");
326         g_value_init (&value, G_TYPE_INT);
327         g_value_set_int (&value, fault_code);
328         insert_value (member, &value);
329         g_value_unset (&value);
330
331         member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
332         xmlNewChild (member, NULL,
333                      (const xmlChar *)"name", (const xmlChar *)"faultString");
334         g_value_init (&value, G_TYPE_STRING);
335         g_value_take_string (&value, fault_string);
336         insert_value (member, &value);
337         g_value_unset (&value);
338
339         xmlDocDumpMemory (doc, &xmlbody, &len);
340         body = g_strndup ((char *)xmlbody, len);
341         xmlFree (xmlbody);
342         xmlFreeDoc (doc);
343
344         return body;
345 }
346
347 /**
348  * soup_xmlrpc_build_fault:
349  * @fault_code: the fault code
350  * @fault_format: a printf()-style format string
351  * @...: the parameters to @fault_format
352  *
353  * This creates an XML-RPC fault response and returns it as a string.
354  * (To create a successful response, use
355  * soup_xmlrpc_build_method_response().)
356  *
357  * Return value: the text of the fault
358  **/
359 char *
360 soup_xmlrpc_build_fault (int fault_code, const char *fault_format, ...)
361 {
362         va_list args;
363         char *body;
364
365         va_start (args, fault_format);
366         body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
367         va_end (args);
368         return body;
369 }
370
371 /**
372  * soup_xmlrpc_set_response:
373  * @msg: an XML-RPC request
374  * @type: the type of the response value
375  * @...: the response value
376  *
377  * Sets the status code and response body of @msg to indicate a
378  * successful XML-RPC call, with a return value given by @type and the
379  * following varargs argument, of the type indicated by @type.
380  **/
381 void
382 soup_xmlrpc_set_response (SoupMessage *msg, GType type, ...)
383 {
384         va_list args;
385         GValue value;
386         char *body;
387
388         va_start (args, type);
389         SOUP_VALUE_SETV (&value, type, args);
390         va_end (args);
391
392         body = soup_xmlrpc_build_method_response (&value);
393         g_value_unset (&value);
394         soup_message_set_status (msg, SOUP_STATUS_OK);
395         soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
396                                    body, strlen (body));
397 }
398
399 /**
400  * soup_xmlrpc_set_fault:
401  * @msg: an XML-RPC request
402  * @fault_code: the fault code
403  * @fault_format: a printf()-style format string
404  * @...: the parameters to @fault_format
405  *
406  * Sets the status code and response body of @msg to indicate an
407  * unsuccessful XML-RPC call, with the error described by @fault_code
408  * and @fault_format.
409  **/
410 void
411 soup_xmlrpc_set_fault (SoupMessage *msg, int fault_code,
412                        const char *fault_format, ...)
413 {
414         va_list args;
415         char *body;
416
417         va_start (args, fault_format);
418         body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
419         va_end (args);
420
421         soup_message_set_status (msg, SOUP_STATUS_OK);
422         soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
423                                    body, strlen (body));
424 }
425
426
427
428 static gboolean
429 parse_value (xmlNode *xmlvalue, GValue *value)
430 {
431         xmlNode *typenode;
432         const char *typename;
433         xmlChar *content;
434
435         memset (value, 0, sizeof (GValue));
436
437         typenode = find_real_node (xmlvalue->children);
438         if (!typenode) {
439                 /* If no type node, it's a string */
440                 content = xmlNodeGetContent (typenode);
441                 g_value_init (value, G_TYPE_STRING);
442                 g_value_set_string (value, (char *)content);
443                 xmlFree (content);
444                 return TRUE;
445         }
446
447         typename = (const char *)typenode->name;
448
449         if (!strcmp (typename, "i4") || !strcmp (typename, "int")) {
450                 content = xmlNodeGetContent (typenode);
451                 g_value_init (value, G_TYPE_INT);
452                 g_value_set_int (value, atoi ((char *)content));
453                 xmlFree (content);
454         } else if (!strcmp (typename, "boolean")) {
455                 content = xmlNodeGetContent (typenode);
456                 g_value_init (value, G_TYPE_BOOLEAN);
457                 g_value_set_boolean (value, atoi ((char *)content));
458                 xmlFree (content);
459         } else if (!strcmp (typename, "string")) {
460                 content = xmlNodeGetContent (typenode);
461                 g_value_init (value, G_TYPE_STRING);
462                 g_value_set_string (value, (char *)content);
463                 xmlFree (content);
464         } else if (!strcmp (typename, "double")) {
465                 content = xmlNodeGetContent (typenode);
466                 g_value_init (value, G_TYPE_DOUBLE);
467                 g_value_set_double (value, g_ascii_strtod ((char *)content, NULL));
468                 xmlFree (content);
469         } else if (!strcmp (typename, "dateTime.iso8601")) {
470                 content = xmlNodeGetContent (typenode);
471                 g_value_init (value, SOUP_TYPE_DATE);
472                 g_value_take_boxed (value, soup_date_new_from_string ((char *)content));
473                 xmlFree (content);
474         } else if (!strcmp (typename, "base64")) {
475                 GByteArray *ba;
476                 guchar *decoded;
477                 gsize len;
478
479                 content = xmlNodeGetContent (typenode);
480                 decoded = g_base64_decode ((char *)content, &len);
481                 ba = g_byte_array_sized_new (len);
482                 g_byte_array_append (ba, decoded, len);
483                 g_free (decoded);
484                 xmlFree (content);
485                 g_value_init (value, SOUP_TYPE_BYTE_ARRAY);
486                 g_value_take_boxed (value, ba);
487         } else if (!strcmp (typename, "struct")) {
488                 xmlNode *member, *child, *mname, *mxval;
489                 GHashTable *hash;
490                 GValue mgval;
491                 
492                 hash = soup_value_hash_new ();
493                 for (member = find_real_node (typenode->children);
494                      member;
495                      member = find_real_node (member->next)) {
496                         if (strcmp ((const char *)member->name, "member") != 0) {
497                                 g_hash_table_destroy (hash);
498                                 return FALSE;
499                         }
500                         mname = mxval = NULL;
501                         memset (&mgval, 0, sizeof (mgval));
502
503                         for (child = find_real_node (member->children);
504                              child;
505                              child = find_real_node (child->next)) {
506                                 if (!strcmp ((const char *)child->name, "name"))
507                                         mname = child;
508                                 else if (!strcmp ((const char *)child->name, "value"))
509                                         mxval = child;
510                                 else
511                                         break;
512                         }
513
514                         if (!mname || !mxval || !parse_value (mxval, &mgval)) {
515                                 g_hash_table_destroy (hash);
516                                 return FALSE;
517                         }
518
519                         content = xmlNodeGetContent (mname);
520                         soup_value_hash_insert_value (hash, (char *)content, &mgval);
521                         xmlFree (content);
522                         g_value_unset (&mgval);
523                 }
524                 g_value_init (value, G_TYPE_HASH_TABLE);
525                 g_value_take_boxed (value, hash);
526         } else if (!strcmp (typename, "array")) {
527                 xmlNode *data, *xval;
528                 GValueArray *array;
529                 GValue gval;
530
531                 data = find_real_node (typenode->children);
532                 if (!data || strcmp ((const char *)data->name, "data") != 0)
533                         return FALSE;
534
535 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
536 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
537 #endif
538                 array = g_value_array_new (1);
539                 for (xval = find_real_node (data->children);
540                      xval;
541                      xval = find_real_node (xval->next)) {
542                         memset (&gval, 0, sizeof (gval));
543                         if (strcmp ((const char *)xval->name, "value") != 0 ||
544                             !parse_value (xval, &gval)) {
545                                 g_value_array_free (array);
546                                 return FALSE;
547                         }
548
549                         g_value_array_append (array, &gval);
550                         g_value_unset (&gval);
551                 }
552                 g_value_init (value, G_TYPE_VALUE_ARRAY);
553                 g_value_take_boxed (value, array);
554 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
555 G_GNUC_END_IGNORE_DEPRECATIONS
556 #endif
557         } else
558                 return FALSE;
559
560         return TRUE;
561 }
562
563 /**
564  * soup_xmlrpc_parse_method_call:
565  * @method_call: the XML-RPC methodCall string
566  * @length: the length of @method_call, or -1 if it is NUL-terminated
567  * @method_name: (out): on return, the methodName from @method_call
568  * @params: (out): on return, the parameters from @method_call
569  *
570  * Parses @method_call to get the name and parameters, and returns the
571  * parameter values in a #GValueArray; see also
572  * soup_xmlrpc_extract_method_call(), which is more convenient if you
573  * know in advance what the types of the parameters will be.
574  *
575  * Return value: success or failure.
576  **/
577 gboolean
578 soup_xmlrpc_parse_method_call (const char *method_call, int length,
579                                char **method_name, GValueArray **params)
580 {
581         xmlDoc *doc;
582         xmlNode *node, *param, *xval;
583         xmlChar *xmlMethodName = NULL;
584         gboolean success = FALSE;
585         GValue value;
586
587         doc = xmlParseMemory (method_call,
588                               length == -1 ? strlen (method_call) : length);
589         if (!doc)
590                 return FALSE;
591
592         node = xmlDocGetRootElement (doc);
593         if (!node || strcmp ((const char *)node->name, "methodCall") != 0)
594                 goto fail;
595
596         node = find_real_node (node->children);
597         if (!node || strcmp ((const char *)node->name, "methodName") != 0)
598                 goto fail;
599         xmlMethodName = xmlNodeGetContent (node);
600
601         node = find_real_node (node->next);
602         if (!node || 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 = g_value_array_new (1);
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
626         success = TRUE;
627         *method_name = g_strdup ((char *)xmlMethodName);
628
629 fail:
630         xmlFreeDoc (doc);
631         if (xmlMethodName)
632                 xmlFree (xmlMethodName);
633         return success;
634 }
635
636 /**
637  * soup_xmlrpc_extract_method_call:
638  * @method_call: the XML-RPC methodCall string
639  * @length: the length of @method_call, or -1 if it is NUL-terminated
640  * @method_name: (out): on return, the methodName from @method_call
641  * @...: return types and locations for parameters
642  *
643  * Parses @method_call to get the name and parameters, and puts
644  * the parameters into variables of the appropriate types.
645  *
646  * The parameters are handled similarly to
647  * @soup_xmlrpc_build_method_call, with pairs of types and values,
648  * terminated by %G_TYPE_INVALID, except that values are pointers to
649  * variables of the indicated type, rather than values of the type.
650  *
651  * See also soup_xmlrpc_parse_method_call(), which can be used if
652  * you don't know the types of the parameters.
653  *
654  * Return value: success or failure.
655  **/
656 gboolean
657 soup_xmlrpc_extract_method_call (const char *method_call, int length,
658                                  char **method_name, ...)
659 {
660         GValueArray *params;
661         gboolean success;
662         va_list args;
663
664         if (!soup_xmlrpc_parse_method_call (method_call, length,
665                                             method_name, &params))
666                 return FALSE;
667
668         va_start (args, method_name);
669         success = soup_value_array_to_args (params, args);
670         va_end (args);
671
672 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
673 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
674 #endif
675         g_value_array_free (params);
676 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
677 G_GNUC_END_IGNORE_DEPRECATIONS
678 #endif
679         return success;
680 }
681
682 /**
683  * soup_xmlrpc_parse_method_response:
684  * @method_response: the XML-RPC methodResponse string
685  * @length: the length of @method_response, or -1 if it is NUL-terminated
686  * @value: (out): on return, the return value from @method_call
687  * @error: error return value
688  *
689  * Parses @method_response and returns the return value in @value. If
690  * @method_response is a fault, @value will be unchanged, and @error
691  * will be set to an error of type %SOUP_XMLRPC_FAULT, with the error
692  * #code containing the fault code, and the error #message containing
693  * the fault string. (If @method_response cannot be parsed at all,
694  * soup_xmlrpc_parse_method_response() will return %FALSE, but @error
695  * will be unset.)
696  *
697  * Return value: %TRUE if a return value was parsed, %FALSE if the
698  * response could not be parsed, or contained a fault.
699  **/
700 gboolean
701 soup_xmlrpc_parse_method_response (const char *method_response, int length,
702                                    GValue *value, GError **error)
703 {
704         xmlDoc *doc;
705         xmlNode *node;
706         gboolean success = FALSE;
707
708         doc = xmlParseMemory (method_response,
709                               length == -1 ? strlen (method_response) : length);
710         if (!doc)
711                 return FALSE;
712
713         node = xmlDocGetRootElement (doc);
714         if (!node || strcmp ((const char *)node->name, "methodResponse") != 0)
715                 goto fail;
716
717         node = find_real_node (node->children);
718         if (!node)
719                 goto fail;
720
721         if (!strcmp ((const char *)node->name, "fault") && error) {
722                 int fault_code;
723                 char *fault_string;
724                 GValue fault_val;
725                 GHashTable *fault_hash;
726
727                 node = find_real_node (node->children);
728                 if (!node || strcmp ((const char *)node->name, "value") != 0)
729                         goto fail;
730                 if (!parse_value (node, &fault_val))
731                         goto fail;
732                 if (!G_VALUE_HOLDS (&fault_val, G_TYPE_HASH_TABLE)) {
733                         g_value_unset (&fault_val);
734                         goto fail;
735                 }
736                 fault_hash = g_value_get_boxed (&fault_val);
737                 if (!soup_value_hash_lookup (fault_hash, "faultCode",
738                                              G_TYPE_INT, &fault_code) ||
739                     !soup_value_hash_lookup (fault_hash, "faultString",
740                                              G_TYPE_STRING, &fault_string)) {
741                         g_value_unset (&fault_val);
742                         goto fail;
743                 }
744
745                 g_set_error (error, SOUP_XMLRPC_FAULT,
746                              fault_code, "%s", fault_string);
747                 g_value_unset (&fault_val);
748         } else if (!strcmp ((const char *)node->name, "params")) {
749                 node = find_real_node (node->children);
750                 if (!node || strcmp ((const char *)node->name, "param") != 0)
751                         goto fail;
752                 node = find_real_node (node->children);
753                 if (!node || strcmp ((const char *)node->name, "value") != 0)
754                         goto fail;
755                 if (!parse_value (node, value))
756                         goto fail;
757                 success = TRUE;
758         }
759
760 fail:
761         xmlFreeDoc (doc);
762         return success;
763 }
764
765 /**
766  * soup_xmlrpc_extract_method_response:
767  * @method_response: the XML-RPC methodResponse string
768  * @length: the length of @method_response, or -1 if it is NUL-terminated
769  * @error: error return value
770  * @type: the expected type of the return value
771  * @...: location for return value
772  *
773  * Parses @method_response and extracts the return value into
774  * a variable of the correct type.
775  *
776  * If @method_response is a fault, the return value will be unset,
777  * and @error will be set to an error of type %SOUP_XMLRPC_FAULT, with
778  * the error #code containing the fault code, and the error #message
779  * containing the fault string. (If @method_response cannot be parsed
780  * at all, soup_xmlrpc_extract_method_response() will return %FALSE,
781  * but @error will be unset.)
782  *
783  * Return value: %TRUE if a return value was parsed, %FALSE if the
784  * response was of the wrong type, or contained a fault.
785  **/
786 gboolean
787 soup_xmlrpc_extract_method_response (const char *method_response, int length,
788                                      GError **error, GType type, ...)
789 {
790         GValue value;
791         va_list args;
792
793         if (!soup_xmlrpc_parse_method_response (method_response, length,
794                                                 &value, error))
795                 return FALSE;
796         if (!G_VALUE_HOLDS (&value, type))
797                 return FALSE;
798
799         va_start (args, type);
800         SOUP_VALUE_GETV (&value, type, args);
801         va_end (args);
802
803         return TRUE;
804 }
805
806
807 GQuark
808 soup_xmlrpc_error_quark (void)
809 {
810         static GQuark error;
811         if (!error)
812                 error = g_quark_from_static_string ("soup_xmlrpc_error_quark");
813         return error;
814 }
815
816 /**
817  * SOUP_XMLRPC_FAULT:
818  *
819  * A #GError domain representing an XML-RPC fault code. Used with
820  * #SoupXMLRPCFault (although servers may also return fault codes not
821  * in that enumeration).
822  */
823
824 /**
825  * SoupXMLRPCFault:
826  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED: request was not
827  *   well-formed
828  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_UNSUPPORTED_ENCODING: request was in
829  *   an unsupported encoding
830  * @SOUP_XMLRPC_FAULT_PARSE_ERROR_INVALID_CHARACTER_FOR_ENCODING:
831  *   request contained an invalid character
832  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_XML_RPC: request was not
833  *   valid XML-RPC
834  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND: method
835  *   not found
836  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS: invalid
837  *   parameters
838  * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INTERNAL_XML_RPC_ERROR: internal
839  *   error
840  * @SOUP_XMLRPC_FAULT_APPLICATION_ERROR: start of reserved range for
841  *   application error codes
842  * @SOUP_XMLRPC_FAULT_SYSTEM_ERROR: start of reserved range for
843  *   system error codes
844  * @SOUP_XMLRPC_FAULT_TRANSPORT_ERROR: start of reserved range for
845  *   transport error codes
846  *
847  * Pre-defined XML-RPC fault codes from <ulink
848  * url="http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php">http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php</ulink>.
849  * These are an extension, not part of the XML-RPC spec; you can't
850  * assume servers will use them.
851  */
852
853 GQuark
854 soup_xmlrpc_fault_quark (void)
855 {
856         static GQuark error;
857         if (!error)
858                 error = g_quark_from_static_string ("soup_xmlrpc_fault_quark");
859         return error;
860 }
861
862 static xmlNode *
863 find_real_node (xmlNode *node)
864 {
865         while (node && (node->type == XML_COMMENT_NODE ||
866                         xmlIsBlankNode (node)))
867                 node = node->next;
868         return node;
869 }