summaryrefslogtreecommitdiff
path: root/libdleyna
diff options
context:
space:
mode:
authorLudovic Ferrandis <ludovic.ferrandis@intel.com>2013-08-27 18:24:55 +0200
committerMark Ryan <mark.d.ryan@intel.com>2013-09-04 14:02:14 +0200
commit8c460ff2e476eeb2b7e35471e97308652a0197f2 (patch)
tree4ee52cf87cef4a8acde8fe2e767e482afae5c9c9 /libdleyna
parent96f4729081606d859a5dff1aaab75511c02829b8 (diff)
downloaddleyna-server-8c460ff2e476eeb2b7e35471e97308652a0197f2.tar.gz
[API] Add new BrowseObjects API
Add new BrowseObjects API in MediaDevice interface Fix issue #114: <https://github.com/01org/dleyna-server/issues/114> Signed-off-by: Ludovic Ferrandis <ludovic.ferrandis@intel.com>
Diffstat (limited to 'libdleyna')
-rw-r--r--libdleyna/server/async.c6
-rw-r--r--libdleyna/server/async.h11
-rw-r--r--libdleyna/server/device.c282
-rw-r--r--libdleyna/server/device.h2
-rw-r--r--libdleyna/server/interface.h3
-rw-r--r--libdleyna/server/server.c15
-rw-r--r--libdleyna/server/task.c26
-rw-r--r--libdleyna/server/task.h12
-rw-r--r--libdleyna/server/upnp.c26
-rw-r--r--libdleyna/server/upnp.h4
10 files changed, 387 insertions, 0 deletions
diff --git a/libdleyna/server/async.c b/libdleyna/server/async.c
index 5571f70..c073841 100644
--- a/libdleyna/server/async.c
+++ b/libdleyna/server/async.c
@@ -40,6 +40,12 @@ void dls_async_task_delete(dls_async_task_t *cb_data)
if (cb_data->ut.get_all.vb)
g_variant_builder_unref(cb_data->ut.get_all.vb);
break;
+ case DLS_TASK_BROWSE_OBJECTS:
+ if (cb_data->ut.browse_objects.avb)
+ g_variant_builder_unref(cb_data->ut.browse_objects.avb);
+ g_free(cb_data->ut.browse_objects.objects_id);
+ g_free(cb_data->ut.browse_objects.upnp_filter);
+ break;
case DLS_TASK_UPLOAD_TO_ANY:
case DLS_TASK_UPLOAD:
g_free(cb_data->ut.upload.mime_type);
diff --git a/libdleyna/server/async.h b/libdleyna/server/async.h
index e14db6e..468e461 100644
--- a/libdleyna/server/async.h
+++ b/libdleyna/server/async.h
@@ -77,6 +77,16 @@ struct dls_async_update_t_ {
GHashTable *map;
};
+typedef struct dls_async_browse_objects_t_ dls_async_browse_objects_t;
+struct dls_async_browse_objects_t_ {
+ dls_async_get_all_t get_all; /* pseudo inheritance - MUST be first */
+ GVariantBuilder *avb;
+ gchar *upnp_filter;
+ const dleyna_task_queue_key_t *queue_id;
+ const gchar **objects_id;
+ guint index;
+};
+
struct dls_async_task_t_ {
dls_task_t task; /* pseudo inheritance - MUST be first field */
dls_upnp_task_complete_t cb;
@@ -91,6 +101,7 @@ struct dls_async_task_t_ {
dls_async_get_all_t get_all;
dls_async_upload_t upload;
dls_async_update_t update;
+ dls_async_browse_objects_t browse_objects;
} ut;
};
diff --git a/libdleyna/server/device.c b/libdleyna/server/device.c
index 678c88e..4864f43 100644
--- a/libdleyna/server/device.c
+++ b/libdleyna/server/device.c
@@ -2821,6 +2821,288 @@ static void prv_get_resource(GUPnPDIDLLiteParser *parser,
task_data->protocol_info);
}
+static void prv_browse_objects_add_error_result(dls_async_browse_objects_t *bo,
+ const gchar *path,
+ GError *error)
+{
+ GVariantBuilder evb;
+ GVariant* gv_result;
+
+ DLEYNA_LOG_WARNING("%s: %s", path, error->message);
+
+ g_variant_builder_init(&evb, G_VARIANT_TYPE("a{sv}"));
+
+ if (bo->get_all.filter_mask & DLS_UPNP_MASK_PROP_PATH)
+ g_variant_builder_add(
+ &evb, "{sv}", DLS_INTERFACE_PROP_PATH,
+ g_variant_new_object_path(path));
+
+ gv_result = dls_props_get_error_prop(error);
+
+ g_variant_builder_add(&evb, "{sv}",
+ DLS_INTERFACE_PROP_ERROR, gv_result);
+
+ gv_result = g_variant_builder_end(&evb);
+
+ g_variant_builder_add(bo->avb, "@a{sv}", gv_result);
+}
+
+static void prv_browse_objects_end_action_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ dls_async_task_t *cb_data = user_data;
+ dls_async_browse_objects_t *cb_task_data = &cb_data->ut.browse_objects;
+ GUPnPDIDLLiteParser *parser = NULL;
+ gchar *result = NULL;
+ const gchar *message;
+ gboolean end;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ end = gupnp_service_proxy_end_action(proxy, action, &error,
+ "Result", G_TYPE_STRING, &result,
+ NULL);
+
+ if (!end || (result == NULL)) {
+ message = (error != NULL) ? error->message : "Invalid result";
+ DLEYNA_LOG_WARNING("Browse Object operation failed: %s",
+ message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Browse operation failed: %s",
+ message);
+ goto on_exit;
+ }
+
+ DLEYNA_LOG_DEBUG_NL();
+ DLEYNA_LOG_DEBUG("Result: %s", result);
+ DLEYNA_LOG_DEBUG_NL();
+
+ cb_task_data->get_all.vb = g_variant_builder_new(
+ G_VARIANT_TYPE("a{sv}"));
+
+ parser = gupnp_didl_lite_parser_new();
+
+ g_signal_connect(parser, "object-available",
+ G_CALLBACK(prv_get_all),
+ cb_data);
+
+ if (!gupnp_didl_lite_parser_parse_didl(parser, result, &error)) {
+ if (error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
+ DLEYNA_LOG_WARNING("Property not defined for object");
+
+ cb_data->error = g_error_new(
+ DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_PROPERTY,
+ "Property not defined for object");
+ } else {
+ DLEYNA_LOG_WARNING(
+ "Unable to parse results of browse: %s",
+ error->message);
+
+ cb_data->error = g_error_new(
+ DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Unable to parse results of browse: %s",
+ error->message);
+ }
+
+ goto on_exit;
+ }
+
+ g_variant_builder_add(cb_task_data->avb, "@a{sv}",
+ g_variant_builder_end(cb_task_data->get_all.vb));
+
+on_exit:
+
+ if (cb_data->error != NULL) {
+ message = cb_task_data->objects_id[cb_task_data->index - 1];
+ prv_browse_objects_add_error_result(cb_task_data, message,
+ cb_data->error);
+ g_error_free(cb_data->error);
+ cb_data->error = NULL;
+ }
+
+ if (cb_task_data->get_all.vb) {
+ g_variant_builder_unref(cb_data->ut.get_all.vb);
+ cb_data->ut.get_all.vb = NULL;
+ }
+
+ if (parser)
+ g_object_unref(parser);
+
+ if (error)
+ g_error_free(error);
+
+ g_free(result);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static GUPnPServiceProxyAction *prv_browse_objects_begin_action_cb(
+ dleyna_service_task_t *task,
+ GUPnPServiceProxy *proxy,
+ gboolean *failed)
+{
+ GUPnPServiceProxyAction *action;
+ dls_task_t *user_data;
+ dls_async_browse_objects_t *cb_task_data;
+ const gchar *path;
+ gchar *root_path = NULL;
+ gchar *id = NULL;
+ GError *error = NULL;
+
+ DLEYNA_LOG_DEBUG("Enter called");
+
+ user_data = (dls_task_t *)dleyna_service_task_get_user_data(task);
+ cb_task_data = &((dls_async_task_t *)user_data)->ut.browse_objects;
+ path = cb_task_data->objects_id[cb_task_data->index];
+
+ /* Process anyway. We will add an entry with error */
+ (void) dls_path_get_path_and_id(path, &root_path, &id, &error);
+
+ if (error != NULL) {
+ DLEYNA_LOG_WARNING("%s: %s", path, error->message);
+
+ prv_browse_objects_add_error_result(cb_task_data, path, error);
+ action = NULL;
+ g_error_free(error);
+
+ goto exit;
+ }
+
+ DLEYNA_LOG_DEBUG("Browse Metadata for path [id]: %s [%s]", path, id);
+
+ action = gupnp_service_proxy_begin_action(
+ proxy, "Browse",
+ dleyna_service_task_begin_action_cb, task,
+ "ObjectID", G_TYPE_STRING, id,
+ "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
+ "Filter", G_TYPE_STRING, cb_task_data->upnp_filter,
+ "StartingIndex", G_TYPE_INT, 0,
+ "RequestedCount", G_TYPE_INT, 0,
+ "SortCriteria", G_TYPE_STRING, "", NULL);
+
+ g_free(root_path);
+ g_free(id);
+
+exit:
+ *failed = FALSE;
+ cb_task_data->index++;
+
+ return action;
+}
+
+static void prv_browse_objects_chain_cancelled(GCancellable *cancellable,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ dleyna_task_processor_cancel_queue(cb_data->ut.browse_objects.queue_id);
+ dls_async_task_cancelled_cb(cancellable, user_data);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_browse_objects_chain_end(gboolean cancelled, gpointer data)
+{
+ dls_task_t *task = (dls_task_t *)data;
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_browse_objects_t *cb_task_data = &cb_data->ut.browse_objects;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!cancelled)
+ cb_data->task.result = g_variant_ref_sink(
+ g_variant_builder_end(
+ cb_task_data->avb));
+ else if (cb_data->error == NULL)
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_CANCELLED,
+ "Operation cancelled.");
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_device_browse_objects(dls_client_t *client, dls_task_t *task)
+{
+ const dleyna_task_queue_key_t *queue_id;
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_browse_objects_t *cb_task_data;
+ dls_device_context_t *context;
+ const gchar **objs;
+ gsize length;
+ guint i;
+ gboolean path_ok;
+
+ DLEYNA_LOG_DEBUG("Root Path %s", task->target.root_path)
+
+ objs = g_variant_get_objv(task->ut.browse_objects.objects, &length);
+
+ path_ok = (length > 0);
+
+ for (i = 0; (i < length) && path_ok; i++)
+ path_ok = g_str_has_prefix(objs[i], task->target.root_path);
+
+ if (!path_ok) {
+ g_free(objs);
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_BAD_PATH,
+ "root path is invalid.");
+ goto on_error;
+ }
+
+ queue_id = dleyna_task_processor_add_queue(
+ dls_server_get_task_processor(),
+ dleyna_service_task_create_source(),
+ DLS_SERVER_SINK,
+ DLEYNA_TASK_QUEUE_FLAG_AUTO_REMOVE,
+ dleyna_service_task_process_cb,
+ dleyna_service_task_cancel_cb,
+ dleyna_service_task_delete_cb);
+
+ dleyna_task_queue_set_finally(queue_id, prv_browse_objects_chain_end);
+ dleyna_task_queue_set_user_data(queue_id, task);
+
+ context = dls_device_get_context(task->target.device, client);
+ cb_data->proxy = context->service_proxy;
+
+ cb_task_data = &cb_data->ut.browse_objects;
+ cb_task_data->queue_id = queue_id;
+ cb_task_data->avb = g_variant_builder_new(G_VARIANT_TYPE("aa{sv}"));
+ cb_task_data->objects_id = objs;
+
+ g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(prv_browse_objects_chain_cancelled),
+ cb_data, NULL);
+
+ for (i = 0; i < length; i++)
+ dleyna_service_task_add(queue_id,
+ prv_browse_objects_begin_action_cb,
+ cb_data->proxy,
+ prv_browse_objects_end_action_cb,
+ NULL, cb_data);
+
+ dleyna_task_queue_start(queue_id);
+
+on_error:
+
+ if (cb_data->error != NULL)
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+}
+
void dls_device_get_resource(dls_client_t *client,
dls_task_t *task,
const gchar *upnp_filter)
diff --git a/libdleyna/server/device.h b/libdleyna/server/device.h
index 30788db..530f976 100644
--- a/libdleyna/server/device.h
+++ b/libdleyna/server/device.h
@@ -113,6 +113,8 @@ void dls_device_search(dls_client_t *client,
const gchar *upnp_filter, const gchar *upnp_query,
const gchar *sort_by);
+void dls_device_browse_objects(dls_client_t *client, dls_task_t *task);
+
void dls_device_get_resource(dls_client_t *client,
dls_task_t *task,
const gchar *upnp_filter);
diff --git a/libdleyna/server/interface.h b/libdleyna/server/interface.h
index 56b7aa0..57f6dea 100644
--- a/libdleyna/server/interface.h
+++ b/libdleyna/server/interface.h
@@ -212,6 +212,9 @@ enum dls_interface_type_ {
#define DLS_INTERFACE_GET_METADATA "GetMetaData"
#define DLS_INTERFACE_METADATA "MetaData"
+#define DLS_INTERFACE_BROWSE_OBJECTS "BrowseObjects"
+#define DLS_INTERFACE_OBJECTS_PATH "Objects"
+
#define DLS_INTERFACE_CREATE_REFERENCE "CreateReference"
#define DLS_INTERFACE_REFPATH "RefPath"
diff --git a/libdleyna/server/server.c b/libdleyna/server/server.c
index fff5ca9..10e01c0 100644
--- a/libdleyna/server/server.c
+++ b/libdleyna/server/server.c
@@ -453,6 +453,14 @@ static const gchar g_server_introspection[] =
" <arg type='s' name='"DLS_INTERFACE_MIME_TYPE"'"
" direction='out'/>"
" </method>"
+ " <method name='"DLS_INTERFACE_BROWSE_OBJECTS"'>"
+ " <arg type='ao' name='"DLS_INTERFACE_OBJECTS_PATH"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
+ " direction='in'/>"
+ " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
+ " direction='out'/>"
+ " </method>"
" <property type='s' name='"DLS_INTERFACE_PROP_LOCATION"'"
" access='read'/>"
" <property type='s' name='"DLS_INTERFACE_PROP_UDN"'"
@@ -666,6 +674,10 @@ static void prv_process_async_task(dls_task_t *task)
dls_upnp_search(g_context.upnp, client, task,
prv_async_task_complete);
break;
+ case DLS_TASK_BROWSE_OBJECTS:
+ dls_upnp_browse_objects(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
case DLS_TASK_GET_RESOURCE:
dls_upnp_get_resource(g_context.upnp, client, task,
prv_async_task_complete);
@@ -1189,6 +1201,9 @@ static void prv_device_method_call(dleyna_connector_id_t conn,
} else if (!strcmp(method, DLS_INTERFACE_GET_ICON)) {
task = dls_task_get_icon_new(invocation, object, parameters,
&error);
+ } else if (!strcmp(method, DLS_INTERFACE_BROWSE_OBJECTS)) {
+ task = dls_task_browse_objects_new(invocation, object,
+ parameters, &error);
} else if (!strcmp(method, DLS_INTERFACE_CANCEL)) {
task = NULL;
diff --git a/libdleyna/server/task.c b/libdleyna/server/task.c
index 9943319..a5b03da 100644
--- a/libdleyna/server/task.c
+++ b/libdleyna/server/task.c
@@ -52,6 +52,12 @@ static void prv_delete(dls_task_t *task)
g_variant_unref(task->ut.search.filter);
g_free(task->ut.search.sort_by);
break;
+ case DLS_TASK_BROWSE_OBJECTS:
+ if (task->ut.browse_objects.objects)
+ g_variant_unref(task->ut.browse_objects.objects);
+ if (task->ut.browse_objects.filter)
+ g_variant_unref(task->ut.browse_objects.filter);
+ break;
case DLS_TASK_GET_RESOURCE:
if (task->ut.resource.filter)
g_variant_unref(task->ut.resource.filter);
@@ -412,6 +418,26 @@ finished:
return task;
}
+dls_task_t *dls_task_browse_objects_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_BROWSE_OBJECTS, invocation, path,
+ "(@aa{sv})", error, FALSE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(@ao@as)",
+ &task->ut.browse_objects.objects,
+ &task->ut.browse_objects.filter);
+
+finished:
+
+ return task;
+}
+
dls_task_t *dls_task_get_resource_new(dleyna_connector_msg_id_t invocation,
const gchar *path, GVariant *parameters,
GError **error)
diff --git a/libdleyna/server/task.h b/libdleyna/server/task.h
index fb00e10..c8cf613 100644
--- a/libdleyna/server/task.h
+++ b/libdleyna/server/task.h
@@ -39,6 +39,7 @@ enum dls_task_type_t_ {
DLS_TASK_GET_ALL_PROPS,
DLS_TASK_GET_PROP,
DLS_TASK_SEARCH,
+ DLS_TASK_BROWSE_OBJECTS,
DLS_TASK_GET_RESOURCE,
DLS_TASK_SET_PREFER_LOCAL_ADDRESSES,
DLS_TASK_SET_PROTOCOL_INFO,
@@ -95,6 +96,12 @@ struct dls_task_search_t_ {
GVariant *filter;
};
+typedef struct dls_task_browse_objects_t_ dls_task_browse_objects_t;
+struct dls_task_browse_objects_t_ {
+ GVariant *objects;
+ GVariant *filter;
+};
+
typedef struct dls_task_get_resource_t_ dls_task_get_resource_t;
struct dls_task_get_resource_t_ {
gchar *protocol_info;
@@ -186,6 +193,7 @@ struct dls_task_t_ {
dls_task_create_reference_t create_reference;
dls_task_get_icon_t get_icon;
dls_task_white_list_t white_list;
+ dls_task_browse_objects_t browse_objects;
} ut;
};
@@ -222,6 +230,10 @@ dls_task_t *dls_task_search_ex_new(dleyna_connector_msg_id_t invocation,
const gchar *path, GVariant *parameters,
GError **error);
+dls_task_t *dls_task_browse_objects_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error);
+
dls_task_t *dls_task_get_resource_new(dleyna_connector_msg_id_t invocation,
const gchar *path, GVariant *parameters,
GError **error);
diff --git a/libdleyna/server/upnp.c b/libdleyna/server/upnp.c
index 2cbef81..68338e6 100644
--- a/libdleyna/server/upnp.c
+++ b/libdleyna/server/upnp.c
@@ -621,6 +621,32 @@ on_error:
DLEYNA_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS");
}
+void dls_upnp_browse_objects(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_browse_objects_t *cb_task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ cb_data->cb = cb;
+ cb_task_data = &cb_data->ut.browse_objects;
+ cb_task_data->get_all.protocol_info = client->protocol_info;
+
+ cb_task_data->get_all.filter_mask =
+ dls_props_parse_filter(upnp->filter_map,
+ task->ut.browse_objects.filter,
+ &cb_task_data->upnp_filter);
+
+ DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
+ cb_task_data->get_all.filter_mask);
+
+ dls_device_browse_objects(client, task);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
void dls_upnp_get_resource(dls_upnp_t *upnp, dls_client_t *client,
dls_task_t *task,
dls_upnp_task_complete_t cb)
diff --git a/libdleyna/server/upnp.h b/libdleyna/server/upnp.h
index b5df0e3..08441c6 100644
--- a/libdleyna/server/upnp.h
+++ b/libdleyna/server/upnp.h
@@ -59,6 +59,10 @@ void dls_upnp_search(dls_upnp_t *upnp, dls_client_t *client,
dls_task_t *task,
dls_upnp_task_complete_t cb);
+void dls_upnp_browse_objects(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
void dls_upnp_get_resource(dls_upnp_t *upnp, dls_client_t *client,
dls_task_t *task,
dls_upnp_task_complete_t cb);