[libsoup/wip/xmlrpc-variant] xmlrpc: Rework datetime API



commit 22d50107fc5dc54b36082cbde52e9a6fd3c31c59
Author: Xavier Claessens <xavier claessens collabora com>
Date:   Tue Aug 11 16:10:59 2015 -0400

    xmlrpc: Rework datetime API
    
    soup_xmlrpc_variant_new_datetime() now takes a SoupDate.
    <datetime> is no longer parsed to a uint64 but into an opaque
    type that must be parsed using soup_xmlrpc_variant_get_datetime().

 docs/reference/libsoup-2.4-sections.txt |    1 +
 libsoup/libsoup-2.4.sym                 |    1 +
 libsoup/soup-xmlrpc.c                   |  101 ++++++++++++++++++++++++-------
 libsoup/soup-xmlrpc.h                   |    6 ++-
 tests/xmlrpc-server-test.c              |   21 +++++--
 tests/xmlrpc-test.c                     |   46 ++++++++++----
 6 files changed, 135 insertions(+), 41 deletions(-)
---
diff --git a/docs/reference/libsoup-2.4-sections.txt b/docs/reference/libsoup-2.4-sections.txt
index 58f77e4..5185d0a 100644
--- a/docs/reference/libsoup-2.4-sections.txt
+++ b/docs/reference/libsoup-2.4-sections.txt
@@ -839,6 +839,7 @@ soup_xmlrpc_build_request
 soup_xmlrpc_message_new
 soup_xmlrpc_parse_response
 soup_xmlrpc_variant_new_datetime
+soup_xmlrpc_variant_get_datetime
 <SUBSECTION>
 SoupXMLRPCParams
 soup_xmlrpc_params_free
diff --git a/libsoup/libsoup-2.4.sym b/libsoup/libsoup-2.4.sym
index 257d4da..e6ff89e 100644
--- a/libsoup/libsoup-2.4.sym
+++ b/libsoup/libsoup-2.4.sym
@@ -563,4 +563,5 @@ soup_xmlrpc_parse_response
 soup_xmlrpc_request_new
 soup_xmlrpc_set_fault
 soup_xmlrpc_set_response
+soup_xmlrpc_variant_get_datetime
 soup_xmlrpc_variant_new_datetime
diff --git a/libsoup/soup-xmlrpc.c b/libsoup/soup-xmlrpc.c
index 4607eee..e078475 100644
--- a/libsoup/soup-xmlrpc.c
+++ b/libsoup/soup-xmlrpc.c
@@ -955,6 +955,13 @@ parse_base64 (xmlNode *typenode, GError **error)
 }
 
 static GVariant *
+soup_xmlrpc_variant_new_custom (const char *type, const char *v)
+{
+       return g_variant_new ("(oss)", "/org/gnome/libsoup/ExtensionType",
+                             type, v);
+}
+
+static GVariant *
 parse_value (xmlNode *node, const char **signature, GError **error)
 {
        xmlNode *typenode;
@@ -1043,25 +1050,15 @@ parse_value (xmlNode *node, const char **signature, GError **error)
                }
                variant = parse_array (typenode, signature, error);
        } else if (g_str_equal (typename, "dateTime.iso8601")) {
-               SoupDate *date;
-
-               if (class != G_VARIANT_CLASS_VARIANT &&
-                   class != G_VARIANT_CLASS_INT64) {
+               if (class != G_VARIANT_CLASS_VARIANT) {
                        g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
                                     "<dateTime.iso8601> node does not match signature");
                        goto fail;
                }
 
                content = xmlNodeGetContent (typenode);
-               date = soup_date_new_from_string ((char *)content);
-               if (!date) {
-                       g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
-                                    "Couldn't parse date: %s", content);
-                       goto fail;
-               }
-
-               variant = g_variant_new_int64 (soup_date_to_time_t (date));
-               soup_date_free (date);
+               variant = soup_xmlrpc_variant_new_custom ("dateTime.iso8601",
+                                                         (const char *)content);
        } else {
                g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
                             "Unknown node name %s", typename);
@@ -1143,7 +1140,11 @@ soup_xmlrpc_params_new (xmlNode *node)
  *    another element type (e.g. "as") or could be a tuple (e.g. "(ss)").
  *  - &lt;base64&gt; will be deserialized to "ay".
  *  - &lt;string&gt; will be deserialized to "s".
- *  - &lt;dateTime.iso8601&gt; will be deserialized to int64 unix timestamp.
+ *  - &lt;dateTime.iso8601&gt; will be deserialized to an unspecified variant
+ *    type. If @signature is provided it must have the generic "v" type, which
+ *    means there is no guarantee that it's actually a datetime that has been
+ *    received. soup_xmlrpc_variant_get_datetime() must be used to parse and
+ *    type check this special variant.
  *  - @signature must not have maybes, otherwise an error is returned.
  *  - Dictionaries must have string keys, otherwise an error is returned.
  *
@@ -1355,35 +1356,89 @@ fail:
 
 /**
  * soup_xmlrpc_variant_new_datetime:
- * @timestamp: a unix timestamp
+ * @date: a #SoupDate
  *
  * Construct a special #GVariant used to serialize a &lt;dateTime.iso8601&gt;
  * node. See soup_xmlrpc_build_request().
  *
+ * The actual type of the returned #GVariant is unspecified and "v" or "*"
+ * should be used in variant format strings. For example:
+ * <informalexample><programlisting>
+ *     args = g_variant_new ("(v)", soup_xmlrpc_variant_new_datetime (date));
+ * </programlisting></informalexample>
+ *
  * Returns: a floating #GVariant.
  *
  * Since: 2.52
  */
 GVariant *
-soup_xmlrpc_variant_new_datetime (time_t timestamp)
+soup_xmlrpc_variant_new_datetime (SoupDate *date)
 {
-       SoupDate *date;
        GVariant *variant;
        char *str;
 
-       date = soup_date_new_from_time_t (timestamp);
        str = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
-       variant = g_variant_new ("(oss)",
-                                "/org/gnome/libsoup/ExtensionType",
-                                "dateTime.iso8601",
-                                str);
-       soup_date_free (date);
+       variant = soup_xmlrpc_variant_new_custom ("dateTime.iso8601", str);
        g_free (str);
 
        return variant;
 }
 
 /**
+ * soup_xmlrpc_variant_get_datetime:
+ * @variant: a #GVariant
+ * @error: a #GError, or %NULL
+ *
+ * Get the #SoupDate from special #GVariant created by
+ * soup_xmlrpc_variant_new_datetime() or by parsing a &lt;dateTime.iso8601&gt;
+ * node. See soup_xmlrpc_params_parse().
+ *
+ * If @variant does not contain a datetime it will return an error but it is not
+ * considered a programmer error because it generally means parameters received
+ * are not in the expected type.
+ *
+ * Returns: a new #SoupDate, or %NULL on error.
+ *
+ * Since: 2.52
+ */
+SoupDate *
+soup_xmlrpc_variant_get_datetime (GVariant *variant, GError **error)
+{
+       SoupDate *date = NULL;
+       const char *path;
+       const char *type;
+       const char *v;
+
+       if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("(oss)"))) {
+               g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
+                            "Variant is of type '%s' which is not expected for a datetime",
+                            g_variant_get_type_string (variant));
+               return NULL;
+       }
+
+       g_variant_get (variant, "(&o&s&s)", &path, &type, &v);
+
+       if (!g_str_equal (path, "/org/gnome/libsoup/ExtensionType") ||
+           !g_str_equal (type, "dateTime.iso8601")) {
+               g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
+                            "Variant doesn't represent a datetime: %s",
+                            g_variant_get_type_string (variant));
+               return NULL;
+       }
+
+       date = soup_date_new_from_string (v);
+
+       if (date == NULL) {
+               g_set_error (error, SOUP_XMLRPC_ERROR, SOUP_XMLRPC_ERROR_ARGUMENTS,
+                            "Can't parse datetime string: %s", v);
+               return NULL;
+       }
+
+       return date;
+
+}
+
+/**
  * SOUP_XMLRPC_FAULT:
  *
  * A #GError domain representing an XML-RPC fault code. Used with
diff --git a/libsoup/soup-xmlrpc.h b/libsoup/soup-xmlrpc.h
index b51d1bd..bed6c81 100644
--- a/libsoup/soup-xmlrpc.h
+++ b/libsoup/soup-xmlrpc.h
@@ -58,7 +58,11 @@ void         soup_xmlrpc_message_set_fault    (SoupMessage       *msg,
 
 /* Utils */
 SOUP_AVAILABLE_IN_2_52
-GVariant *soup_xmlrpc_variant_new_datetime (time_t       timestamp);
+GVariant *soup_xmlrpc_variant_new_datetime (SoupDate *date);
+
+SOUP_AVAILABLE_IN_2_52
+SoupDate *soup_xmlrpc_variant_get_datetime (GVariant *variant,
+                                           GError  **error);
 
 /* Errors */
 #define SOUP_XMLRPC_ERROR soup_xmlrpc_error_quark()
diff --git a/tests/xmlrpc-server-test.c b/tests/xmlrpc-server-test.c
index 59d3197..db390cc 100644
--- a/tests/xmlrpc-server-test.c
+++ b/tests/xmlrpc-server-test.c
@@ -119,17 +119,25 @@ static void
 do_dateChange (SoupMessage *msg, SoupXMLRPCParams *params)
 {
        GVariant *args;
-       time_t timestamp;
+       GVariant *timestamp;
        SoupDate *date;
        GVariant *arg;
        int val;
+       GError *error = NULL;
 
-       if (!(args = parse_params (msg, params, "(xa{si})")))
+       if (!(args = parse_params (msg, params, "(va{si})")))
                return;
 
-       g_variant_get (args, "(x a{si})", &timestamp, &arg);
+       g_variant_get (args, "(v a{si})", &timestamp, &arg);
 
-       date = soup_date_new_from_time_t (timestamp);
+       date = soup_xmlrpc_variant_get_datetime (timestamp, &error);
+       if (!date) {
+               soup_xmlrpc_message_set_fault (msg,
+                                              SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS,
+                                              "%s", error->message);
+               g_clear_error (&error);
+               goto fail;
+       }
 
        if (g_variant_lookup (arg, "tm_year", "i", &val))
                date->year = val + 1900;
@@ -145,12 +153,15 @@ do_dateChange (SoupMessage *msg, SoupXMLRPCParams *params)
                date->second = val;
 
        soup_xmlrpc_message_set_response (msg,
-                                         soup_xmlrpc_variant_new_datetime (soup_date_to_time_t (date)),
+                                         soup_xmlrpc_variant_new_datetime (date),
                                          NULL);
 
        soup_date_free (date);
+
+fail:
        g_variant_unref (args);
        g_variant_unref (arg);
+       g_variant_unref (timestamp);
 }
 
 static void
diff --git a/tests/xmlrpc-test.c b/tests/xmlrpc-test.c
index b966072..c50995a 100644
--- a/tests/xmlrpc-test.c
+++ b/tests/xmlrpc-test.c
@@ -202,10 +202,11 @@ static void
 test_dateChange (void)
 {
        GVariantDict structval;
-       SoupDate *date;
-       time_t timestamp;
+       SoupDate *date, *result;
+       char *timestamp;
        GVariant *retval;
        gboolean ok;
+       GError *error = NULL;
 
        SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER;
 
@@ -266,22 +267,36 @@ test_dateChange (void)
                                        "i", date->second);
        }
 
-       timestamp = soup_date_to_time_t (date);
-       soup_date_free (date);
-
        debug_printf (2, "} -> ");
 
        ok = do_xmlrpc ("dateChange",
                        g_variant_new ("(vv)",
-                                      soup_xmlrpc_variant_new_datetime (timestamp),
+                                      soup_xmlrpc_variant_new_datetime (date),
                                       g_variant_dict_end (&structval)),
-                       "x", &retval);
-       if (!ok)
+                       NULL, &retval);
+       if (!ok) {
+               soup_date_free (date);
                return;
+       }
 
-       debug_printf (2, "%"G_GINT64_FORMAT"\n", g_variant_get_int64 (retval));
+       result = soup_xmlrpc_variant_get_datetime (retval, &error);
+       g_assert_no_error (error);
 
-       g_assert_cmpint (timestamp, ==, g_variant_get_int64 (retval));
+       if (debug_level >= 2) {
+               timestamp = soup_date_to_string (result, SOUP_DATE_ISO8601_XMLRPC);
+               debug_printf (2, "%s\n", timestamp);
+               g_free (timestamp);
+       }
+
+       g_assert_cmpint (date->year,   ==, result->year);
+       g_assert_cmpint (date->month,  ==, result->month);
+       g_assert_cmpint (date->day,    ==, result->day);
+       g_assert_cmpint (date->hour,   ==, result->hour);
+       g_assert_cmpint (date->minute, ==, result->minute);
+       g_assert_cmpint (date->second, ==, result->second);
+
+       soup_date_free (date);
+       soup_date_free (result);
        g_variant_unref (retval);
 }
 
@@ -474,6 +489,8 @@ verify_serialization_fail (GVariant *value)
 static void
 test_serializer (void)
 {
+       SoupDate *date;
+
        verify_serialization (g_variant_new_parsed ("()"),
                "<params/>");
        verify_serialization (g_variant_new_parsed ("(1, 2)"),
@@ -503,10 +520,12 @@ test_serializer (void)
                "<params>"
                "<param><value><int>42</int></value></param>"
                "</params>");
-       verify_serialization (g_variant_new ("(@*)", soup_xmlrpc_variant_new_datetime (1434161309)),
+       date = soup_date_new_from_time_t (1434161309);
+       verify_serialization (g_variant_new ("(v)", soup_xmlrpc_variant_new_datetime (date)),
                "<params>"
                "<param><value><dateTime.iso8601>20150613T02:08:29</dateTime.iso8601></value></param>"
                "</params>");
+       soup_date_free (date);
        verify_serialization (g_variant_new ("(s)", "<>&"),
                "<params>"
                "<param><value><string>&lt;&gt;&amp;</string></value></param>"
@@ -590,6 +609,7 @@ static void
 test_deserializer (void)
 {
        char *tmp;
+       SoupDate *date;
 
        verify_deserialization (g_variant_new_parsed ("@av []"),
                NULL,
@@ -615,11 +635,13 @@ test_deserializer (void)
                "<member><name>one</name><value><int>1</int></value></member>"
                "<member><name>two</name><value><int>2</int></value></member>"
                "</struct></value></param></params>");
-       verify_deserialization (g_variant_new_parsed ("[<int64 1434146909>]"),
+       date = soup_date_new_from_time_t (1434146909);
+       verify_deserialization (g_variant_new_parsed ("[%v]", soup_xmlrpc_variant_new_datetime (date)),
                NULL,
                "<params>"
                "<param><value><dateTime.iso8601>20150612T22:08:29</dateTime.iso8601></value></param>"
                "</params>");
+       soup_date_free (date);
        verify_deserialization (g_variant_new_parsed ("[<b'bytestring'>]"),
                NULL,
                "<params>"


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]