diff options
author | Ludovic Ferrandis <ludovic.ferrandis@intel.com> | 2013-08-27 18:24:55 +0200 |
---|---|---|
committer | Mark Ryan <mark.d.ryan@intel.com> | 2013-09-04 14:02:14 +0200 |
commit | 8c460ff2e476eeb2b7e35471e97308652a0197f2 (patch) | |
tree | 4ee52cf87cef4a8acde8fe2e767e482afae5c9c9 /libdleyna | |
parent | 96f4729081606d859a5dff1aaab75511c02829b8 (diff) | |
download | dleyna-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.c | 6 | ||||
-rw-r--r-- | libdleyna/server/async.h | 11 | ||||
-rw-r--r-- | libdleyna/server/device.c | 282 | ||||
-rw-r--r-- | libdleyna/server/device.h | 2 | ||||
-rw-r--r-- | libdleyna/server/interface.h | 3 | ||||
-rw-r--r-- | libdleyna/server/server.c | 15 | ||||
-rw-r--r-- | libdleyna/server/task.c | 26 | ||||
-rw-r--r-- | libdleyna/server/task.h | 12 | ||||
-rw-r--r-- | libdleyna/server/upnp.c | 26 | ||||
-rw-r--r-- | libdleyna/server/upnp.h | 4 |
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); |