[gupnp/wip/phako/new-api: 7/7] wip: service-proxy-action new api
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gupnp/wip/phako/new-api: 7/7] wip: service-proxy-action new api
- Date: Sun, 13 Jan 2019 15:26:54 +0000 (UTC)
commit 1a19682cf42968b144a82736c26cadcb6f857648
Author: Jens Georg <mail jensge org>
Date: Sun Jan 13 16:24:51 2019 +0100
wip: service-proxy-action new api
examples/light-client.c | 36 +-
libgupnp/gupnp-service-proxy-action-private.h | 96 +++-
libgupnp/gupnp-service-proxy-action.c | 210 ++++++++-
libgupnp/gupnp-service-proxy.c | 655 ++++++++++----------------
libgupnp/gupnp-service-proxy.h | 45 +-
5 files changed, 611 insertions(+), 431 deletions(-)
---
diff --git a/examples/light-client.c b/examples/light-client.c
index 82ee54c..359bc3e 100644
--- a/examples/light-client.c
+++ b/examples/light-client.c
@@ -39,16 +39,28 @@ send_cmd (GUPnPServiceProxy *proxy)
GError *error = NULL;
gboolean target;
+ GUPnPServiceProxyAction *action;
+
if (mode == TOGGLE) {
/* We're toggling, so first fetch the current status */
- if (!gupnp_service_proxy_send_action
- (proxy, "GetStatus", &error,
- /* IN args */ NULL,
- /* OUT args */ "ResultStatus", G_TYPE_BOOLEAN, &target, NULL)) {
+
+ action = gupnp_service_proxy_action_new ("GetStatus", NULL);
+
+ gupnp_service_proxy_call_action (proxy, action, NULL, &error);
+ if (error != NULL)
goto error;
- }
+
+ gupnp_service_proxy_action_get_result (action,
+ &error,
+ "ResultStatus", G_TYPE_BOOLEAN, &target, NULL);
+ g_clear_pointer (&action, gupnp_service_proxy_action_unref);
+
+ if (error != NULL)
+ goto error;
+
/* And then toggle it */
target = ! target;
+
} else {
/* Mode is a boolean, so the target is the mode thanks to our well chosen
enumeration values. */
@@ -56,12 +68,13 @@ send_cmd (GUPnPServiceProxy *proxy)
}
/* Set the target */
- if (!gupnp_service_proxy_send_action (proxy, "SetTarget", &error,
- /* IN args */
- "newTargetValue", G_TYPE_BOOLEAN, target, NULL,
- /* OUT args */
- NULL)) {
- goto error;
+
+ action = gupnp_service_proxy_action_new ("SetTarget",
+ "newTargetValue", G_TYPE_BOOLEAN, target, NULL);
+ gupnp_service_proxy_call_action (proxy, action, NULL, &error);
+ g_clear_pointer (&action, gupnp_service_proxy_action_unref);
+ if (error != NULL) {
+ goto error;
} else {
if (!quiet) {
g_print ("Set switch to %s.\n", target ? "on" : "off");
@@ -76,6 +89,7 @@ send_cmd (GUPnPServiceProxy *proxy)
return;
error:
+ g_clear_pointer (&action, gupnp_service_proxy_action_unref);
g_printerr ("Cannot set switch: %s\n", error->message);
g_error_free (error);
goto done;
diff --git a/libgupnp/gupnp-service-proxy-action-private.h b/libgupnp/gupnp-service-proxy-action-private.h
index 53de39f..26c78de 100644
--- a/libgupnp/gupnp-service-proxy-action-private.h
+++ b/libgupnp/gupnp-service-proxy-action-private.h
@@ -38,6 +38,92 @@ G_BEGIN_DECLS
values = g_list_reverse (values); \
} G_STMT_END
+/* This is a skip variant of G_VALUE_LCOPY, same as there is
+ * G_VALUE_COLLECT_SKIP for G_VALUE_COLLECT.
+ */
+#define VALUE_LCOPY_SKIP(value_type, var_args) \
+ G_STMT_START { \
+ GTypeValueTable *_vtable = g_type_value_table_peek (value_type); \
+ const gchar *_lcopy_format = _vtable->lcopy_format; \
+ \
+ while (*_lcopy_format) { \
+ switch (*_lcopy_format++) { \
+ case G_VALUE_COLLECT_INT: \
+ va_arg ((var_args), gint); \
+ break; \
+ case G_VALUE_COLLECT_LONG: \
+ va_arg ((var_args), glong); \
+ break; \
+ case G_VALUE_COLLECT_INT64: \
+ va_arg ((var_args), gint64); \
+ break; \
+ case G_VALUE_COLLECT_DOUBLE: \
+ va_arg ((var_args), gdouble); \
+ break; \
+ case G_VALUE_COLLECT_POINTER: \
+ va_arg ((var_args), gpointer); \
+ break; \
+ default: \
+ g_assert_not_reached (); \
+ } \
+ } \
+ } G_STMT_END
+
+/* Initializes hash table to hold arg names as keys and GValues of
+ * given type, but without any specific value. Note that if you are
+ * going to use OUT_HASH_TABLE_TO_VAR_ARGS then you have to store a
+ * copy of var_args with G_VA_COPY before using this macro.
+ */
+#define VAR_ARGS_TO_OUT_HASH_TABLE(var_args, hash) \
+ G_STMT_START { \
+ const gchar *arg_name = va_arg (var_args, const gchar *); \
+ \
+ while (arg_name != NULL) { \
+ GValue *value = g_new0 (GValue, 1); \
+ GType type = va_arg (var_args, GType); \
+ \
+ VALUE_LCOPY_SKIP (type, var_args); \
+ g_value_init (value, type); \
+ g_hash_table_insert (hash, g_strdup (arg_name), value); \
+ arg_name = va_arg (var_args, const gchar *); \
+ } \
+ } G_STMT_END
+
+/* Puts values stored in hash table with GValues into var args.
+ */
+#define OUT_HASH_TABLE_TO_VAR_ARGS(hash, var_args) \
+ G_STMT_START { \
+ const gchar *arg_name = va_arg (var_args, const gchar *); \
+ \
+ while (arg_name != NULL) { \
+ GValue *value = g_hash_table_lookup (hash, arg_name); \
+ GType type = va_arg (var_args, GType); \
+ \
+ if (value == NULL) { \
+ g_warning ("No value for %s", arg_name); \
+ G_VALUE_COLLECT_SKIP (type, var_args); \
+ } else if (G_VALUE_TYPE (value) != type) { \
+ g_warning ("Different GType in value (%s) and in var args (%s) for %s.", \
+ G_VALUE_TYPE_NAME (value), \
+ g_type_name (type), \
+ arg_name); \
+ } else { \
+ gchar *__error = NULL; \
+ \
+ G_VALUE_LCOPY (value, var_args, 0, &__error); \
+ if (__error != NULL) { \
+ g_warning ("Failed to lcopy the value of type %s for %s: %s", \
+ g_type_name (type), \
+ arg_name, \
+ __error); \
+ g_free (__error); \
+ } \
+ } \
+ arg_name = va_arg (var_args, const gchar *); \
+ } \
+ } G_STMT_END
+
+
struct _GUPnPServiceProxyAction {
GUPnPServiceProxy *proxy;
char *name;
@@ -56,12 +142,10 @@ struct _GUPnPServiceProxyAction {
G_GNUC_INTERNAL GUPnPServiceProxyAction *
gupnp_service_proxy_action_new_internal (const char *action);
-G_GNUC_INTERNAL GUPnPServiceProxyAction *
-gupnp_service_proxy_action_ref (GUPnPServiceProxyAction *action);
-
-G_GNUC_INTERNAL void
-gupnp_service_proxy_action_unref (GUPnPServiceProxyAction *action);
-
+G_GNUC_INTERNAL gboolean
+gupnp_service_proxy_action_get_result_valist (GUPnPServiceProxyAction *action,
+ GError **error,
+ va_list var_args);
G_END_DECLS
diff --git a/libgupnp/gupnp-service-proxy-action.c b/libgupnp/gupnp-service-proxy-action.c
index 1e8ea04..93880e7 100644
--- a/libgupnp/gupnp-service-proxy-action.c
+++ b/libgupnp/gupnp-service-proxy-action.c
@@ -25,6 +25,25 @@
#include "gvalue-util.h"
#include "xml-util.h"
+/* Checks an action response for errors and returns the parsed
+ * xmlDoc object. */
+
+G_GNUC_INTERNAL xmlDoc *
+_check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ xmlNode **params,
+ GError **error);
+
+G_GNUC_INTERNAL void
+_read_out_parameter (const char *arg_name,
+ GValue *value,
+ xmlNode *params);
+
+/* GDestroyNotify for GHashTable holding GValues.
+ */
+G_GNUC_INTERNAL void
+_value_free (gpointer data);
+
GUPnPServiceProxyAction *
gupnp_service_proxy_action_new_internal (const char *action) {
GUPnPServiceProxyAction *ret;
@@ -41,6 +60,7 @@ GUPnPServiceProxyAction *
gupnp_service_proxy_action_ref (GUPnPServiceProxyAction *action)
{
g_return_val_if_fail (action, NULL);
+ g_debug ("=> action_action_ref");
return g_atomic_rc_box_acquire (action);
}
@@ -48,12 +68,14 @@ gupnp_service_proxy_action_ref (GUPnPServiceProxyAction *action)
static void
action_dispose (GUPnPServiceProxyAction *action)
{
+ g_debug ("=> action_dispose");
if (action->proxy != NULL) {
g_object_remove_weak_pointer (G_OBJECT (action->proxy),
(gpointer *)&(action->proxy));
gupnp_service_proxy_remove_action (action->proxy, action);
}
+ g_clear_error (&action->error);
g_clear_object (&action->msg);
g_free (action->name);
}
@@ -62,6 +84,7 @@ void
gupnp_service_proxy_action_unref (GUPnPServiceProxyAction *action)
{
g_return_if_fail (action);
+ g_debug ("=> action_unref");
g_atomic_rc_box_release_full (action, (GDestroyNotify) action_dispose);
}
@@ -100,9 +123,10 @@ GUPnPServiceProxyAction *
gupnp_service_proxy_action_new (const char *action,
...)
{
- va_list var_args;
GList *in_names = NULL;
GList *in_values = NULL;
+ GUPnPServiceProxyAction *result = NULL;
+ va_list var_args;
g_return_val_if_fail (action != NULL, NULL);
@@ -110,11 +134,14 @@ gupnp_service_proxy_action_new (const char *action,
VAR_ARGS_TO_IN_LIST (var_args, in_names, in_values);
va_end (var_args);
- return gupnp_service_proxy_action_new_from_list (action,
- in_names,
- in_values);;
-}
+ result = gupnp_service_proxy_action_new_from_list (action,
+ in_names,
+ in_values);;
+ g_list_free_full (in_names, g_free);
+ g_list_free_full (in_values, _value_free);
+ return result;
+}
/**
* gupnp_service_proxy_action_new_from_list:
@@ -161,3 +188,176 @@ gupnp_service_proxy_action_new_from_list (const char *action_name,
return action;
}
+/**
+ * gupnp_service_proxy_action_get_result_list:
+ * @action: A #GUPnPServiceProxyAction handle
+ * @out_names: (element-type utf8) (transfer none): #GList of 'out' parameter
+ * names (as strings)
+ * @out_types: (element-type GType) (transfer none): #GList of types (as #GType)
+ * that line up with @out_names
+ * @out_values: (element-type GValue) (transfer full) (out): #GList of values
+ * (as #GValue) that line up with @out_names and @out_types.
+ * @error: The location where to store any error, or %NULL
+ *
+ * A variant of gupnp_service_proxy_action_get_result() that takes lists of
+ * out-parameter names, types and place-holders for values. The returned list
+ * in @out_values must be freed using #g_list_free and each element in it using
+ * #g_value_unset and #g_free.
+ *
+ * Return value : %TRUE on success.
+ *
+ **/
+gboolean
+gupnp_service_proxy_action_get_result_list (GUPnPServiceProxyAction *action,
+ GList *out_names,
+ GList *out_types,
+ GList **out_values,
+ GError **error)
+{
+ xmlDoc *response;
+ xmlNode *params;
+ GList *names;
+ GList *types;
+ GList *out_values_list;
+
+ out_values_list = NULL;
+
+ g_return_val_if_fail (action, FALSE);
+
+ /* Check for saved error from begin_action() */
+ if (action->error) {
+ g_propagate_error (error, g_error_copy (action->error));
+
+ return FALSE;
+ }
+
+ /* Check response for errors and do initial parsing */
+ response = _check_action_response (NULL, action, ¶ms, &action->error);
+ if (response == NULL) {
+ g_propagate_error (error, g_error_copy (action->error));
+
+ return FALSE;
+ }
+
+ /* Read arguments */
+ types = out_types;
+ for (names = out_names; names; names=names->next) {
+ GValue *val;
+
+ val = g_new0 (GValue, 1);
+ g_value_init (val, (GType) types->data);
+
+ _read_out_parameter (names->data, val, params);
+
+ out_values_list = g_list_append (out_values_list, val);
+
+ types = types->next;
+ }
+
+ *out_values = out_values_list;
+
+ /* Cleanup */
+ xmlFreeDoc (response);
+
+ return TRUE;
+
+}
+
+/**
+ * gupnp_service_proxy_action_get_result_hash:
+ * @action: A #GUPnPServiceProxyAction handle
+ * @out_hash: (element-type utf8 GValue) (inout) (transfer none): A #GHashTable of
+ * out parameter name and initialised #GValue pairs
+ * @error: The location where to store any error, or %NULL
+ *
+ * See gupnp_service_proxy_action_get_result(); this version takes a #GHashTable for
+ * runtime generated parameter lists.
+ *
+ * Return value: %TRUE on success.
+ *
+ **/
+gboolean
+gupnp_service_proxy_action_get_result_hash (GUPnPServiceProxyAction *action,
+ GHashTable *hash,
+ GError **error)
+{
+ xmlDoc *response;
+ xmlNode *params;
+
+ g_return_val_if_fail (action, FALSE);
+
+ /* Check for saved error from begin_action() */
+ if (action->error) {
+ g_propagate_error (error, g_error_copy (action->error));
+
+ return FALSE;
+ }
+
+ /* Check response for errors and do initial parsing */
+ response = _check_action_response (NULL, action, ¶ms, &action->error);
+ if (response == NULL) {
+ g_propagate_error (error, g_error_copy (action->error));
+
+ return FALSE;
+ }
+
+ /* Read arguments */
+ g_hash_table_foreach (hash, (GHFunc) _read_out_parameter, params);
+
+ /* Cleanup */
+ xmlFreeDoc (response);
+
+ return TRUE;
+
+}
+
+gboolean
+gupnp_service_proxy_action_get_result (GUPnPServiceProxyAction *action,
+ GError **error,
+ ...)
+{
+ va_list var_args;
+ gboolean ret;
+
+ va_start (var_args, error);
+ ret = gupnp_service_proxy_action_get_result_valist (action,
+ error,
+ var_args);
+ va_end (var_args);
+
+ return ret;
+}
+
+gboolean
+gupnp_service_proxy_action_get_result_valist (GUPnPServiceProxyAction *action,
+ GError **error,
+ va_list var_args)
+{
+ GHashTable *out_hash;
+ va_list var_args_copy;
+ gboolean result;
+ GError *local_error;
+
+ g_return_val_if_fail (action, FALSE);
+
+ out_hash = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ _value_free);
+ G_VA_COPY (var_args_copy, var_args);
+ VAR_ARGS_TO_OUT_HASH_TABLE (var_args, out_hash);
+ local_error = NULL;
+ result = gupnp_service_proxy_action_get_result_hash (action,
+ out_hash,
+ &local_error);
+
+ if (local_error == NULL) {
+ OUT_HASH_TABLE_TO_VAR_ARGS (out_hash, var_args_copy);
+ } else {
+ g_propagate_error (error, local_error);
+ }
+ va_end (var_args_copy);
+ g_hash_table_unref (out_hash);
+
+ return result;
+}
diff --git a/libgupnp/gupnp-service-proxy.c b/libgupnp/gupnp-service-proxy.c
index 9805f98..4e03774 100644
--- a/libgupnp/gupnp-service-proxy.c
+++ b/libgupnp/gupnp-service-proxy.c
@@ -411,103 +411,10 @@ gupnp_service_proxy_send_action (GUPnPServiceProxy *proxy,
return ret;
}
-static void
-stop_main_loop (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
- G_GNUC_UNUSED GUPnPServiceProxyAction *action,
- gpointer user_data)
-{
- g_main_loop_quit ((GMainLoop *) user_data);
-}
-
-/* This is a skip variant of G_VALUE_LCOPY, same as there is
- * G_VALUE_COLLECT_SKIP for G_VALUE_COLLECT.
- */
-#define VALUE_LCOPY_SKIP(value_type, var_args) \
- G_STMT_START { \
- GTypeValueTable *_vtable = g_type_value_table_peek (value_type); \
- const gchar *_lcopy_format = _vtable->lcopy_format; \
- \
- while (*_lcopy_format) { \
- switch (*_lcopy_format++) { \
- case G_VALUE_COLLECT_INT: \
- va_arg ((var_args), gint); \
- break; \
- case G_VALUE_COLLECT_LONG: \
- va_arg ((var_args), glong); \
- break; \
- case G_VALUE_COLLECT_INT64: \
- va_arg ((var_args), gint64); \
- break; \
- case G_VALUE_COLLECT_DOUBLE: \
- va_arg ((var_args), gdouble); \
- break; \
- case G_VALUE_COLLECT_POINTER: \
- va_arg ((var_args), gpointer); \
- break; \
- default: \
- g_assert_not_reached (); \
- } \
- } \
- } G_STMT_END
-
-/* Initializes hash table to hold arg names as keys and GValues of
- * given type, but without any specific value. Note that if you are
- * going to use OUT_HASH_TABLE_TO_VAR_ARGS then you have to store a
- * copy of var_args with G_VA_COPY before using this macro.
- */
-#define VAR_ARGS_TO_OUT_HASH_TABLE(var_args, hash) \
- G_STMT_START { \
- const gchar *arg_name = va_arg (var_args, const gchar *); \
- \
- while (arg_name != NULL) { \
- GValue *value = g_new0 (GValue, 1); \
- GType type = va_arg (var_args, GType); \
- \
- VALUE_LCOPY_SKIP (type, var_args); \
- g_value_init (value, type); \
- g_hash_table_insert (hash, g_strdup (arg_name), value); \
- arg_name = va_arg (var_args, const gchar *); \
- } \
- } G_STMT_END
-
-/* Puts values stored in hash table with GValues into var args.
- */
-#define OUT_HASH_TABLE_TO_VAR_ARGS(hash, var_args) \
- G_STMT_START { \
- const gchar *arg_name = va_arg (var_args, const gchar *); \
- \
- while (arg_name != NULL) { \
- GValue *value = g_hash_table_lookup (hash, arg_name); \
- GType type = va_arg (var_args, GType); \
- \
- if (value == NULL) { \
- g_warning ("No value for %s", arg_name); \
- G_VALUE_COLLECT_SKIP (type, var_args); \
- } else if (G_VALUE_TYPE (value) != type) { \
- g_warning ("Different GType in value (%s) and in var args (%s) for %s.", \
- G_VALUE_TYPE_NAME (value), \
- g_type_name (type), \
- arg_name); \
- } else { \
- gchar *__error = NULL; \
- \
- G_VALUE_LCOPY (value, var_args, 0, &__error); \
- if (__error != NULL) { \
- g_warning ("Failed to lcopy the value of type %s for %s: %s", \
- g_type_name (type), \
- arg_name, \
- __error); \
- g_free (__error); \
- } \
- } \
- arg_name = va_arg (var_args, const gchar *); \
- } \
- } G_STMT_END
-
/* GDestroyNotify for GHashTable holding GValues.
*/
-static void
-value_free (gpointer data)
+void
+_value_free (gpointer data)
{
GValue *value = (GValue *) data;
@@ -530,7 +437,7 @@ value_free (gpointer data)
**/
gboolean
gupnp_service_proxy_send_action_valist (GUPnPServiceProxy *proxy,
- const char *action,
+ const char *action_name,
GError **error,
va_list var_args)
{
@@ -539,48 +446,30 @@ gupnp_service_proxy_send_action_valist (GUPnPServiceProxy *proxy,
va_list var_args_copy;
gboolean result;
GError *local_error;
- GMainLoop *main_loop;
GUPnPServiceProxyAction *handle;
g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
- g_return_val_if_fail (action, FALSE);
-
+ g_return_val_if_fail (action_name, FALSE);
- main_loop = g_main_loop_new (g_main_context_get_thread_default (),
- TRUE);
VAR_ARGS_TO_IN_LIST (var_args, in_names, in_values);
G_VA_COPY (var_args_copy, var_args);
out_hash = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
- value_free);
+ _value_free);
VAR_ARGS_TO_OUT_HASH_TABLE (var_args, out_hash);
- local_error = NULL;
- handle = gupnp_service_proxy_begin_action_list (proxy,
- action,
- in_names,
- in_values,
- stop_main_loop,
- main_loop);
- if (!handle) {
- g_main_loop_unref (main_loop);
- result = FALSE;
+ handle = gupnp_service_proxy_action_new_from_list (action_name, in_names, in_values);
+ if (gupnp_service_proxy_call_action (proxy, handle, NULL, error) != NULL) {
+ result = FALSE;
goto out;
}
- /* Loop till we get a reply (or time out) */
- if (g_main_loop_is_running (main_loop))
- g_main_loop_run (main_loop);
-
- g_main_loop_unref (main_loop);
-
- result = gupnp_service_proxy_end_action_hash (proxy,
- handle,
- out_hash,
- &local_error);
+ result = gupnp_service_proxy_action_get_result_hash (handle,
+ out_hash,
+ &local_error);
if (local_error == NULL) {
OUT_HASH_TABLE_TO_VAR_ARGS (out_hash, var_args_copy);
@@ -588,9 +477,10 @@ gupnp_service_proxy_send_action_valist (GUPnPServiceProxy *proxy,
g_propagate_error (error, local_error);
}
out:
+ gupnp_service_proxy_action_unref (handle);
va_end (var_args_copy);
g_list_free_full (in_names, g_free);
- g_list_free_full (in_values, value_free);
+ g_list_free_full (in_values, _value_free);
g_hash_table_unref (out_hash);
return result;
@@ -617,6 +507,8 @@ out:
*
* Return value: %TRUE if sending the action was succesful.
*
+ * Deprecated: 1.1.2: Use gupnp_service_proxy_action_new_from_list() and gupnp_service_proxy_call_action()
+ *
**/
gboolean
gupnp_service_proxy_send_action_list (GUPnPServiceProxy *proxy,
@@ -628,42 +520,40 @@ gupnp_service_proxy_send_action_list (GUPnPServiceProxy *proxy,
GList **out_values,
GError **error)
{
- GMainLoop *main_loop;
GUPnPServiceProxyAction *handle;
+ gboolean result = FALSE;
g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
g_return_val_if_fail (action, FALSE);
- main_loop = g_main_loop_new (g_main_context_get_thread_default (),
- TRUE);
+ handle = gupnp_service_proxy_action_new_from_list (action, in_names, in_values);
- handle = gupnp_service_proxy_begin_action_list (proxy,
- action,
- in_names,
- in_values,
- stop_main_loop,
- main_loop);
- if (!handle) {
- g_main_loop_unref (main_loop);
+ if (gupnp_service_proxy_call_action (proxy, handle, NULL, error) != NULL) {
+ result = FALSE;
- return FALSE;
+ goto out;
}
- /* Loop till we get a reply (or time out) */
- if (g_main_loop_is_running (main_loop))
- g_main_loop_run (main_loop);
+ result = gupnp_service_proxy_action_get_result_list (handle,
+ out_names,
+ out_types,
+ out_values,
+ error);
+out:
+ gupnp_service_proxy_action_unref (handle);
- g_main_loop_unref (main_loop);
+ return result;
+}
- if (!gupnp_service_proxy_end_action_list (proxy,
- handle,
- out_names,
- out_types,
- out_values,
- error))
- return FALSE;
+static void
+on_legacy_async_callback (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ GError *error = NULL;
+ GUPnPServiceProxyAction *action;
- return TRUE;
+ gupnp_service_proxy_call_action_finish (GUPNP_SERVICE_PROXY (source), res, &error);
+ action = (GUPnPServiceProxyAction *) user_data;
+ action->callback (action->proxy, action, action->user_data);
}
/**
@@ -684,6 +574,8 @@ gupnp_service_proxy_send_action_list (GUPnPServiceProxy *proxy,
* Returns: (transfer none): A #GUPnPServiceProxyAction handle. This will be freed when
* gupnp_service_proxy_cancel_action() or
* gupnp_service_proxy_end_action_valist().
+ *
+ * Deprecated: 1.1.1: Use gupnp_service_proxy_action_new() and gupnp_service_proxy_action_call_action_async()
**/
GUPnPServiceProxyAction *
gupnp_service_proxy_begin_action (GUPnPServiceProxy *proxy,
@@ -692,18 +584,27 @@ gupnp_service_proxy_begin_action (GUPnPServiceProxy *proxy,
gpointer user_data,
...)
{
- va_list var_args;
GUPnPServiceProxyAction *ret;
+ GList *in_names = NULL;
+ GList *in_values = NULL;
+ va_list var_args;
va_start (var_args, user_data);
- ret = gupnp_service_proxy_begin_action_valist (proxy,
- action,
- callback,
- user_data,
- var_args);
+ VAR_ARGS_TO_IN_LIST (var_args, in_names, in_values);
va_end (var_args);
- return ret;
+ ret = gupnp_service_proxy_action_new_from_list (action, in_names, in_values);
+ g_list_free_full (in_names, g_free);
+ g_list_free_full (in_values, _value_free);
+
+ ret->callback = callback;
+ ret->user_data = user_data;
+
+ return gupnp_service_proxy_call_action_async (proxy,
+ ret,
+ NULL,
+ on_legacy_async_callback,
+ ret);
}
/* Begins a basic action message */
@@ -788,168 +689,80 @@ prepare_action_msg (GUPnPServiceProxy *proxy,
g_string_insert (ret->msg_str, ret->header_pos, service_type);
ret->header_pos += strlen (service_type);
g_string_insert (ret->msg_str, ret->header_pos, "\">");
-}
-
-
-/* Begins a basic action message */
-static GUPnPServiceProxyAction *
-begin_action_msg (GUPnPServiceProxy *proxy,
- const char *action,
- GUPnPServiceProxyActionCallback callback,
- gpointer user_data)
-{
- GUPnPServiceProxyAction *ret;
- GUPnPServiceProxyPrivate *priv;
- char *control_url, *full_action;
- const char *service_type;
-
- priv = gupnp_service_proxy_get_instance_private (proxy);
-
- /* Create action structure */
- ret = gupnp_service_proxy_action_new_internal (action);
-
- ret->proxy = proxy;
- g_object_add_weak_pointer (G_OBJECT (proxy), (gpointer *)&(ret->proxy));
-
- ret->callback = callback;
- ret->user_data = user_data;
-
- ret->msg = NULL;
-
- ret->error = NULL;
-
- priv->pending_actions = g_list_prepend (priv->pending_actions, ret);
-
- /* Make sure we have a service type */
- service_type = gupnp_service_info_get_service_type
- (GUPNP_SERVICE_INFO (proxy));
- if (service_type == NULL) {
- ret->error = g_error_new (GUPNP_SERVER_ERROR,
- GUPNP_SERVER_ERROR_OTHER,
- "No service type defined");
-
- return ret;
- }
- /* Create message */
- control_url = gupnp_service_info_get_control_url
- (GUPNP_SERVICE_INFO (proxy));
-
- if (control_url != NULL) {
- GUPnPContext *context = NULL;
- char *local_control_url = NULL;
-
- context = gupnp_service_info_get_context
- (GUPNP_SERVICE_INFO (proxy));
+ soup_message_set_request (ret->msg,
+ "text/xml; charset=\"utf-8\"",
+ SOUP_MEMORY_TAKE,
+ ret->msg_str->str,
+ ret->msg_str->len);
- local_control_url = gupnp_context_rewrite_uri (context,
- control_url);
- g_free (control_url);
+ g_string_free (ret->msg_str, FALSE);
+}
- ret->msg = soup_message_new (SOUP_METHOD_POST, local_control_url);
- g_free (local_control_url);
- }
+static void
+update_message_after_not_allowed (SoupMessage *msg)
+{
+ const char *full_action;
- if (ret->msg == NULL) {
- ret->error = g_error_new (GUPNP_SERVER_ERROR,
- GUPNP_SERVER_ERROR_INVALID_URL,
- "No valid control URL defined");
+ /* Retry with M-POST */
+ msg->method = "M-POST";
- return ret;
- }
+ soup_message_headers_append
+ (msg->request_headers,
+ "Man",
+ "\"http://schemas.xmlsoap.org/soap/envelope/\"; ns=s");
- /* Specify action */
- full_action = g_strdup_printf ("\"%s#%s\"", service_type, action);
- soup_message_headers_append (ret->msg->request_headers,
- "SOAPAction",
+ /* Rename "SOAPAction" to "s-SOAPAction" */
+ full_action = soup_message_headers_get_one
+ (msg->request_headers,
+ "SOAPAction");
+ soup_message_headers_append (msg->request_headers,
+ "s-SOAPAction",
full_action);
- g_free (full_action);
-
- /* Specify language */
- http_request_set_accept_language (ret->msg);
-
- /* Accept gzip encoding */
- soup_message_headers_append (ret->msg->request_headers,
- "Accept-Encoding", "gzip");
-
- /* Set up envelope */
- ret->msg_str = xml_util_new_string ();
-
- g_string_append (ret->msg_str,
- "<?xml version=\"1.0\"?>"
- "<s:Envelope xmlns:s="
- "\"http://schemas.xmlsoap.org/soap/envelope/\" "
- "s:encodingStyle="
- "\"http://schemas.xmlsoap.org/soap/encoding/\">"
- "<s:Body>");
-
- g_string_append (ret->msg_str, "<u:");
- g_string_append (ret->msg_str, action);
- g_string_append (ret->msg_str, " xmlns:u=\"");
- g_string_append (ret->msg_str, service_type);
- g_string_append (ret->msg_str, "\">");
-
- return ret;
+ soup_message_headers_remove (msg->request_headers,
+ "SOAPAction");
}
-/* Received response to action message */
static void
-action_got_response (SoupSession *session,
- SoupMessage *msg,
- GUPnPServiceProxyAction *action)
+action_task_got_response (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
{
- const char *full_action;
+ GTask *task = G_TASK (user_data);
switch (msg->status_code) {
case SOUP_STATUS_CANCELLED:
- /* Silently return */
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ "Action message was cancelled");
+ g_object_unref (task);
break;
case SOUP_STATUS_METHOD_NOT_ALLOWED:
- /* Retry with M-POST */
- msg->method = "M-POST";
-
- soup_message_headers_append
- (msg->request_headers,
- "Man",
- "\"http://schemas.xmlsoap.org/soap/envelope/\"; ns=s");
-
- /* Rename "SOAPAction" to "s-SOAPAction" */
- full_action = soup_message_headers_get_one
- (msg->request_headers,
- "SOAPAction");
- soup_message_headers_append (msg->request_headers,
- "s-SOAPAction",
- full_action);
- soup_message_headers_remove (msg->request_headers,
- "SOAPAction");
-
/* And re-queue */
+ update_message_after_not_allowed (msg);
soup_session_requeue_message (session, msg);
break;
default:
- /* Success: Call callback */
- action->callback (action->proxy, action, action->user_data);
+ g_task_return_pointer (task,
+ g_task_get_task_data (task),
+ (GDestroyNotify) gupnp_service_proxy_action_unref);
+
+ g_object_unref (task);
break;
}
}
static void
-gupnp_service_proxy_action_queue (GUPnPServiceProxyAction *action)
+gupnp_service_proxy_action_queue_task (GTask *task)
{
GUPnPContext *context;
SoupSession *session;
-
- soup_message_set_request (action->msg,
- "text/xml; charset=\"utf-8\"",
- SOUP_MEMORY_TAKE,
- action->msg_str->str,
- action->msg_str->len);
-
- g_string_free (action->msg_str, FALSE);
+ GUPnPServiceProxyAction *action = g_task_get_task_data (task);
/* We need to keep our own reference to the message as well,
* in order for send_action() to work. */
@@ -962,11 +775,12 @@ gupnp_service_proxy_action_queue (GUPnPServiceProxyAction *action)
soup_session_queue_message (session,
action->msg,
- (SoupSessionCallback) action_got_response,
- action);
+ (SoupSessionCallback) action_task_got_response,
+ task);
}
/* Finishes an action message and sends it off */
+#if 0
static void
finish_action_msg (GUPnPServiceProxyAction *action,
const char *action_name)
@@ -1005,16 +819,7 @@ finish_action_msg (GUPnPServiceProxyAction *action,
(SoupSessionCallback) action_got_response,
action);
}
-
-static gboolean
-action_error_idle_cb (gpointer user_data)
-{
- GUPnPServiceProxyAction *action = (GUPnPServiceProxyAction *) user_data;
-
- action->callback (action->proxy, action, action->user_data);
-
- return FALSE;
-}
+#endif
/**
* gupnp_service_proxy_begin_action_valist:
@@ -1031,6 +836,8 @@ action_error_idle_cb (gpointer user_data)
* Returns: (transfer none): A #GUPnPServiceProxyAction handle. This will
* be freed when calling gupnp_service_proxy_cancel_action() or
* gupnp_service_proxy_end_action_valist().
+ *
+ * Deprecated: 1.1.1: Use ove of
**/
GUPnPServiceProxyAction *
gupnp_service_proxy_begin_action_valist
@@ -1041,23 +848,20 @@ gupnp_service_proxy_begin_action_valist
va_list var_args)
{
GUPnPServiceProxyAction *ret;
- GList *in_names = NULL, *in_values = NULL;
-
- g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), NULL);
- g_return_val_if_fail (action, NULL);
- g_return_val_if_fail (callback, NULL);
+ GList *in_names = NULL;
+ GList *in_values = NULL;
VAR_ARGS_TO_IN_LIST (var_args, in_names, in_values);
- ret = gupnp_service_proxy_begin_action_list (proxy,
- action,
- in_names,
- in_values,
- callback,
- user_data);
+
+ ret = gupnp_service_proxy_action_new_from_list (action, in_names, in_values);
g_list_free_full (in_names, g_free);
- g_list_free_full (in_values, value_free);
+ g_list_free_full (in_values, _value_free);
+
+ ret->callback = callback;
+ ret->user_data = user_data;
+
+ return gupnp_service_proxy_call_action_async (proxy, ret, NULL, on_legacy_async_callback, ret);
- return ret;
}
/**
@@ -1092,27 +896,12 @@ gupnp_service_proxy_begin_action_list
{
GUPnPServiceProxyAction *ret;
- g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), NULL);
- g_return_val_if_fail (action, NULL);
- g_return_val_if_fail (callback, NULL);
- g_return_val_if_fail (g_list_length (in_names) ==
- g_list_length (in_values),
- NULL);
-
- ret = gupnp_service_proxy_action_new_from_list (action,
- in_names,
- in_values);
- prepare_action_msg (proxy, ret, callback, user_data);
-
- if (ret->error) {
- g_idle_add (action_error_idle_cb, ret);
-
- return ret;
- }
+ ret = gupnp_service_proxy_action_new_from_list (action, in_names, in_values);
- gupnp_service_proxy_action_queue (ret);
+ ret->callback = callback;
+ ret->user_data = user_data;
- return ret;
+ return gupnp_service_proxy_call_action_async (proxy, ret, NULL, on_legacy_async_callback, ret);
}
/**
@@ -1139,20 +928,31 @@ gupnp_service_proxy_end_action (GUPnPServiceProxy *proxy,
va_list var_args;
gboolean ret;
+ g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
+ g_return_val_if_fail (action, FALSE);
+ g_return_val_if_fail (proxy == action->proxy, FALSE);
+
+ if (action->error) {
+ g_propagate_error (error, action->error);
+ gupnp_service_proxy_action_unref (action);
+
+ return FALSE;
+ }
+
+
va_start (var_args, error);
- ret = gupnp_service_proxy_end_action_valist (proxy,
- action,
- error,
- var_args);
+ ret = gupnp_service_proxy_action_get_result_valist (action, error, var_args);
va_end (var_args);
+ gupnp_service_proxy_action_unref (action);
+
return ret;
}
/* Checks an action response for errors and returns the parsed
* xmlDoc object. */
-static xmlDoc *
-check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
+xmlDoc *
+_check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
GUPnPServiceProxyAction *action,
xmlNode **params,
GError **error)
@@ -1280,8 +1080,8 @@ check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
/* Reads a value into the parameter name and initialised GValue pair
* from @response */
-static void
-read_out_parameter (const char *arg_name,
+void
+_read_out_parameter (const char *arg_name,
GValue *value,
xmlNode *params)
{
@@ -1320,34 +1120,23 @@ gupnp_service_proxy_end_action_valist (GUPnPServiceProxy *proxy,
GError **error,
va_list var_args)
{
- GHashTable *out_hash;
- va_list var_args_copy;
gboolean result;
- GError *local_error;
g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
g_return_val_if_fail (action, FALSE);
g_return_val_if_fail (proxy == action->proxy, FALSE);
- out_hash = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- value_free);
- G_VA_COPY (var_args_copy, var_args);
- VAR_ARGS_TO_OUT_HASH_TABLE (var_args, out_hash);
- local_error = NULL;
- result = gupnp_service_proxy_end_action_hash (proxy,
- action,
- out_hash,
- &local_error);
+ if (action->error) {
+ g_propagate_error (error, action->error);
+ gupnp_service_proxy_action_unref (action);
- if (local_error == NULL) {
- OUT_HASH_TABLE_TO_VAR_ARGS (out_hash, var_args_copy);
- } else {
- g_propagate_error (error, local_error);
+ return FALSE;
}
- va_end (var_args_copy);
- g_hash_table_unref (out_hash);
+
+ result = gupnp_service_proxy_action_get_result_valist (action,
+ error,
+ var_args);
+ gupnp_service_proxy_action_unref (action);
return result;
}
@@ -1380,13 +1169,7 @@ gupnp_service_proxy_end_action_list (GUPnPServiceProxy *proxy,
GList **out_values,
GError **error)
{
- xmlDoc *response;
- xmlNode *params;
- GList *names;
- GList *types;
- GList *out_values_list;
-
- out_values_list = NULL;
+ gboolean result;
g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
g_return_val_if_fail (action, FALSE);
@@ -1400,37 +1183,14 @@ gupnp_service_proxy_end_action_list (GUPnPServiceProxy *proxy,
return FALSE;
}
- /* Check response for errors and do initial parsing */
- response = check_action_response (proxy, action, ¶ms, error);
- if (response == NULL) {
- gupnp_service_proxy_action_unref (action);
-
- return FALSE;
- }
-
- /* Read arguments */
- types = out_types;
- for (names = out_names; names; names=names->next) {
- GValue *val;
-
- val = g_slice_new0 (GValue);
- g_value_init (val, (GType) types->data);
-
- read_out_parameter (names->data, val, params);
-
- out_values_list = g_list_append (out_values_list, val);
-
- types = types->next;
- }
-
- *out_values = out_values_list;
-
- /* Cleanup */
+ result = gupnp_service_proxy_action_get_result_list (action,
+ out_names,
+ out_types,
+ out_values,
+ error);
gupnp_service_proxy_action_unref (action);
- xmlFreeDoc (response);
-
- return TRUE;
+ return result;
}
/**
@@ -1454,8 +1214,7 @@ gupnp_service_proxy_end_action_hash
GHashTable *hash,
GError **error)
{
- xmlDoc *response;
- xmlNode *params;
+ gboolean result;
g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
g_return_val_if_fail (action, FALSE);
@@ -1469,23 +1228,13 @@ gupnp_service_proxy_end_action_hash
return FALSE;
}
- /* Check response for errors and do initial parsing */
- response = check_action_response (proxy, action, ¶ms, error);
- if (response == NULL) {
- gupnp_service_proxy_action_unref (action);
-
- return FALSE;
- }
-
- /* Read arguments */
- g_hash_table_foreach (hash, (GHFunc) read_out_parameter, params);
- /* Cleanup */
+ result = gupnp_service_proxy_action_get_result_hash (action,
+ hash,
+ error);
gupnp_service_proxy_action_unref (action);
- xmlFreeDoc (response);
-
- return TRUE;
+ return result;
}
/**
@@ -2463,3 +2212,93 @@ gupnp_service_proxy_get_subscribed (GUPnPServiceProxy *proxy)
return priv->subscribed;
}
+
+/**
+ * gupnp_service_proxy_call_action_async:
+ */
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action_async (GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), NULL);
+
+ task = g_task_new (proxy, cancellable, callback, user_data);
+ g_task_set_task_data (task,
+ gupnp_service_proxy_action_ref (action),
+ (GDestroyNotify) gupnp_service_proxy_action_unref);
+
+ prepare_action_msg (proxy, action, NULL, NULL);
+
+ if (action->error != NULL) {
+ g_task_return_error (task, g_error_copy (action->error));
+ g_object_unref (task);
+
+ return NULL;
+ }
+
+ gupnp_service_proxy_action_queue_task (task);
+
+ return action;
+}
+
+/**
+ * gupnp_service_proxy_call_action_finish:
+ */
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action_finish (GUPnPServiceProxy *proxy,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (G_TASK (result), proxy), NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action (GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GUPnPContext *context = NULL;
+ SoupSession *session = NULL;
+
+ g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), NULL);
+
+ prepare_action_msg (proxy, action, NULL, NULL);
+
+ if (action->error != NULL) {
+ g_propagate_error (error, g_error_copy (action->error));
+
+ return NULL;
+ }
+
+ context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (proxy));
+ session = gupnp_context_get_session (context);
+ soup_session_send_message (session, action->msg);
+
+ /* If not allowed, try again */
+ if (action->msg->status_code == SOUP_STATUS_METHOD_NOT_ALLOWED) {
+ update_message_after_not_allowed (action->msg);
+ soup_session_send_message (session, action->msg);
+ }
+
+ /* prepare_action_msg has queued the message, so remove it */
+ gupnp_service_proxy_remove_action (proxy, action);
+
+ if (action->msg->status_code == SOUP_STATUS_CANCELLED) {
+ action->error = g_error_new (G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ "Action message was cancelled");
+ g_propagate_error (error, g_error_copy (action->error));
+
+ return NULL;
+ }
+
+ return action;
+}
diff --git a/libgupnp/gupnp-service-proxy.h b/libgupnp/gupnp-service-proxy.h
index fe9f46b..74e574d 100644
--- a/libgupnp/gupnp-service-proxy.h
+++ b/libgupnp/gupnp-service-proxy.h
@@ -210,13 +210,56 @@ gupnp_service_proxy_get_subscribed (GUPnPServiceProxy *proxy);
GUPnPServiceProxyAction *
gupnp_service_proxy_action_new (const char *action,
- ...);
+ ...) G_GNUC_NULL_TERMINATED;
GUPnPServiceProxyAction *
gupnp_service_proxy_action_new_from_list (const char *action,
GList *in_names,
GList *in_values);
+gboolean
+gupnp_service_proxy_action_get_result (GUPnPServiceProxyAction *action,
+ GError **error,
+ ...) G_GNUC_NULL_TERMINATED;
+
+gboolean
+gupnp_service_proxy_action_get_result_list (GUPnPServiceProxyAction *action,
+ GList *out_names,
+ GList *out_types,
+ GList **out_values,
+ GError **error);
+gboolean
+gupnp_service_proxy_action_get_result_hash (GUPnPServiceProxyAction *action,
+ GHashTable *out_hash,
+ GError **error);
+
+
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action_async (GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action_finish (GUPnPServiceProxy *proxy,
+ GAsyncResult *result,
+ GError **error);
+
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action (GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ GCancellable *cancellable,
+ GError **error);
+
+GUPnPServiceProxyAction *
+gupnp_service_proxy_action_ref (GUPnPServiceProxyAction *action);
+
+void
+gupnp_service_proxy_action_unref (GUPnPServiceProxyAction *action);
+
+
G_END_DECLS
#endif /* GUPNP_SERVICE_PROXY_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]