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 2007 Red Hat, Inc.
6 * Copyright 2007 OpenedHand Ltd.
7 * Copyright 2015 Collabora ltd.
8 *
9 * Author:
10 * Eduardo Lima Mitev <elima@igalia.com>
11 * Xavier Claessens <xavier.claessens@collabora.com>
12 */
13
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17
18 #include <string.h>
19 #include <errno.h>
20 #include <libxml/tree.h>
21 #include "soup-xmlrpc.h"
22 #include "soup.h"
23
24 static gboolean insert_value (xmlNode *parent, GVariant *value, GError **error);
25
26 static gboolean
insert_array(xmlNode * parent,GVariant * value,GError ** error)27 insert_array (xmlNode *parent, GVariant *value, GError **error)
28 {
29 xmlNode *node;
30 GVariantIter iter;
31 GVariant *child;
32
33 node = xmlNewChild (parent, NULL,
34 (const xmlChar *)"array", NULL);
35 node = xmlNewChild (node, NULL,
36 (const xmlChar *)"data", NULL);
37
38 g_variant_iter_init (&iter, value);
39 while ((child = g_variant_iter_next_value (&iter))) {
40 if (!insert_value (node, child, error)) {
41 g_variant_unref (child);
42 return FALSE;
43 }
44 g_variant_unref (child);
45 }
46
47 return TRUE;
48 }
49
50 static gboolean
insert_struct_member(xmlNode * parent,GVariant * value,GError ** error)51 insert_struct_member (xmlNode *parent, GVariant *value, GError **error)
52 {
53 xmlNode *member;
54 GVariant *mname;
55 GVariant *mvalue;
56 gboolean ret = FALSE;
57
58 mname = g_variant_get_child_value (value, 0);
59 mvalue = g_variant_get_child_value (value, 1);
60
61 if (g_variant_classify (mname) != G_VARIANT_CLASS_STRING) {
62 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
63 "Only string keys are supported in dictionaries, got %s",
64 g_variant_get_type_string (mname));
65 goto fail;
66 }
67
68 member = xmlNewChild (parent, NULL,
69 (const xmlChar *)"member", NULL);
70
71 xmlNewTextChild (member, NULL,
72 (const xmlChar *)"name",
73 (const xmlChar *)g_variant_get_string (mname, NULL));
74
75 ret = insert_value (member, mvalue, error);
76
77 fail:
78 g_variant_unref (mname);
79 g_variant_unref (mvalue);
80
81 return ret;
82 }
83
84 static gboolean
insert_struct(xmlNode * parent,GVariant * value,GError ** error)85 insert_struct (xmlNode *parent, GVariant *value, GError **error)
86 {
87 xmlNode *struct_node;
88 GVariantIter iter;
89 GVariant *child;
90
91 struct_node = xmlNewChild (parent, NULL,
92 (const xmlChar *)"struct", NULL);
93
94 g_variant_iter_init (&iter, value);
95 while ((child = g_variant_iter_next_value (&iter))) {
96 if (!insert_struct_member (struct_node, child, error)) {
97 g_variant_unref (child);
98 return FALSE;
99 }
100 g_variant_unref (child);
101 }
102
103 return TRUE;
104 }
105
106 static gboolean
insert_value(xmlNode * parent,GVariant * value,GError ** error)107 insert_value (xmlNode *parent, GVariant *value, GError **error)
108 {
109 xmlNode *xvalue;
110 const char *type_str = NULL;
111 char buf[128];
112
113 xvalue = xmlNewChild (parent, NULL, (const xmlChar *)"value", NULL);
114
115 switch (g_variant_classify (value)) {
116 case G_VARIANT_CLASS_BOOLEAN:
117 g_snprintf (buf, sizeof (buf), "%d", g_variant_get_boolean (value));
118 type_str = "boolean";
119 break;
120 case G_VARIANT_CLASS_BYTE:
121 g_snprintf (buf, sizeof (buf), "%u", g_variant_get_byte (value));
122 type_str = "int";
123 break;
124 case G_VARIANT_CLASS_INT16:
125 g_snprintf (buf, sizeof (buf), "%d", g_variant_get_int16 (value));
126 type_str = "int";
127 break;
128 case G_VARIANT_CLASS_UINT16:
129 g_snprintf (buf, sizeof (buf), "%u", g_variant_get_uint16 (value));
130 type_str = "int";
131 break;
132 case G_VARIANT_CLASS_INT32:
133 g_snprintf (buf, sizeof (buf), "%d", g_variant_get_int32 (value));
134 type_str = "int";
135 break;
136 case G_VARIANT_CLASS_UINT32:
137 g_snprintf (buf, sizeof (buf), "%u", g_variant_get_uint32 (value));
138 type_str = "i8";
139 break;
140 case G_VARIANT_CLASS_INT64:
141 g_snprintf (buf, sizeof (buf), "%"G_GINT64_FORMAT, g_variant_get_int64 (value));
142 type_str = "i8";
143 break;
144 case G_VARIANT_CLASS_DOUBLE:
145 g_ascii_dtostr (buf, sizeof (buf), g_variant_get_double (value));
146 type_str = "double";
147 break;
148 case G_VARIANT_CLASS_STRING:
149 xmlNewTextChild (xvalue, NULL,
150 (const xmlChar *)"string",
151 (const xmlChar *)g_variant_get_string (value, NULL));
152 break;
153 case G_VARIANT_CLASS_VARIANT: {
154 GVariant *child;
155
156 xmlUnlinkNode (xvalue);
157 xmlFreeNode (xvalue);
158
159 child = g_variant_get_variant (value);
160 if (!insert_value (parent, child, error)) {
161 g_variant_unref (child);
162 return FALSE;
163 }
164 g_variant_unref (child);
165 break;
166 }
167 case G_VARIANT_CLASS_ARRAY: {
168 if (g_variant_is_of_type (value, G_VARIANT_TYPE_BYTESTRING)) {
169 char *encoded;
170
171 encoded = g_base64_encode (g_variant_get_data (value),
172 g_variant_get_size (value));
173 xmlNewChild (xvalue, NULL,
174 (const xmlChar *)"base64",
175 (const xmlChar *)encoded);
176 g_free (encoded);
177 } else if (g_variant_is_of_type (value, G_VARIANT_TYPE_DICTIONARY)) {
178 if (!insert_struct (xvalue, value, error))
179 return FALSE;
180 } else {
181 if (!insert_array (xvalue, value, error))
182 return FALSE;
183 }
184
185 break;
186 }
187 case G_VARIANT_CLASS_TUPLE: {
188 /* Special case for custom types */
189 if (g_variant_is_of_type (value, G_VARIANT_TYPE ("(oss)"))) {
190 const char *path;
191 const char *type;
192 const char *v;
193
194 g_variant_get (value, "(&o&s&s)", &path, &type, &v);
195 if (g_str_equal (path, "/org/gnome/libsoup/ExtensionType")) {
196 xmlNewTextChild (xvalue, NULL,
197 (const xmlChar *)type,
198 (const xmlChar *)v);
199 break;
200 }
201 }
202
203 if (!insert_array (xvalue, value, error))
204 return FALSE;
205 break;
206 }
207 case G_VARIANT_CLASS_DICT_ENTRY: {
208 xmlNode *node;
209
210 node = xmlNewChild (xvalue, NULL,
211 (const xmlChar *)"struct", NULL);
212 if (!insert_struct_member (node, value, error))
213 return FALSE;
214 break;
215 }
216 case G_VARIANT_CLASS_HANDLE:
217 case G_VARIANT_CLASS_MAYBE:
218 case G_VARIANT_CLASS_UINT64:
219 case G_VARIANT_CLASS_OBJECT_PATH:
220 case G_VARIANT_CLASS_SIGNATURE:
221 default:
222 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
223 "Unsupported type: %s", g_variant_get_type_string (value));
224 goto fail;
225 }
226
227 if (type_str != NULL) {
228 xmlNewTextChild (xvalue, NULL,
229 (const xmlChar *)type_str,
230 (const xmlChar *)buf);
231 }
232
233 return TRUE;
234
235 fail:
236 return FALSE;
237 }
238
239 /**
240 * soup_xmlrpc_build_request:
241 * @method_name: the name of the XML-RPC method
242 * @params: a #GVariant tuple
243 * @error: a #GError, or %NULL
244 *
245 * This creates an XML-RPC methodCall and returns it as a string.
246 * This is the low-level method that soup_xmlrpc_message_new() is
247 * built on.
248 *
249 * @params is a #GVariant tuple representing the method parameters.
250 *
251 * Serialization details:
252 * - "a{s*}" and "{s*}" are serialized as <struct>
253 * - "ay" is serialized as <base64>
254 * - Other arrays and tuples are serialized as <array>
255 * - booleans are serialized as <boolean>
256 * - byte, int16, uint16 and int32 are serialized as <int>
257 * - uint32 and int64 are serialized as the nonstandard <i8> type
258 * - doubles are serialized as <double>
259 * - Strings are serialized as <string>
260 * - Variants (i.e. "v" type) are unwrapped and their child is serialized.
261 * - #GVariants created by soup_xmlrpc_variant_new_datetime() are serialized as
262 * <dateTime.iso8601>
263 * - Other types are not supported and will return %NULL and set @error.
264 * This notably includes: object-paths, signatures, uint64, handles, maybes
265 * and dictionaries with non-string keys.
266 *
267 * If @params is floating, it is consumed.
268 *
269 * Return value: the text of the methodCall, or %NULL on error.
270 * Since: 2.52
271 **/
272 char *
soup_xmlrpc_build_request(const char * method_name,GVariant * params,GError ** error)273 soup_xmlrpc_build_request (const char *method_name,
274 GVariant *params,
275 GError **error)
276 {
277 xmlDoc *doc;
278 xmlNode *node, *param;
279 xmlChar *xmlbody;
280 GVariantIter iter;
281 GVariant *child;
282 int len;
283 char *body = NULL;
284
285 g_return_val_if_fail (g_variant_is_of_type (params, G_VARIANT_TYPE_TUPLE), NULL);
286
287 g_variant_ref_sink (params);
288
289 doc = xmlNewDoc ((const xmlChar *)"1.0");
290 doc->standalone = FALSE;
291 doc->encoding = xmlCharStrdup ("UTF-8");
292
293 node = xmlNewDocNode (doc, NULL, (const xmlChar *)"methodCall", NULL);
294 xmlDocSetRootElement (doc, node);
295 xmlNewChild (node, NULL, (const xmlChar *)"methodName",
296 (const xmlChar *)method_name);
297
298 node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
299 g_variant_iter_init (&iter, params);
300 while ((child = g_variant_iter_next_value (&iter))) {
301 param = xmlNewChild (node, NULL,
302 (const xmlChar *)"param", NULL);
303 if (!insert_value (param, child, error)) {
304 xmlFreeDoc (doc);
305 g_variant_unref (child);
306 g_variant_unref (params);
307 return NULL;
308 }
309 g_variant_unref (child);
310 }
311
312 xmlDocDumpMemory (doc, &xmlbody, &len);
313 body = g_strndup ((char *)xmlbody, len);
314 xmlFree (xmlbody);
315
316 xmlFreeDoc (doc);
317 g_variant_unref (params);
318
319 return body;
320 }
321
322 /**
323 * soup_xmlrpc_message_new:
324 * @uri: URI of the XML-RPC service
325 * @method_name: the name of the XML-RPC method to invoke at @uri
326 * @params: a #GVariant tuple
327 * @error: a #GError, or %NULL
328 *
329 * Creates an XML-RPC methodCall and returns a #SoupMessage, ready
330 * to send, for that method call.
331 *
332 * See soup_xmlrpc_build_request() for serialization details.
333 *
334 * If @params is floating, it is consumed.
335 *
336 * Returns: (transfer full): a #SoupMessage encoding the
337 * indicated XML-RPC request, or %NULL on error.
338 *
339 * Since: 2.52
340 **/
341 SoupMessage *
soup_xmlrpc_message_new(const char * uri,const char * method_name,GVariant * params,GError ** error)342 soup_xmlrpc_message_new (const char *uri,
343 const char *method_name,
344 GVariant *params,
345 GError **error)
346 {
347 SoupMessage *msg;
348 char *body;
349
350 body = soup_xmlrpc_build_request (method_name, params, error);
351 if (!body)
352 return NULL;
353
354 msg = soup_message_new ("POST", uri);
355 soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
356 body, strlen (body));
357 return msg;
358 }
359
360 /**
361 * soup_xmlrpc_build_response:
362 * @value: the return value
363 * @error: a #GError, or %NULL
364 *
365 * This creates a (successful) XML-RPC methodResponse and returns it
366 * as a string. To create a fault response, use soup_xmlrpc_build_fault(). This
367 * is the low-level method that soup_xmlrpc_message_set_response() is built on.
368 *
369 * See soup_xmlrpc_build_request() for serialization details, but note
370 * that since a method can only have a single return value, @value
371 * should not be a tuple here (unless the return value is an array).
372 *
373 * If @value is floating, it is consumed.
374 *
375 * Returns: the text of the methodResponse, or %NULL on error.
376 *
377 * Since: 2.52
378 **/
379 char *
soup_xmlrpc_build_response(GVariant * value,GError ** error)380 soup_xmlrpc_build_response (GVariant *value, GError **error)
381 {
382 xmlDoc *doc;
383 xmlNode *node;
384 xmlChar *xmlbody;
385 char *body;
386 int len;
387
388 g_variant_ref_sink (value);
389
390 doc = xmlNewDoc ((const xmlChar *)"1.0");
391 doc->standalone = FALSE;
392 doc->encoding = xmlCharStrdup ("UTF-8");
393
394 node = xmlNewDocNode (doc, NULL,
395 (const xmlChar *)"methodResponse", NULL);
396 xmlDocSetRootElement (doc, node);
397
398 node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
399 node = xmlNewChild (node, NULL, (const xmlChar *)"param", NULL);
400 if (!insert_value (node, value, error)) {
401 xmlFreeDoc (doc);
402 g_variant_unref (value);
403 return NULL;
404 }
405
406 xmlDocDumpMemory (doc, &xmlbody, &len);
407 body = g_strndup ((char *)xmlbody, len);
408 xmlFree (xmlbody);
409
410 xmlFreeDoc (doc);
411 g_variant_unref (value);
412
413 return body;
414 }
415
416 char *
417 soup_xmlrpc_build_faultv (int fault_code,
418 const char *fault_format,
419 va_list args) G_GNUC_PRINTF (2, 0);
420
421 char *
soup_xmlrpc_build_faultv(int fault_code,const char * fault_format,va_list args)422 soup_xmlrpc_build_faultv (int fault_code, const char *fault_format, va_list args)
423 {
424 xmlDoc *doc;
425 xmlNode *node, *member;
426 GVariant *value;
427 xmlChar *xmlbody;
428 char *fault_string, *body;
429 int len;
430
431 fault_string = g_strdup_vprintf (fault_format, args);
432
433 doc = xmlNewDoc ((const xmlChar *)"1.0");
434 doc->standalone = FALSE;
435 doc->encoding = xmlCharStrdup ("UTF-8");
436
437 node = xmlNewDocNode (doc, NULL,
438 (const xmlChar *)"methodResponse", NULL);
439 xmlDocSetRootElement (doc, node);
440 node = xmlNewChild (node, NULL, (const xmlChar *)"fault", NULL);
441 node = xmlNewChild (node, NULL, (const xmlChar *)"value", NULL);
442 node = xmlNewChild (node, NULL, (const xmlChar *)"struct", NULL);
443
444 member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
445 xmlNewChild (member, NULL,
446 (const xmlChar *)"name", (const xmlChar *)"faultCode");
447 value = g_variant_new_int32 (fault_code);
448 insert_value (member, value, NULL);
449 g_variant_unref (value);
450
451 member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
452 xmlNewChild (member, NULL,
453 (const xmlChar *)"name", (const xmlChar *)"faultString");
454 value = g_variant_new_take_string (fault_string);
455 insert_value (member, value, NULL);
456 g_variant_unref (value);
457
458 xmlDocDumpMemory (doc, &xmlbody, &len);
459 body = g_strndup ((char *)xmlbody, len);
460 xmlFree (xmlbody);
461 xmlFreeDoc (doc);
462
463 return body;
464 }
465
466 /**
467 * soup_xmlrpc_build_fault:
468 * @fault_code: the fault code
469 * @fault_format: a printf()-style format string
470 * @...: the parameters to @fault_format
471 *
472 * This creates an XML-RPC fault response and returns it as a string.
473 * (To create a successful response, use
474 * soup_xmlrpc_build_method_response().)
475 *
476 * Return value: the text of the fault
477 **/
478 char *
soup_xmlrpc_build_fault(int fault_code,const char * fault_format,...)479 soup_xmlrpc_build_fault (int fault_code, const char *fault_format, ...)
480 {
481 va_list args;
482 char *body;
483
484 va_start (args, fault_format);
485 body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
486 va_end (args);
487 return body;
488 }
489
490 /**
491 * soup_xmlrpc_message_set_fault:
492 * @msg: an XML-RPC request
493 * @fault_code: the fault code
494 * @fault_format: a printf()-style format string
495 * @...: the parameters to @fault_format
496 *
497 * Sets the status code and response body of @msg to indicate an
498 * unsuccessful XML-RPC call, with the error described by @fault_code
499 * and @fault_format.
500 *
501 * Since: 2.52
502 **/
503 void
soup_xmlrpc_message_set_fault(SoupMessage * msg,int fault_code,const char * fault_format,...)504 soup_xmlrpc_message_set_fault (SoupMessage *msg, int fault_code,
505 const char *fault_format, ...)
506 {
507 va_list args;
508 char *body;
509
510 va_start (args, fault_format);
511 body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
512 va_end (args);
513
514 soup_message_set_status (msg, SOUP_STATUS_OK);
515 soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
516 body, strlen (body));
517 }
518
519 /**
520 * soup_xmlrpc_message_set_response:
521 * @msg: an XML-RPC request
522 * @value: a #GVariant
523 * @error: a #GError, or %NULL
524 *
525 * Sets the status code and response body of @msg to indicate a
526 * successful XML-RPC call, with a return value given by @value. To set a
527 * fault response, use soup_xmlrpc_message_set_fault().
528 *
529 * See soup_xmlrpc_build_request() for serialization details.
530 *
531 * If @value is floating, it is consumed.
532 *
533 * Returns: %TRUE on success, %FALSE otherwise.
534 *
535 * Since: 2.52
536 **/
537 gboolean
soup_xmlrpc_message_set_response(SoupMessage * msg,GVariant * value,GError ** error)538 soup_xmlrpc_message_set_response (SoupMessage *msg, GVariant *value, GError **error)
539 {
540 char *body;
541
542 body = soup_xmlrpc_build_response (value, error);
543 if (!body)
544 return FALSE;
545
546 soup_message_set_status (msg, SOUP_STATUS_OK);
547 soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
548 body, strlen (body));
549 return TRUE;
550 }
551
552 static GVariant *parse_value (xmlNode *node, const char **signature, GError **error);
553
554 static xmlNode *
find_real_node(xmlNode * node)555 find_real_node (xmlNode *node)
556 {
557 while (node && (node->type == XML_COMMENT_NODE ||
558 xmlIsBlankNode (node)))
559 node = node->next;
560 return node;
561 }
562
563 static char *
signature_get_next_complete_type(const char ** signature)564 signature_get_next_complete_type (const char **signature)
565 {
566 GVariantClass class;
567 const char *initial_signature;
568 char *result;
569
570 /* here it is assumed that 'signature' is a valid type string */
571
572 initial_signature = *signature;
573 class = (*signature)[0];
574
575 if (class == G_VARIANT_CLASS_TUPLE || class == G_VARIANT_CLASS_DICT_ENTRY) {
576 char stack[256] = {0};
577 guint stack_len = 0;
578
579 do {
580 if ((*signature)[0] == G_VARIANT_CLASS_TUPLE) {
581 stack[stack_len] = ')';
582 stack_len++;
583 }
584 else if ( (*signature)[0] == G_VARIANT_CLASS_DICT_ENTRY) {
585 stack[stack_len] = '}';
586 stack_len++;
587 }
588
589 (*signature)++;
590
591 if ( (*signature)[0] == stack[stack_len - 1])
592 stack_len--;
593 } while (stack_len > 0);
594
595 (*signature)++;
596 } else if (class == G_VARIANT_CLASS_ARRAY || class == G_VARIANT_CLASS_MAYBE) {
597 char *tmp_sig;
598
599 (*signature)++;
600 tmp_sig = signature_get_next_complete_type (signature);
601 g_free (tmp_sig);
602 } else {
603 (*signature)++;
604 }
605
606 result = g_strndup (initial_signature, (*signature) - initial_signature);
607
608 return result;
609 }
610
611 static GVariant *
parse_array(xmlNode * node,const char ** signature,GError ** error)612 parse_array (xmlNode *node, const char **signature, GError **error)
613 {
614 GVariant *variant = NULL;
615 char *child_signature = NULL;
616 char *array_signature = NULL;
617 const char *tmp_signature;
618 gboolean is_tuple = FALSE;
619 xmlNode *member;
620 GVariantBuilder builder;
621 gboolean is_params = FALSE;
622
623 if (signature && *signature[0] == G_VARIANT_CLASS_VARIANT)
624 signature = NULL;
625
626 if (g_str_equal (node->name, "array")) {
627 node = find_real_node (node->children);
628 if (!g_str_equal (node->name, "data")) {
629 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
630 "<data> expected but got '%s'", node->name);
631 goto fail;
632 }
633 } else if (g_str_equal (node->name, "params")) {
634 is_params = TRUE;
635 } else {
636 g_assert_not_reached ();
637 }
638
639 if (signature != NULL) {
640 if ((*signature)[0] == G_VARIANT_CLASS_TUPLE) {
641 tmp_signature = *signature;
642 array_signature = signature_get_next_complete_type (&tmp_signature);
643 is_tuple = TRUE;
644 }
645 (*signature)++;
646 child_signature = signature_get_next_complete_type (signature);
647 } else {
648 child_signature = g_strdup ("v");
649 }
650
651 if (!array_signature)
652 array_signature = g_strdup_printf ("a%s", child_signature);
653 g_variant_builder_init (&builder, G_VARIANT_TYPE (array_signature));
654
655 for (member = find_real_node (node->children);
656 member;
657 member = find_real_node (member->next)) {
658 GVariant *child;
659 xmlNode *xval = member;
660
661 if (is_params) {
662 if (!g_str_equal (member->name, "param")) {
663 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
664 "<param> expected but got '%s'", member->name);
665 goto fail;
666 }
667 xval = find_real_node (member->children);
668 }
669
670 if (strcmp ((const char *)xval->name, "value") != 0) {
671 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
672 "<value> expected but got '%s'", xval->name);
673 goto fail;
674 }
675
676 if (is_tuple && child_signature[0] == ')') {
677 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
678 "Too many values for tuple");
679 goto fail;
680 }
681
682 tmp_signature = child_signature;
683 child = parse_value (xval, &tmp_signature, error);
684 if (child == NULL)
685 goto fail;
686
687 if (is_tuple) {
688 g_free (child_signature),
689 child_signature = signature_get_next_complete_type (signature);
690 }
691
692 g_variant_builder_add_value (&builder, child);
693 }
694
695 if (is_tuple && child_signature[0] != ')') {
696 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
697 "Too few values for tuple");
698 goto fail;
699 }
700
701 variant = g_variant_builder_end (&builder);
702
703 fail:
704 g_variant_builder_clear (&builder);
705 g_free (child_signature);
706 g_free (array_signature);
707
708 /* compensate the (*signature)++ call at the end of 'recurse()' */
709 if (signature)
710 (*signature)--;
711
712 return variant;
713 }
714
715 static void
parse_dict_entry_signature(const char ** signature,char ** entry_signature,char ** key_signature,char ** value_signature)716 parse_dict_entry_signature (const char **signature,
717 char **entry_signature,
718 char **key_signature,
719 char **value_signature)
720 {
721 const char *tmp_sig;
722
723 if (signature)
724 *entry_signature = signature_get_next_complete_type (signature);
725 else
726 *entry_signature = g_strdup ("{sv}");
727
728 tmp_sig = (*entry_signature) + 1;
729 *key_signature = signature_get_next_complete_type (&tmp_sig);
730 *value_signature = signature_get_next_complete_type (&tmp_sig);
731 }
732
733 static GVariant *
parse_dictionary(xmlNode * node,const char ** signature,GError ** error)734 parse_dictionary (xmlNode *node, const char **signature, GError **error)
735 {
736 GVariant *variant = NULL;
737 char *dict_signature;
738 char *entry_signature;
739 char *key_signature;
740 char *value_signature;
741 GVariantBuilder builder;
742 xmlNode *member;
743
744 if (signature && *signature[0] == G_VARIANT_CLASS_VARIANT)
745 signature = NULL;
746
747 if (signature)
748 (*signature)++;
749
750 parse_dict_entry_signature (signature,
751 &entry_signature,
752 &key_signature,
753 &value_signature);
754
755 dict_signature = g_strdup_printf ("a%s", entry_signature);
756 g_variant_builder_init (&builder, G_VARIANT_TYPE (dict_signature));
757
758 if (!g_str_equal (key_signature, "s")) {
759 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
760 "Dictionary key must be string but got '%s'", key_signature);
761 goto fail;
762 }
763
764 for (member = find_real_node (node->children);
765 member;
766 member = find_real_node (member->next)) {
767 xmlNode *child, *mname, *mxval;
768 const char *tmp_signature;
769 GVariant *value;
770 xmlChar *content;
771
772 if (strcmp ((const char *)member->name, "member") != 0) {
773 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
774 "<member> expected but got '%s'", member->name);
775 goto fail;
776 }
777
778 mname = mxval = NULL;
779
780 for (child = find_real_node (member->children);
781 child;
782 child = find_real_node (child->next)) {
783 if (!strcmp ((const char *)child->name, "name"))
784 mname = child;
785 else if (!strcmp ((const char *)child->name, "value"))
786 mxval = child;
787 else {
788 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
789 "<name> or <value> expected but got '%s'", child->name);
790 goto fail;
791 }
792 }
793
794 if (!mname || !mxval) {
795 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
796 "Missing name or value in <member>");
797 goto fail;
798 }
799
800 tmp_signature = value_signature;
801 value = parse_value (mxval, &tmp_signature, error);
802 if (!value)
803 goto fail;
804
805 content = xmlNodeGetContent (mname);
806 g_variant_builder_open (&builder, G_VARIANT_TYPE (entry_signature));
807 g_variant_builder_add (&builder, "s", content);
808 g_variant_builder_add_value (&builder, value);
809 g_variant_builder_close (&builder);
810 xmlFree (content);
811 }
812
813 variant = g_variant_builder_end (&builder);
814
815 fail:
816 g_variant_builder_clear (&builder);
817 g_free (value_signature);
818 g_free (key_signature);
819 g_free (entry_signature);
820 g_free (dict_signature);
821
822 /* compensate the (*signature)++ call at the end of 'recurse()' */
823 if (signature != NULL)
824 (*signature)--;
825
826 return variant;
827 }
828
829 static GVariant *
parse_number(xmlNode * typenode,GVariantClass class,GError ** error)830 parse_number (xmlNode *typenode, GVariantClass class, GError **error)
831 {
832 xmlChar *content;
833 const char *str;
834 char *endptr;
835 gint64 num = 0;
836 guint64 unum = 0;
837 GVariant *variant = NULL;
838
839 content = xmlNodeGetContent (typenode);
840 str = (const char *) content;
841
842 errno = 0;
843
844 if (class == G_VARIANT_CLASS_UINT64)
845 unum = g_ascii_strtoull (str, &endptr, 10);
846 else
847 num = g_ascii_strtoll (str, &endptr, 10);
848
849 if (errno || endptr == str) {
850 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
851 "Couldn't parse number '%s'", str);
852 goto fail;
853 }
854
855 #define RANGE(v, min, max) \
856 G_STMT_START{ \
857 if (v < min || v > max) { \
858 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS, \
859 "Number out of range '%s'", str); \
860 goto fail; \
861 } \
862 } G_STMT_END
863
864 switch (class) {
865 case G_VARIANT_CLASS_BOOLEAN:
866 RANGE (num, 0, 1);
867 variant = g_variant_new_boolean (num);
868 break;
869 case G_VARIANT_CLASS_BYTE:
870 RANGE (num, 0, G_MAXUINT8);
871 variant = g_variant_new_byte (num);
872 break;
873 case G_VARIANT_CLASS_INT16:
874 RANGE (num, G_MININT16, G_MAXINT16);
875 variant = g_variant_new_int16 (num);
876 break;
877 case G_VARIANT_CLASS_UINT16:
878 RANGE (num, 0, G_MAXUINT16);
879 variant = g_variant_new_uint16 (num);
880 break;
881 case G_VARIANT_CLASS_INT32:
882 RANGE (num, G_MININT32, G_MAXINT32);
883 variant = g_variant_new_int32 (num);
884 break;
885 case G_VARIANT_CLASS_UINT32:
886 RANGE (num, 0, G_MAXUINT32);
887 variant = g_variant_new_uint32 (num);
888 break;
889 case G_VARIANT_CLASS_INT64:
890 RANGE (num, G_MININT64, G_MAXINT64);
891 variant = g_variant_new_int64 (num);
892 break;
893 case G_VARIANT_CLASS_UINT64:
894 RANGE (unum, 0, G_MAXUINT64);
895 variant = g_variant_new_uint64 (unum);
896 break;
897 default:
898 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
899 "<%s> node does not match signature",
900 (const char *)typenode->name);
901 goto fail;
902 }
903
904 fail:
905 xmlFree (content);
906
907 return variant;
908 }
909
910 static GVariant *
parse_double(xmlNode * typenode,GError ** error)911 parse_double (xmlNode *typenode, GError **error)
912 {
913 GVariant *variant = NULL;
914 xmlChar *content;
915 const char *str;
916 char *endptr;
917 gdouble d;
918
919 content = xmlNodeGetContent (typenode);
920 str = (const char *) content;
921
922 errno = 0;
923 d = g_ascii_strtod (str, &endptr);
924 if (errno || endptr == str) {
925 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
926 "Couldn't parse double '%s'", str);
927 goto fail;
928 }
929
930 variant = g_variant_new_double (d);
931
932 fail:
933 xmlFree (content);
934
935 return variant;
936 }
937
938 static GVariant *
parse_base64(xmlNode * typenode,GError ** error)939 parse_base64 (xmlNode *typenode, GError **error)
940 {
941 GVariant *variant;
942 xmlChar *content;
943 guchar *decoded;
944 gsize len;
945
946 content = xmlNodeGetContent (typenode);
947 decoded = g_base64_decode ((char *)content, &len);
948 variant = g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
949 decoded, len,
950 TRUE,
951 g_free, decoded);
952 xmlFree (content);
953
954 return variant;
955 }
956
957 static GVariant *
soup_xmlrpc_variant_new_custom(const char * type,const char * v)958 soup_xmlrpc_variant_new_custom (const char *type, const char *v)
959 {
960 return g_variant_new ("(oss)", "/org/gnome/libsoup/ExtensionType",
961 type, v);
962 }
963
964 static GVariant *
parse_value(xmlNode * node,const char ** signature,GError ** error)965 parse_value (xmlNode *node, const char **signature, GError **error)
966 {
967 xmlNode *typenode;
968 const char *typename;
969 xmlChar *content = NULL;
970 GVariant *variant = NULL;
971 GVariantClass class = G_VARIANT_CLASS_VARIANT;
972
973 if (signature)
974 class = *signature[0];
975
976 if (g_str_equal ((const char *)node->name, "value")) {
977 typenode = find_real_node (node->children);
978 if (!typenode) {
979 /* If no typenode, assume value's content is string */
980 typename = "string";
981 typenode = node;
982 } else {
983 typename = (const char *)typenode->name;
984 }
985 } else if (g_str_equal ((const char *)node->name, "params")) {
986 typenode = node;
987 typename = "params";
988 } else {
989 g_assert_not_reached ();
990 }
991
992 if (g_str_equal (typename, "boolean")) {
993 if (class != G_VARIANT_CLASS_VARIANT && class != G_VARIANT_CLASS_BOOLEAN) {
994 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
995 "<boolean> node does not match signature");
996 goto fail;
997 }
998 variant = parse_number (typenode, G_VARIANT_CLASS_BOOLEAN, error);
999 } else if (g_str_equal (typename, "int") || g_str_equal (typename, "i4")) {
1000 if (class == G_VARIANT_CLASS_VARIANT)
1001 variant = parse_number (typenode, G_VARIANT_CLASS_INT32, error);
1002 else
1003 variant = parse_number (typenode, class, error);
1004 } else if (g_str_equal (typename, "i8")) {
1005 if (class == G_VARIANT_CLASS_VARIANT)
1006 variant = parse_number (typenode, G_VARIANT_CLASS_INT64, error);
1007 else
1008 variant = parse_number (typenode, class, error);
1009 } else if (g_str_equal (typename, "double")) {
1010 if (class != G_VARIANT_CLASS_VARIANT && class != G_VARIANT_CLASS_DOUBLE) {
1011 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1012 "<double> node does not match signature");
1013 goto fail;
1014 }
1015 variant = parse_double (typenode, error);
1016 } else if (g_str_equal (typename, "string")) {
1017 if (class != G_VARIANT_CLASS_VARIANT && class != G_VARIANT_CLASS_STRING) {
1018 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1019 "<string> node does not match signature");
1020 goto fail;
1021 }
1022 content = xmlNodeGetContent (typenode);
1023 variant = g_variant_new_string ((const char *)content);
1024 } else if (g_str_equal (typename, "base64")) {
1025 if (class != G_VARIANT_CLASS_VARIANT) {
1026 if (!g_str_has_prefix (*signature, "ay")) {
1027 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1028 "<base64> node does not match signature");
1029 goto fail;
1030 }
1031 (*signature)++;
1032 }
1033 variant = parse_base64 (typenode, error);
1034 } else if (g_str_equal (typename, "struct")) {
1035 if (class != G_VARIANT_CLASS_VARIANT &&
1036 !g_str_has_prefix (*signature, "a{")) {
1037 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1038 "<struct> node does not match signature");
1039 goto fail;
1040 }
1041 variant = parse_dictionary (typenode, signature, error);
1042 } else if (g_str_equal (typename, "array") || g_str_equal (typename, "params")) {
1043 if (class != G_VARIANT_CLASS_VARIANT &&
1044 class != G_VARIANT_CLASS_ARRAY &&
1045 class != G_VARIANT_CLASS_TUPLE) {
1046 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1047 "<%s> node does not match signature", typename);
1048 goto fail;
1049 }
1050 variant = parse_array (typenode, signature, error);
1051 } else if (g_str_equal (typename, "dateTime.iso8601")) {
1052 if (class != G_VARIANT_CLASS_VARIANT) {
1053 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1054 "<dateTime.iso8601> node does not match signature");
1055 goto fail;
1056 }
1057
1058 content = xmlNodeGetContent (typenode);
1059 variant = soup_xmlrpc_variant_new_custom ("dateTime.iso8601",
1060 (const char *)content);
1061 } else {
1062 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1063 "Unknown node name %s", typename);
1064 goto fail;
1065 }
1066
1067 if (variant && signature) {
1068 if (class == G_VARIANT_CLASS_VARIANT)
1069 variant = g_variant_new_variant (variant);
1070 (*signature)++;
1071 }
1072
1073 fail:
1074 if (content)
1075 xmlFree (content);
1076
1077 return variant;
1078 }
1079
1080 /**
1081 * SoupXMLRPCParams:
1082 *
1083 * Opaque structure containing XML-RPC methodCall parameter values.
1084 * Can be parsed using soup_xmlrpc_params_parse() and freed with
1085 * soup_xmlrpc_params_free().
1086 *
1087 * Since: 2.52
1088 */
1089 struct _SoupXMLRPCParams
1090 {
1091 xmlNode *node;
1092 };
1093
1094 /**
1095 * soup_xmlrpc_params_free:
1096 * @self: a SoupXMLRPCParams
1097 *
1098 * Free a #SoupXMLRPCParams returned by soup_xmlrpc_parse_request().
1099 *
1100 * Since: 2.52
1101 */
1102 void
soup_xmlrpc_params_free(SoupXMLRPCParams * self)1103 soup_xmlrpc_params_free (SoupXMLRPCParams *self)
1104 {
1105 g_return_if_fail (self != NULL);
1106
1107 if (self->node)
1108 xmlFreeDoc (self->node->doc);
1109 g_slice_free (SoupXMLRPCParams, self);
1110 }
1111
1112 static SoupXMLRPCParams *
soup_xmlrpc_params_new(xmlNode * node)1113 soup_xmlrpc_params_new (xmlNode *node)
1114 {
1115 SoupXMLRPCParams *self;
1116
1117 self = g_slice_new (SoupXMLRPCParams);
1118 self->node = node;
1119
1120 return self;
1121 }
1122
1123 /**
1124 * soup_xmlrpc_params_parse:
1125 * @self: A #SoupXMLRPCParams
1126 * @signature: (allow-none): A valid #GVariant type string, or %NULL
1127 * @error: a #GError, or %NULL
1128 *
1129 * Parse method parameters returned by soup_xmlrpc_parse_request().
1130 *
1131 * Deserialization details:
1132 * - If @signature is provided, <int> and <i4> can be deserialized
1133 * to byte, int16, uint16, int32, uint32, int64 or uint64. Otherwise
1134 * it will be deserialized to int32. If the value is out of range
1135 * for the target type it will return an error.
1136 * - <struct> will be deserialized to "a{sv}". @signature could define
1137 * another value type (e.g. "a{ss}").
1138 * - <array> will be deserialized to "av". @signature could define
1139 * another element type (e.g. "as") or could be a tuple (e.g. "(ss)").
1140 * - <base64> will be deserialized to "ay".
1141 * - <string> will be deserialized to "s".
1142 * - <dateTime.iso8601> will be deserialized to an unspecified variant
1143 * type. If @signature is provided it must have the generic "v" type, which
1144 * means there is no guarantee that it's actually a datetime that has been
1145 * received. soup_xmlrpc_variant_get_datetime() must be used to parse and
1146 * type check this special variant.
1147 * - @signature must not have maybes, otherwise an error is returned.
1148 * - Dictionaries must have string keys, otherwise an error is returned.
1149 *
1150 * Returns: (transfer full): a new (non-floating) #GVariant, or %NULL
1151 *
1152 * Since: 2.52
1153 */
1154 GVariant *
soup_xmlrpc_params_parse(SoupXMLRPCParams * self,const char * signature,GError ** error)1155 soup_xmlrpc_params_parse (SoupXMLRPCParams *self,
1156 const char *signature,
1157 GError **error)
1158 {
1159 GVariant *value = NULL;
1160
1161 g_return_val_if_fail (self, NULL);
1162 g_return_val_if_fail (!signature || g_variant_type_string_is_valid (signature), NULL);
1163
1164 if (!self->node) {
1165 if (!signature || g_variant_type_equal (G_VARIANT_TYPE (signature), G_VARIANT_TYPE ("av")))
1166 value = g_variant_new_array (G_VARIANT_TYPE_VARIANT, NULL, 0);
1167 else if (g_variant_type_equal (G_VARIANT_TYPE (signature), G_VARIANT_TYPE_UNIT))
1168 value = g_variant_new_tuple (NULL, 0);
1169 else {
1170 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1171 "Invalid signature '%s', was expecting '()'", signature);
1172 goto fail;
1173 }
1174 } else {
1175 value = parse_value (self->node, signature ? &signature : NULL, error);
1176 }
1177
1178 fail:
1179 return value ? g_variant_ref_sink (value) : NULL;
1180 }
1181
1182 /**
1183 * soup_xmlrpc_parse_request:
1184 * @method_call: the XML-RPC methodCall string
1185 * @length: the length of @method_call, or -1 if it is NUL-terminated
1186 * @params: (out): on success, a new #SoupXMLRPCParams
1187 * @error: a #GError, or %NULL
1188 *
1189 * Parses @method_call and return the method name. Method parameters can be
1190 * parsed later using soup_xmlrpc_params_parse().
1191 *
1192 * Returns: (transfer full): method's name, or %NULL on error.
1193 * Since: 2.52
1194 **/
1195 char *
soup_xmlrpc_parse_request(const char * method_call,int length,SoupXMLRPCParams ** params,GError ** error)1196 soup_xmlrpc_parse_request (const char *method_call,
1197 int length,
1198 SoupXMLRPCParams **params,
1199 GError **error)
1200 {
1201 xmlDoc *doc = NULL;
1202 xmlNode *node;
1203 xmlChar *xmlMethodName = NULL;
1204 char *method_name = NULL;
1205
1206 doc = xmlParseMemory (method_call, length == -1 ? strlen (method_call) : length);
1207 if (!doc) {
1208 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1209 "Could not parse XML document");
1210 goto fail;
1211 }
1212
1213 node = xmlDocGetRootElement (doc);
1214 if (!node || strcmp ((const char *)node->name, "methodCall") != 0) {
1215 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1216 "<methodCall> node expected");
1217 goto fail;
1218 }
1219
1220 node = find_real_node (node->children);
1221 if (!node || strcmp ((const char *)node->name, "methodName") != 0) {
1222 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1223 "<methodName> node expected");
1224 goto fail;
1225 }
1226 xmlMethodName = xmlNodeGetContent (node);
1227
1228 if (params) {
1229 node = find_real_node (node->next);
1230 if (node) {
1231 if (strcmp ((const char *)node->name, "params") != 0) {
1232 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1233 "<params> node expected");
1234 goto fail;
1235 }
1236 *params = soup_xmlrpc_params_new (node);
1237 doc = NULL;
1238 } else {
1239 *params = soup_xmlrpc_params_new (NULL);
1240 }
1241 }
1242
1243 method_name = g_strdup ((char *)xmlMethodName);
1244
1245 fail:
1246 if (doc)
1247 xmlFreeDoc (doc);
1248 if (xmlMethodName)
1249 xmlFree (xmlMethodName);
1250
1251 return method_name;
1252 }
1253
1254 /**
1255 * soup_xmlrpc_parse_response:
1256 * @method_response: the XML-RPC methodResponse string
1257 * @length: the length of @method_response, or -1 if it is NUL-terminated
1258 * @signature: (allow-none): A valid #GVariant type string, or %NULL
1259 * @error: a #GError, or %NULL
1260 *
1261 * Parses @method_response and returns the return value. If
1262 * @method_response is a fault, %NULL is returned, and @error
1263 * will be set to an error in the %SOUP_XMLRPC_FAULT domain, with the error
1264 * code containing the fault code, and the error message containing
1265 * the fault string. If @method_response cannot be parsed, %NULL is returned,
1266 * and @error will be set to an error in the %SOUP_XMLRPC_ERROR domain.
1267 *
1268 * See soup_xmlrpc_params_parse() for deserialization details.
1269 *
1270 * Returns: (transfer full): a new (non-floating) #GVariant, or %NULL
1271 *
1272 * Since: 2.52
1273 **/
1274 GVariant *
soup_xmlrpc_parse_response(const char * method_response,int length,const char * signature,GError ** error)1275 soup_xmlrpc_parse_response (const char *method_response,
1276 int length,
1277 const char *signature,
1278 GError **error)
1279 {
1280 xmlDoc *doc = NULL;
1281 xmlNode *node;
1282 GVariant *value = NULL;
1283
1284 g_return_val_if_fail (!signature || g_variant_type_string_is_valid (signature), NULL);
1285
1286 doc = xmlParseMemory (method_response,
1287 length == -1 ? strlen (method_response) : length);
1288 if (!doc) {
1289 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1290 "Failed to parse response XML");
1291 goto fail;
1292 }
1293
1294 node = xmlDocGetRootElement (doc);
1295 if (!node || strcmp ((const char *)node->name, "methodResponse") != 0) {
1296 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1297 "Missing 'methodResponse' node");
1298 goto fail;
1299 }
1300
1301 node = find_real_node (node->children);
1302 if (!node) {
1303 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1304 "'methodResponse' has no child");
1305 goto fail;
1306 }
1307
1308 if (!strcmp ((const char *)node->name, "fault")) {
1309 int fault_code;
1310 const char *fault_string;
1311 const char *fault_sig = "a{sv}";
1312 GVariant *fault_val;
1313
1314 node = find_real_node (node->children);
1315 if (!node || strcmp ((const char *)node->name, "value") != 0) {
1316 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1317 "'fault' has no 'value' child");
1318 goto fail;
1319 }
1320
1321 fault_val = parse_value (node, &fault_sig, error);
1322 if (!fault_val)
1323 goto fail;
1324
1325 if (!g_variant_lookup (fault_val, "faultCode", "i", &fault_code) ||
1326 !g_variant_lookup (fault_val, "faultString", "&s", &fault_string)) {
1327 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1328 "'fault' missing 'faultCode' or 'faultString'");
1329 goto fail;
1330 }
1331 g_set_error (error, SOUP_XMLRPC_FAULT,
1332 fault_code, "%s", fault_string);
1333 g_variant_unref (fault_val);
1334 } else if (!strcmp ((const char *)node->name, "params")) {
1335 node = find_real_node (node->children);
1336 if (!node || strcmp ((const char *)node->name, "param") != 0) {
1337 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1338 "'params' has no 'param' child");
1339 goto fail;
1340 }
1341 node = find_real_node (node->children);
1342 if (!node || strcmp ((const char *)node->name, "value") != 0) {
1343 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1344 "'param' has no 'value' child");
1345 goto fail;
1346 }
1347 value = parse_value (node, signature ? &signature : NULL, error);
1348 }
1349
1350 fail:
1351 if (doc)
1352 xmlFreeDoc (doc);
1353 return value ? g_variant_ref_sink (value) : NULL;
1354 }
1355
1356 /**
1357 * soup_xmlrpc_variant_new_datetime:
1358 * @date: a #SoupDate
1359 *
1360 * Construct a special #GVariant used to serialize a <dateTime.iso8601>
1361 * node. See soup_xmlrpc_build_request().
1362 *
1363 * The actual type of the returned #GVariant is unspecified and "v" or "*"
1364 * should be used in variant format strings. For example:
1365 * <informalexample><programlisting>
1366 * args = g_variant_new ("(v)", soup_xmlrpc_variant_new_datetime (date));
1367 * </programlisting></informalexample>
1368 *
1369 * Returns: a floating #GVariant.
1370 *
1371 * Since: 2.52
1372 */
1373 GVariant *
soup_xmlrpc_variant_new_datetime(SoupDate * date)1374 soup_xmlrpc_variant_new_datetime (SoupDate *date)
1375 {
1376 GVariant *variant;
1377 char *str;
1378
1379 str = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
1380 variant = soup_xmlrpc_variant_new_custom ("dateTime.iso8601", str);
1381 g_free (str);
1382
1383 return variant;
1384 }
1385
1386 /**
1387 * soup_xmlrpc_variant_get_datetime:
1388 * @variant: a #GVariant
1389 * @error: a #GError, or %NULL
1390 *
1391 * Get the #SoupDate from special #GVariant created by
1392 * soup_xmlrpc_variant_new_datetime() or by parsing a <dateTime.iso8601>
1393 * node. See soup_xmlrpc_params_parse().
1394 *
1395 * If @variant does not contain a datetime it will return an error but it is not
1396 * considered a programmer error because it generally means parameters received
1397 * are not in the expected type.
1398 *
1399 * Returns: a new #SoupDate, or %NULL on error.
1400 *
1401 * Since: 2.52
1402 */
1403 SoupDate *
soup_xmlrpc_variant_get_datetime(GVariant * variant,GError ** error)1404 soup_xmlrpc_variant_get_datetime (GVariant *variant, GError **error)
1405 {
1406 SoupDate *date = NULL;
1407 const char *path;
1408 const char *type;
1409 const char *v;
1410
1411 if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("(oss)"))) {
1412 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1413 "Variant is of type '%s' which is not expected for a datetime",
1414 g_variant_get_type_string (variant));
1415 return NULL;
1416 }
1417
1418 g_variant_get (variant, "(&o&s&s)", &path, &type, &v);
1419
1420 if (!g_str_equal (path, "/org/gnome/libsoup/ExtensionType") ||
1421 !g_str_equal (type, "dateTime.iso8601")) {
1422 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1423 "Variant doesn't represent a datetime: %s",
1424 g_variant_get_type_string (variant));
1425 return NULL;
1426 }
1427
1428 date = soup_date_new_from_string (v);
1429
1430 if (date == NULL) {
1431 g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
1432 "Can't parse datetime string: %s", v);
1433 return NULL;
1434 }
1435
1436 return date;
1437
1438 }
1439
1440 /**
1441 * SOUP_XMLRPC_FAULT:
1442 *
1443 * A #GError domain representing an XML-RPC fault code. Used with
1444 * #SoupXMLRPCFault (although servers may also return fault codes not
1445 * in that enumeration).
1446 */
1447
1448 /**
1449 * SoupXMLRPCFault:
1450 * @SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED: request was not
1451 * well-formed
1452 * @SOUP_XMLRPC_FAULT_PARSE_ERROR_UNSUPPORTED_ENCODING: request was in
1453 * an unsupported encoding
1454 * @SOUP_XMLRPC_FAULT_PARSE_ERROR_INVALID_CHARACTER_FOR_ENCODING:
1455 * request contained an invalid character
1456 * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_XML_RPC: request was not
1457 * valid XML-RPC
1458 * @SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND: method
1459 * not found
1460 * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS: invalid
1461 * parameters
1462 * @SOUP_XMLRPC_FAULT_SERVER_ERROR_INTERNAL_XML_RPC_ERROR: internal
1463 * error
1464 * @SOUP_XMLRPC_FAULT_APPLICATION_ERROR: start of reserved range for
1465 * application error codes
1466 * @SOUP_XMLRPC_FAULT_SYSTEM_ERROR: start of reserved range for
1467 * system error codes
1468 * @SOUP_XMLRPC_FAULT_TRANSPORT_ERROR: start of reserved range for
1469 * transport error codes
1470 *
1471 * Pre-defined XML-RPC fault codes from <ulink
1472 * url="http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php">http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php</ulink>.
1473 * These are an extension, not part of the XML-RPC spec; you can't
1474 * assume servers will use them.
1475 */
1476
1477 G_DEFINE_QUARK (soup_xmlrpc_fault_quark, soup_xmlrpc_fault);
1478 G_DEFINE_QUARK (soup_xmlrpc_error_quark, soup_xmlrpc_error);
1479