b166610ac1c5fa721360a64a7bff2d6663b7161a
[platform/upstream/libsoup.git] / libsoup / soup-xmlrpc-response.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-xmlrpc-response.c: XMLRPC response message
4  *
5  * Copyright (C) 2003, Novell, Inc.
6  * Copyright (C) 2004, Mariano Suarez-Alvarez <mariano@gnome.org>
7  * Copyright (C) 2004, Fernando Herrera  <fherrera@onirica.com>
8  * Copyright (C) 2005, Jeff Bailey  <jbailey@ubuntu.com>
9  */
10
11 #ifdef HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14
15 #include <ctype.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <time.h>
19
20 #include <glib.h>
21 #include <libxml/tree.h>
22
23 #include "soup-date.h"
24 #include "soup-misc.h"
25 #include "soup-xmlrpc-response.h"
26
27
28 G_DEFINE_TYPE (SoupXmlrpcResponse, soup_xmlrpc_response, G_TYPE_OBJECT)
29
30 typedef struct {
31         xmlDocPtr doc;
32         gboolean fault;
33         xmlNodePtr value;
34 } SoupXmlrpcResponsePrivate;
35 #define SOUP_XMLRPC_RESPONSE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_XMLRPC_RESPONSE, SoupXmlrpcResponsePrivate))
36
37 static void
38 finalize (GObject *object)
39 {
40         SoupXmlrpcResponsePrivate *priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (object);
41
42         if (priv->doc)
43                 xmlFreeDoc (priv->doc);
44
45         G_OBJECT_CLASS (soup_xmlrpc_response_parent_class)->finalize (object);
46 }
47
48 static void
49 soup_xmlrpc_response_class_init (SoupXmlrpcResponseClass *soup_xmlrpc_response_class)
50 {
51         GObjectClass *object_class = G_OBJECT_CLASS (soup_xmlrpc_response_class);
52
53         g_type_class_add_private (soup_xmlrpc_response_class, sizeof (SoupXmlrpcResponsePrivate));
54
55         object_class->finalize = finalize;
56 }
57
58 static void
59 soup_xmlrpc_response_init (SoupXmlrpcResponse *response)
60 {
61         SoupXmlrpcResponsePrivate *priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (response);
62
63         priv->doc = xmlNewDoc ((const xmlChar *)"1.0");
64         priv->fault = FALSE;
65 }
66
67
68 SoupXmlrpcResponse *
69 soup_xmlrpc_response_new (void)
70 {
71         SoupXmlrpcResponse *response;
72
73         response = g_object_new (SOUP_TYPE_XMLRPC_RESPONSE, NULL);
74         return response;
75 }
76
77 SoupXmlrpcResponse *
78 soup_xmlrpc_response_new_from_string (const char *xmlstr)
79 {
80         SoupXmlrpcResponse *response;
81
82         g_return_val_if_fail (xmlstr != NULL, NULL);
83
84         response = g_object_new (SOUP_TYPE_XMLRPC_RESPONSE, NULL);
85
86         if (!soup_xmlrpc_response_from_string (response, xmlstr)) {
87                 g_object_unref (response);
88                 return NULL;
89         }
90
91         return response;
92 }
93
94 static xmlNode *
95 exactly_one_child (xmlNode *node)
96 {
97         xmlNode *child, *tmp;
98
99         tmp = node->children;
100         while (tmp && xmlIsBlankNode (tmp))
101                 tmp = tmp->next;
102
103         child = tmp;
104         if (tmp && tmp->next) {
105                 tmp = tmp->next;
106                 while (tmp && xmlIsBlankNode (tmp))
107                         tmp = tmp->next;
108                 if (tmp)
109                         return NULL;
110         }
111
112         return child;
113 }
114
115 gboolean
116 soup_xmlrpc_response_from_string (SoupXmlrpcResponse *response, const char *xmlstr)
117 {
118         SoupXmlrpcResponsePrivate *priv;
119         xmlDocPtr newdoc;
120         xmlNodePtr body;
121         gboolean fault = TRUE;
122
123         g_return_val_if_fail (SOUP_IS_XMLRPC_RESPONSE (response), FALSE);
124         priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (response);
125         g_return_val_if_fail (xmlstr != NULL, FALSE);
126
127         xmlKeepBlanksDefault (0);
128         newdoc = xmlParseMemory (xmlstr, strlen (xmlstr));
129         if (!newdoc)
130                 goto very_bad;
131
132         body = xmlDocGetRootElement (newdoc);
133         if (!body || strcmp ((const char *)body->name, "methodResponse"))
134                 goto bad;
135
136         body = exactly_one_child (body);
137         if (!body)
138                 goto bad;
139
140         if (strcmp ((const char *)body->name, "params") == 0) {
141                 fault = FALSE;
142                 body = exactly_one_child (body);
143                 if (!body || strcmp ((const char *)body->name, "param"))
144                         goto bad;
145         } else if (strcmp ((const char *)body->name, "fault") != 0)
146                 goto bad;
147
148         body = exactly_one_child (body);
149         if (!body || strcmp ((const char *)body->name, "value"))
150                 goto bad;
151
152         /* body should be pointing by now to the struct of a fault, or the value of a
153          * normal response
154          */
155
156         xmlFreeDoc (priv->doc);
157         priv->doc = newdoc;
158         priv->value = body;
159         priv->fault = fault;
160
161         return TRUE;
162
163 bad:
164         xmlFreeDoc (newdoc);
165 very_bad:
166         return FALSE;
167 }
168
169 xmlChar *
170 soup_xmlrpc_response_to_string (SoupXmlrpcResponse *response)
171 {
172         SoupXmlrpcResponsePrivate *priv;
173         xmlChar *str;
174         int size;
175
176         g_return_val_if_fail (SOUP_IS_XMLRPC_RESPONSE (response), FALSE);
177         priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (response);
178
179         xmlDocDumpMemoryEnc (priv->doc, &str, &size, "UTF-8");
180
181         return str;
182 }
183
184 gboolean
185 soup_xmlrpc_response_is_fault (SoupXmlrpcResponse *response)
186 {
187         SoupXmlrpcResponsePrivate *priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (response);
188
189         return priv->fault;
190 }
191
192 SoupXmlrpcValue *
193 soup_xmlrpc_response_get_value (SoupXmlrpcResponse *response)
194 {
195         SoupXmlrpcResponsePrivate *priv;
196         g_return_val_if_fail (SOUP_IS_XMLRPC_RESPONSE (response), FALSE);
197         priv = SOUP_XMLRPC_RESPONSE_GET_PRIVATE (response);
198
199         return (SoupXmlrpcValue*) priv->value;
200 }
201
202 SoupXmlrpcValueType
203 soup_xmlrpc_value_get_type (SoupXmlrpcValue *value)
204 {
205         xmlNode *xml;
206
207         xml = (xmlNode *) value;
208
209         if (strcmp ((const char *)xml->name, "value"))
210                 return SOUP_XMLRPC_VALUE_TYPE_BAD;
211
212         xml = exactly_one_child (xml);
213         if (!xml)
214                 return SOUP_XMLRPC_VALUE_TYPE_BAD;
215
216         if (strcmp ((const char *)xml->name, "i4") == 0 || strcmp ((const char *)xml->name, "int") == 0)
217                 return SOUP_XMLRPC_VALUE_TYPE_INT;
218         else if (strcmp ((const char *)xml->name, "boolean") == 0)
219                 return SOUP_XMLRPC_VALUE_TYPE_BOOLEAN;
220         else if (strcmp ((const char *)xml->name, "string") == 0)
221                 return SOUP_XMLRPC_VALUE_TYPE_STRING;
222         else if (strcmp ((const char *)xml->name, "double") == 0)
223                 return SOUP_XMLRPC_VALUE_TYPE_DOUBLE;
224         else if (strcmp ((const char *)xml->name, "dateTime.iso8601") == 0)
225                 return SOUP_XMLRPC_VALUE_TYPE_DATETIME;
226         else if (strcmp ((const char *)xml->name, "base64") == 0)
227                 return SOUP_XMLRPC_VALUE_TYPE_BASE64;
228         else if (strcmp ((const char *)xml->name, "struct") == 0)
229                 return SOUP_XMLRPC_VALUE_TYPE_STRUCT;
230         else if (strcmp ((const char *)xml->name, "array") == 0)
231                 return SOUP_XMLRPC_VALUE_TYPE_ARRAY;
232         else
233                 return SOUP_XMLRPC_VALUE_TYPE_BAD;
234 }
235
236 gboolean
237 soup_xmlrpc_value_get_int (SoupXmlrpcValue *value, long *i)
238 {
239         xmlNode *xml;
240         xmlChar *content;
241         char *tail;
242         gboolean ok;
243
244         xml = (xmlNode *) value;
245
246         if (strcmp ((const char *)xml->name, "value"))
247                 return FALSE;
248         xml = exactly_one_child (xml);
249         if (!xml || (strcmp ((const char *)xml->name, "int") && strcmp ((const char *)xml->name, "i4")))
250                 return FALSE;
251
252         /* FIXME this should be exactly one text node */
253         content = xmlNodeGetContent (xml);
254         *i = strtol ((char *)content, &tail, 10);
255         ok = (*tail == '\0');
256         xmlFree (content);
257
258         return ok;
259 }
260
261 gboolean
262 soup_xmlrpc_value_get_double (SoupXmlrpcValue *value, double *d)
263 {
264         xmlNode *xml;
265         xmlChar *content;
266         char *tail;
267         gboolean ok;
268
269         xml = (xmlNode *) value;
270
271         if (strcmp ((const char *)xml->name, "value"))
272                 return FALSE;
273         xml = exactly_one_child (xml);
274         if (!xml || (strcmp ((const char *)xml->name, "double")))
275                 return FALSE;
276
277         /* FIXME this should be exactly one text node */
278         content = xmlNodeGetContent (xml);
279         *d = g_ascii_strtod ((char *)content, &tail);
280         ok = (*tail == '\0');
281         xmlFree (content);
282
283         return ok;
284 }
285
286 gboolean
287 soup_xmlrpc_value_get_boolean (SoupXmlrpcValue *value, gboolean *b)
288 {
289         xmlNode *xml;
290         xmlChar *content;
291         char *tail;
292         gboolean ok;
293         int i;
294
295         xml = (xmlNode *) value;
296
297         if (strcmp ((const char *)xml->name, "value"))
298                 return FALSE;
299         xml = exactly_one_child (xml);
300         if (!xml || strcmp ((const char *)xml->name, "boolean"))
301                 return FALSE;
302
303         content = xmlNodeGetContent (xml);
304         i = strtol ((char *)content, &tail, 10);
305         *b = (i == 1);
306         ok = (*tail == '\0');
307         xmlFree (content);
308
309         return ok;
310 }
311
312 gboolean
313 soup_xmlrpc_value_get_string (SoupXmlrpcValue *value, char **str)
314 {
315         xmlNode *xml;
316         xmlChar *content;
317
318         xml = (xmlNode *) value;
319
320         if (strcmp ((const char *)xml->name, "value"))
321                 return FALSE;
322         xml = exactly_one_child (xml);
323         if (!xml || strcmp ((const char *)xml->name, "string"))
324                 return FALSE;
325
326         content = xmlNodeGetContent (xml);
327         *str = content ? g_strdup ((char *)content) : g_strdup ("");
328         xmlFree (content);
329
330         return TRUE;
331 }
332
333 gboolean
334 soup_xmlrpc_value_get_datetime (SoupXmlrpcValue *value, time_t *timeval)
335 {
336         xmlNode *xml;
337         xmlChar *content;
338
339         xml = (xmlNode *) value;
340
341         if (strcmp ((const char *)xml->name, "value"))
342                 return FALSE;
343         xml = exactly_one_child (xml);
344         if (!xml || (strcmp ((const char *)xml->name, "dateTime.iso8601")))
345                 return FALSE;
346
347         /* FIXME this should be exactly one text node */
348         content = xmlNodeGetContent (xml);
349         if (xmlStrlen (content) != 17) {
350                 xmlFree (content);
351                 return FALSE;
352         }
353
354         *timeval = soup_date_iso8601_parse ((char *)content);
355         xmlFree (content);
356         return TRUE;
357 }
358
359 gboolean
360 soup_xmlrpc_value_get_base64 (SoupXmlrpcValue *value, GByteArray **data)
361 {
362         xmlNode *xml;
363         xmlChar *content;
364         char *decoded;
365         int len;
366
367         xml = (xmlNode *) value;
368         if (strcmp ((const char *)xml->name, "value"))
369                 return FALSE;
370         xml = exactly_one_child (xml);
371         if (!xml || strcmp ((const char *)xml->name, "base64"))
372                 return FALSE;
373
374         content = xmlNodeGetContent (xml);
375         decoded = soup_base64_decode ((const char *)content, &len);
376         xmlFree (content);
377
378         *data = g_byte_array_new ();
379         g_byte_array_append (*data, (guchar *)decoded, len);
380         g_free (decoded);
381
382         return TRUE;
383 }
384
385
386 gboolean
387 soup_xmlrpc_value_get_struct (SoupXmlrpcValue *value, GHashTable **table)
388 {
389         xmlNode *xml;
390         GHashTable *t;
391
392         xml = (xmlNode *) value;
393
394         if (strcmp ((const char *)xml->name, "value"))
395                 return FALSE;
396         xml = exactly_one_child (xml);
397
398         if (!xml || strcmp ((const char *)xml->name, "struct"))
399                 return FALSE;
400
401         t = g_hash_table_new_full (g_str_hash, g_str_equal, xmlFree, NULL);
402
403         for (xml = xml->children; xml; xml = xml->next) {
404                 xmlChar *name;
405                 xmlNode *val, *cur;
406
407                 if (strcmp ((const char *)xml->name, "member") || !xml->children)
408                         goto bad;
409
410                 name = NULL;
411                 val = NULL;
412
413                 for (cur = xml->children; cur; cur = cur->next) {
414                         if (strcmp((const char *)cur->name, "name") == 0) {
415                                 if (name)
416                                         goto local_bad;
417                                 name = xmlNodeGetContent (cur);
418                         }
419                         else if (strcmp ((const char *)cur->name, "value") == 0)
420                                 val = cur;
421                         else goto local_bad;
422
423                         continue;
424 local_bad:
425                         if (name) xmlFree (name);
426                         goto bad;
427                 }
428
429                 if (!name || !val) {
430                         if (name) xmlFree (name);
431                         goto bad;
432                 }
433                 g_hash_table_insert (t, name, val);
434         }
435
436         *table = t;
437         return TRUE;
438
439 bad:
440         g_hash_table_destroy (t);
441         return FALSE;
442 }
443
444 gboolean
445 soup_xmlrpc_value_array_get_iterator (SoupXmlrpcValue *value, SoupXmlrpcValueArrayIterator **iter)
446 {
447         xmlNode *xml;
448
449         xml = (xmlNode *) value;
450
451         if (!xml->children || strcmp((const char *)xml->children->name, "array") != 0 ||
452             xml->children->next || !xml->children->children ||
453             strcmp((const char *)xml->children->children->name, "data") != 0 ||
454             xml->children->children->next)
455                 return FALSE;
456
457         *iter = (SoupXmlrpcValueArrayIterator *) xml->children->children->children;
458         return TRUE;
459 }
460
461
462 SoupXmlrpcValueArrayIterator *
463 soup_xmlrpc_value_array_iterator_prev (SoupXmlrpcValueArrayIterator *iter)
464 {
465         xmlNode *xml;
466
467         xml = (xmlNode *) iter;
468
469         return (SoupXmlrpcValueArrayIterator *) xml->prev;
470 }
471
472 SoupXmlrpcValueArrayIterator *
473 soup_xmlrpc_value_array_iterator_next (SoupXmlrpcValueArrayIterator *iter)
474 {
475         xmlNode *xml;
476
477         xml = (xmlNode *) iter;
478
479         return (SoupXmlrpcValueArrayIterator *) xml->next;
480 }
481
482 gboolean
483 soup_xmlrpc_value_array_iterator_get_value (SoupXmlrpcValueArrayIterator *iter,
484                                             SoupXmlrpcValue **value)
485 {
486         *value = (SoupXmlrpcValue *) iter;
487
488         return TRUE;
489 }
490
491 static void
492 indent (int d)
493 {
494         while (d--)
495                 g_printerr (" ");
496 }
497
498 static void
499 soup_xmlrpc_value_dump_internal (SoupXmlrpcValue *value, int d);
500
501 static void
502 soup_xmlrpc_value_dump_struct_member (const char *name, SoupXmlrpcValue *value, gpointer d)
503 {
504         indent (GPOINTER_TO_INT (d));
505         g_printerr ("MEMBER: %s\n", name);
506         soup_xmlrpc_value_dump_internal (value, GPOINTER_TO_INT (d));
507 }
508
509 static void
510 soup_xmlrpc_value_dump_array_element (const int i, SoupXmlrpcValue *value, gpointer d)
511 {
512         indent (GPOINTER_TO_INT (d));
513         g_printerr ("ELEMENT: %d\n", i);
514         soup_xmlrpc_value_dump_internal (value, GPOINTER_TO_INT (d));
515 }
516
517 static void
518 soup_xmlrpc_value_dump_internal (SoupXmlrpcValue *value, int d)
519 {
520         long i;
521         gboolean b;
522         char *str;
523         double f;
524         time_t timeval;
525         GByteArray *base64;
526         GHashTable *hash;
527         SoupXmlrpcValueArrayIterator *iter;
528
529         g_printerr ("\n\n[%s]\n", ((xmlNode*)value)->name);
530         switch (soup_xmlrpc_value_get_type (value)) {
531
532                 case SOUP_XMLRPC_VALUE_TYPE_BAD:
533                         indent (d);
534                         g_printerr ("BAD\n");
535                         break;
536
537                 case SOUP_XMLRPC_VALUE_TYPE_INT:
538                         indent (d);
539                         if (!soup_xmlrpc_value_get_int (value, &i))
540                                 g_printerr ("BAD INT\n");
541                         else
542                                 g_printerr ("INT: %ld\n", i);
543                         break;
544
545                 case SOUP_XMLRPC_VALUE_TYPE_BOOLEAN:
546                         indent (d);
547                         if (!soup_xmlrpc_value_get_boolean (value, &b))
548                                 g_printerr ("BAD BOOLEAN\n");
549                         else
550                                 g_printerr ("BOOLEAN: %s\n", b ? "true" : "false");
551                         break;
552
553                 case SOUP_XMLRPC_VALUE_TYPE_STRING:
554                         indent (d);
555                         if (!soup_xmlrpc_value_get_string (value, &str))
556                                 g_printerr ("BAD STRING\n");
557                         else {
558                                 g_printerr ("STRING: \"%s\"\n", str);
559                                 g_free (str);
560                         }
561                         break;
562
563                 case SOUP_XMLRPC_VALUE_TYPE_DOUBLE:
564                         indent (d);
565                         if (!soup_xmlrpc_value_get_double (value, &f))
566                                 g_printerr ("BAD DOUBLE\n");
567                         else
568                                 g_printerr ("DOUBLE: %f\n", f);
569                         break;
570
571                 case SOUP_XMLRPC_VALUE_TYPE_DATETIME:
572                         indent (d);
573                         if (!soup_xmlrpc_value_get_datetime (value, &timeval))
574                                 g_printerr ("BAD DATETIME\n");
575                         else
576                                 g_printerr ("DATETIME: %s\n", asctime (gmtime (&timeval)));
577                         break;
578
579                 case SOUP_XMLRPC_VALUE_TYPE_BASE64:
580                         indent (d);
581                         if (!soup_xmlrpc_value_get_base64 (value, &base64))
582                                 g_printerr ("BAD BASE64\n");
583                         else {
584                                 GString *hex = g_string_new (NULL);
585                                 int i;
586
587                                 for (i = 0; i < base64->len; i++)
588                                         g_string_append_printf (hex, "%02x", base64->data[i]);
589
590                                 g_printerr ("BASE64: %s\n", hex->str);
591                                 g_string_free (hex, TRUE);
592                                 g_byte_array_free (base64, TRUE);
593                         }
594
595                         break;
596
597                 case SOUP_XMLRPC_VALUE_TYPE_STRUCT:
598                         indent (d);
599                         if (!soup_xmlrpc_value_get_struct (value, &hash))
600                                 g_printerr ("BAD STRUCT\n");
601                         else {
602                                 g_printerr ("STRUCT\n");
603                                 g_hash_table_foreach (hash, (GHFunc) soup_xmlrpc_value_dump_struct_member,
604                                                       GINT_TO_POINTER (d+1));
605                                 g_hash_table_destroy (hash);
606                         }
607                         break;
608
609                 case SOUP_XMLRPC_VALUE_TYPE_ARRAY:
610                         indent (d);
611                         if (!soup_xmlrpc_value_array_get_iterator (value, &iter))
612                                 g_printerr ("BAD ARRAY\n");
613                         else {
614                                 SoupXmlrpcValue *evalue;
615                                 int i = 0;
616                                 g_printerr ("ARRAY\n");
617                                 while (iter != NULL) {
618                                         soup_xmlrpc_value_array_iterator_get_value (iter, &evalue);
619                                         soup_xmlrpc_value_dump_array_element (i, evalue, GINT_TO_POINTER (d+1));
620                                         iter = soup_xmlrpc_value_array_iterator_next (iter);
621                                         i++;
622                                 }
623                         }
624                         break;
625         }
626
627 }
628
629 void
630 soup_xmlrpc_value_dump (SoupXmlrpcValue *value)
631 {
632         soup_xmlrpc_value_dump_internal (value, 0);
633 }
634