/* * dLeyna * * Copyright (C) 2012-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Mark Ryan * */ #include #include #include #include #include #include #include #include "async.h" #include "device.h" #include "interface.h" #include "path.h" #include "search.h" #include "sort.h" #include "upnp.h" #define DLS_DMS_DEVICE_TYPE "urn:schemas-upnp-org:device:MediaServer:" struct dls_upnp_t_ { dleyna_connector_id_t connection; const dleyna_connector_dispatch_cb_t *interface_info; GHashTable *filter_map; GHashTable *property_map; dls_upnp_callback_t found_server; dls_upnp_callback_t lost_server; GUPnPContextManager *context_manager; void *user_data; GHashTable *device_udn_map; GHashTable *sleeping_device_udn_map; GHashTable *device_uc_map; }; /* Private structure used in service task */ typedef struct prv_device_new_ct_t_ prv_device_new_ct_t; struct prv_device_new_ct_t_ { dls_upnp_t *upnp; char *udn; gchar *ip_address; dls_device_t *device; const dleyna_task_queue_key_t *queue_id; }; static void prv_device_new_free(prv_device_new_ct_t *priv_t) { if (priv_t) { g_free(priv_t->udn); g_free(priv_t->ip_address); g_free(priv_t); } } static void prv_device_chain_end(gboolean cancelled, gpointer data) { dls_device_t *device; prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)data; DLEYNA_LOG_DEBUG("Enter"); device = priv_t->device; if (cancelled) goto on_clear; DLEYNA_LOG_DEBUG("Notify new server available: %s", device->path); g_hash_table_insert(priv_t->upnp->device_udn_map, g_strdup(priv_t->udn), device); priv_t->upnp->found_server(device->path, priv_t->upnp->user_data); on_clear: g_hash_table_remove(priv_t->upnp->device_uc_map, priv_t->udn); if (cancelled) dls_device_delete(device); prv_device_new_free(priv_t); DLEYNA_LOG_DEBUG_NL(); } static void prv_device_context_switch_end(gboolean cancelled, gpointer data) { prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)data; DLEYNA_LOG_DEBUG("Enter"); g_hash_table_remove(priv_t->upnp->device_uc_map, priv_t->udn); prv_device_new_free(priv_t); DLEYNA_LOG_DEBUG("Exit"); } static const dleyna_task_queue_key_t *prv_create_device_queue( prv_device_new_ct_t **priv_t) { const dleyna_task_queue_key_t *queue_id; *priv_t = g_new0(prv_device_new_ct_t, 1); 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_device_chain_end); dleyna_task_queue_set_user_data(queue_id, *priv_t); return queue_id; } static void prv_update_device_context(prv_device_new_ct_t *priv_t, dls_upnp_t *upnp, const char *udn, dls_device_t *device, const gchar *ip_address, const dleyna_task_queue_key_t *queue_id) { priv_t->upnp = upnp; priv_t->udn = g_strdup(udn); priv_t->ip_address = g_strdup(ip_address); priv_t->queue_id = queue_id; priv_t->device = device; g_hash_table_insert(upnp->device_uc_map, g_strdup(udn), priv_t); } static GUPnPDeviceInfo *prv_lookup_dms_child_device(GUPnPDeviceInfo *proxy) { GList *child_devices; GList *next; const gchar *device_type; GUPnPDeviceInfo *info = NULL; GUPnPDeviceInfo *child_info = NULL; child_devices = gupnp_device_info_list_device_types(proxy); next = child_devices; while (next != NULL) { device_type = (gchar *)next->data; child_info = gupnp_device_info_get_device(proxy, device_type); if (g_str_has_prefix(device_type, DLS_DMS_DEVICE_TYPE)) { break; } else { info = prv_lookup_dms_child_device(child_info); g_object_unref(child_info); child_info = NULL; if (info != NULL) { child_info = info; break; } } next = g_list_next(next); } g_list_free_full(child_devices, (GDestroyNotify)g_free); return child_info; } static void prv_device_available_cb(GUPnPControlPoint *cp, GUPnPDeviceProxy *proxy, gpointer user_data) { dls_upnp_t *upnp = user_data; const char *udn; dls_device_t *device; const gchar *ip_address; dls_device_context_t *context; const dleyna_task_queue_key_t *queue_id; unsigned int i; prv_device_new_ct_t *priv_t; GUPnPDeviceInfo *device_proxy = (GUPnPDeviceInfo *)proxy; GUPnPDeviceInfo *device_info = NULL; const gchar *device_type; gboolean subscribe = FALSE; gpointer key; gpointer val; udn = gupnp_device_info_get_udn(device_proxy); ip_address = gssdp_client_get_host_ip( GSSDP_CLIENT(gupnp_control_point_get_context(cp))); if (!udn || !ip_address) goto on_error; DLEYNA_LOG_DEBUG("UDN %s", udn); DLEYNA_LOG_DEBUG("IP Address %s", ip_address); device_type = gupnp_device_info_get_device_type(device_proxy); if (!g_str_has_prefix(device_type, DLS_DMS_DEVICE_TYPE)) { device_info = prv_lookup_dms_child_device(device_proxy); if (device_info == NULL) goto on_error; } else { device_info = device_proxy; } device = g_hash_table_lookup(upnp->device_udn_map, udn); if (!device) { device = g_hash_table_lookup(upnp->sleeping_device_udn_map, udn); if (device != NULL) { if (g_hash_table_lookup_extended( upnp->sleeping_device_udn_map, udn, &key, &val)) { g_hash_table_steal( upnp->sleeping_device_udn_map, udn); g_free(key); } g_hash_table_insert(upnp->device_udn_map, g_strdup(udn), device); if (device->wake_on_timeout_id) { DLEYNA_LOG_DEBUG("Stop WAKE-ON watcher..."); (void) g_source_remove( device->wake_on_timeout_id); device->wake_on_timeout_id = 0; } dls_device_delete_context(device->sleeping_context); device->sleeping_context = NULL; device->sleeping = FALSE; subscribe = TRUE; } } if (!device) { priv_t = g_hash_table_lookup(upnp->device_uc_map, udn); if (priv_t) device = priv_t->device; } if (!device) { DLEYNA_LOG_DEBUG("Device not found. Adding"); DLEYNA_LOG_DEBUG_NL(); queue_id = prv_create_device_queue(&priv_t); device = dls_device_new(upnp->connection, proxy, device_info, ip_address, upnp->interface_info, upnp->property_map, udn, queue_id); prv_update_device_context(priv_t, upnp, udn, device, ip_address, queue_id); } else { DLEYNA_LOG_DEBUG("Device Found"); for (i = 0; i < device->contexts->len; ++i) { context = g_ptr_array_index(device->contexts, i); if (!strcmp(context->ip_address, ip_address)) break; } if (i == device->contexts->len) { DLEYNA_LOG_DEBUG("Adding Context"); (void) dls_device_append_new_context(device, ip_address, proxy, device_info); if (subscribe) dls_device_subscribe_to_service_changes(device); } DLEYNA_LOG_DEBUG_NL(); } on_error: return; } static gboolean prv_subscribe_to_service_changes(gpointer user_data) { dls_device_t *device = user_data; device->timeout_id = 0; dls_device_subscribe_to_service_changes(device); return FALSE; } static void prv_device_unavailable_cb(GUPnPControlPoint *cp, GUPnPDeviceProxy *proxy, gpointer user_data) { dls_upnp_t *upnp = user_data; const char *udn; dls_device_t *device; const gchar *ip_address; unsigned int i; dls_device_context_t *context; gboolean subscribed; gboolean construction_ctx = FALSE; gboolean under_construction = FALSE; prv_device_new_ct_t *priv_t; const dleyna_task_queue_key_t *queue_id; dls_device_context_t *lost_context; gpointer key; gpointer val; DLEYNA_LOG_DEBUG("Enter"); udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *)proxy); ip_address = gssdp_client_get_host_ip( GSSDP_CLIENT(gupnp_control_point_get_context(cp))); if (!udn || !ip_address) goto on_error; DLEYNA_LOG_DEBUG("UDN %s", udn); DLEYNA_LOG_DEBUG("IP Address %s", ip_address); device = g_hash_table_lookup(upnp->device_udn_map, udn); if (!device) { priv_t = g_hash_table_lookup(upnp->device_uc_map, udn); if (priv_t) { device = priv_t->device; under_construction = TRUE; } } if (!device) { DLEYNA_LOG_WARNING("Device not found. Ignoring"); goto on_error; } for (i = 0; i < device->contexts->len; ++i) { context = g_ptr_array_index(device->contexts, i); if (!strcmp(context->ip_address, ip_address)) break; } if (i >= device->contexts->len) goto on_error; subscribed = (context->cds.subscribed || context->ems.subscribed); if (under_construction) construction_ctx = !strcmp(context->ip_address, priv_t->ip_address); g_ptr_array_set_free_func(device->contexts, NULL); lost_context = g_ptr_array_remove_index(device->contexts, i); g_ptr_array_set_free_func(device->contexts, (GDestroyNotify)dls_device_delete_context); if (device->contexts->len == 0) { if (!under_construction) { DLEYNA_LOG_DEBUG("Last Context lost."); if (!device->sleeping) { DLEYNA_LOG_DEBUG("Delete device."); upnp->lost_server(device->path, upnp->user_data); g_hash_table_remove(upnp->device_udn_map, udn); } else { DLEYNA_LOG_DEBUG("Persist sleeping device."); dleyna_task_processor_remove_queues_for_sink( dls_server_get_task_processor(), device->path); g_hash_table_insert( upnp->sleeping_device_udn_map, g_strdup(udn), device); if (g_hash_table_lookup_extended( upnp->device_udn_map, udn, &key, &val)) { g_hash_table_steal(upnp->device_udn_map, udn); g_free(key); } device->sleeping_context = lost_context; lost_context = NULL; } } else { DLEYNA_LOG_WARNING( "Device under construction. Cancelling"); dleyna_task_processor_cancel_queue(priv_t->queue_id); } } else if (under_construction && construction_ctx) { DLEYNA_LOG_WARNING( "Device under construction. Switching context"); /* Cancel previous contruction task chain */ g_hash_table_remove(priv_t->upnp->device_uc_map, priv_t->udn); dleyna_task_queue_set_finally(priv_t->queue_id, prv_device_context_switch_end); dleyna_task_processor_cancel_queue(priv_t->queue_id); /* Create a new construction task chain */ context = dls_device_get_context(device, NULL); queue_id = prv_create_device_queue(&priv_t); prv_update_device_context(priv_t, upnp, udn, device, context->ip_address, queue_id); /* Start tasks from current construction step */ dls_device_construct(device, context, upnp->connection, upnp->interface_info, upnp->property_map, queue_id); } else if (subscribed && !device->timeout_id) { DLEYNA_LOG_DEBUG("Subscribe on new context"); device->timeout_id = g_timeout_add_seconds(1, prv_subscribe_to_service_changes, device); } if (lost_context != NULL) dls_device_delete_context(lost_context); on_error: DLEYNA_LOG_DEBUG("Exit"); DLEYNA_LOG_DEBUG_NL(); return; } static void prv_on_context_available(GUPnPContextManager *context_manager, GUPnPContext *context, gpointer user_data) { dls_upnp_t *upnp = user_data; GUPnPControlPoint *cp; cp = gupnp_control_point_new( context, "upnp:rootdevice"); g_signal_connect(cp, "device-proxy-available", G_CALLBACK(prv_device_available_cb), upnp); g_signal_connect(cp, "device-proxy-unavailable", G_CALLBACK(prv_device_unavailable_cb), upnp); gssdp_resource_browser_set_active(GSSDP_RESOURCE_BROWSER(cp), TRUE); gupnp_context_manager_manage_control_point(upnp->context_manager, cp); g_object_unref(cp); } dls_upnp_t *dls_upnp_new(dleyna_connector_id_t connection, guint port, const dleyna_connector_dispatch_cb_t *dispatch_table, dls_upnp_callback_t found_server, dls_upnp_callback_t lost_server, void *user_data) { dls_upnp_t *upnp = g_new0(dls_upnp_t, 1); upnp->connection = connection; upnp->interface_info = dispatch_table; upnp->user_data = user_data; upnp->found_server = found_server; upnp->lost_server = lost_server; upnp->device_udn_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, dls_device_delete); upnp->sleeping_device_udn_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, dls_device_delete); upnp->device_uc_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); dls_prop_maps_new(&upnp->property_map, &upnp->filter_map); upnp->context_manager = gupnp_context_manager_create(port); g_signal_connect(upnp->context_manager, "context-available", G_CALLBACK(prv_on_context_available), upnp); return upnp; } void dls_upnp_delete(dls_upnp_t *upnp) { if (upnp) { g_signal_handlers_disconnect_by_func (G_OBJECT (upnp->context_manager), G_CALLBACK(prv_on_context_available), upnp); g_object_unref(upnp->context_manager); g_hash_table_unref(upnp->property_map); g_hash_table_unref(upnp->filter_map); g_hash_table_unref(upnp->device_udn_map); g_hash_table_unref(upnp->sleeping_device_udn_map); g_hash_table_unref(upnp->device_uc_map); g_free(upnp); } } GVariant *dls_upnp_get_device_ids(dls_upnp_t *upnp) { GVariantBuilder vb; GHashTableIter iter; gpointer value; dls_device_t *device; GVariant *retval; DLEYNA_LOG_DEBUG("Enter"); g_variant_builder_init(&vb, G_VARIANT_TYPE("ao")); g_hash_table_iter_init(&iter, upnp->device_udn_map); while (g_hash_table_iter_next(&iter, NULL, &value)) { device = value; DLEYNA_LOG_DEBUG("Have device %s", device->path); g_variant_builder_add(&vb, "o", device->path); } g_hash_table_iter_init(&iter, upnp->sleeping_device_udn_map); while (g_hash_table_iter_next(&iter, NULL, &value)) { device = value; DLEYNA_LOG_DEBUG("Have sleeping device %s", device->path); g_variant_builder_add(&vb, "o", device->path); } retval = g_variant_ref_sink(g_variant_builder_end(&vb)); DLEYNA_LOG_DEBUG("Exit"); return retval; } GHashTable *dls_upnp_get_device_udn_map(dls_upnp_t *upnp) { return upnp->device_udn_map; } GHashTable *dls_upnp_get_sleeping_device_udn_map(dls_upnp_t *upnp) { return upnp->sleeping_device_udn_map; } void dls_upnp_delete_sleeping_device(dls_upnp_t *upnp, dls_device_t *device) { const char *udn; dls_device_context_t *ctx = device->sleeping_context; udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *)ctx->device_proxy); upnp->lost_server(device->path, upnp->user_data); g_hash_table_remove(upnp->sleeping_device_udn_map, udn); } void dls_upnp_get_children(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_bas_t *cb_task_data; gchar *upnp_filter = NULL; gchar *sort_by = NULL; DLEYNA_LOG_DEBUG("Enter"); DLEYNA_LOG_DEBUG("Path: %s", task->target.path); DLEYNA_LOG_DEBUG("Start: %u", task->ut.get_children.start); DLEYNA_LOG_DEBUG("Count: %u", task->ut.get_children.count); cb_data->cb = cb; cb_task_data = &cb_data->ut.bas; cb_task_data->filter_mask = dls_props_parse_filter(upnp->filter_map, task->ut.get_children.filter, &upnp_filter); DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x", cb_task_data->filter_mask); sort_by = dls_sort_translate_sort_string(upnp->filter_map, task->ut.get_children.sort_by); if (!sort_by) { DLEYNA_LOG_WARNING("Invalid Sort Criteria"); cb_data->error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_QUERY, "Sort Criteria are not valid"); goto on_error; } DLEYNA_LOG_DEBUG("Sort By %s", sort_by); cb_task_data->protocol_info = client->protocol_info; dls_device_get_children(client, task, upnp_filter, sort_by); on_error: if (!cb_data->action) (void) g_idle_add(dls_async_task_complete, cb_data); g_free(sort_by); g_free(upnp_filter); DLEYNA_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS"); } void dls_upnp_get_all_props(dls_upnp_t *upnp, dls_client_t *client, dls_task_t *task, dls_upnp_task_complete_t cb) { gboolean root_object; dls_async_task_t *cb_data = (dls_async_task_t *)task; dls_async_get_all_t *cb_task_data; DLEYNA_LOG_DEBUG("Enter"); DLEYNA_LOG_DEBUG("Path: %s", task->target.path); DLEYNA_LOG_DEBUG("Interface %s", task->ut.get_prop.interface_name); cb_data->cb = cb; cb_task_data = &cb_data->ut.get_all; root_object = task->target.id[0] == '0' && task->target.id[1] == 0; DLEYNA_LOG_DEBUG("Root Object = %d", root_object); cb_task_data->protocol_info = client->protocol_info; cb_task_data->filter_mask = DLS_UPNP_MASK_ALL_PROPS; dls_device_get_all_props(client, task, root_object); DLEYNA_LOG_DEBUG("Exit with SUCCESS"); } void dls_upnp_get_prop(dls_upnp_t *upnp, dls_client_t *client, dls_task_t *task, dls_upnp_task_complete_t cb) { gboolean root_object; dls_async_task_t *cb_data = (dls_async_task_t *)task; dls_async_get_prop_t *cb_task_data; dls_prop_map_t *prop_map; dls_task_get_prop_t *task_data; DLEYNA_LOG_DEBUG("Enter"); DLEYNA_LOG_DEBUG("Path: %s", task->target.path); DLEYNA_LOG_DEBUG("Interface %s", task->ut.get_prop.interface_name); DLEYNA_LOG_DEBUG("Prop.%s", task->ut.get_prop.prop_name); task_data = &task->ut.get_prop; cb_data->cb = cb; cb_task_data = &cb_data->ut.get_prop; root_object = task->target.id[0] == '0' && task->target.id[1] == 0; DLEYNA_LOG_DEBUG("Root Object = %d", root_object); cb_task_data->protocol_info = client->protocol_info; prop_map = g_hash_table_lookup(upnp->filter_map, task_data->prop_name); dls_device_get_prop(client, task, prop_map, root_object); DLEYNA_LOG_DEBUG("Exit with SUCCESS"); } void dls_upnp_search(dls_upnp_t *upnp, dls_client_t *client, dls_task_t *task, dls_upnp_task_complete_t cb) { gchar *upnp_filter = NULL; gchar *upnp_query = NULL; gchar *sort_by = NULL; dls_async_task_t *cb_data = (dls_async_task_t *)task; dls_async_bas_t *cb_task_data; DLEYNA_LOG_DEBUG("Enter"); DLEYNA_LOG_DEBUG("Path: %s", task->target.path); DLEYNA_LOG_DEBUG("Query: %s", task->ut.search.query); DLEYNA_LOG_DEBUG("Start: %u", task->ut.search.start); DLEYNA_LOG_DEBUG("Count: %u", task->ut.search.count); cb_data->cb = cb; cb_task_data = &cb_data->ut.bas; cb_task_data->filter_mask = dls_props_parse_filter(upnp->filter_map, task->ut.search.filter, &upnp_filter); DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x", cb_task_data->filter_mask); upnp_query = dls_search_translate_search_string(upnp->filter_map, task->ut.search.query); if (!upnp_query) { DLEYNA_LOG_WARNING("Query string is not valid:%s", task->ut.search.query); cb_data->error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_QUERY, "Query string is not valid."); goto on_error; } DLEYNA_LOG_DEBUG("UPnP Query %s", upnp_query); sort_by = dls_sort_translate_sort_string(upnp->filter_map, task->ut.search.sort_by); if (!sort_by) { DLEYNA_LOG_WARNING("Invalid Sort Criteria"); cb_data->error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_QUERY, "Sort Criteria are not valid"); goto on_error; } DLEYNA_LOG_DEBUG("Sort By %s", sort_by); cb_task_data->protocol_info = client->protocol_info; dls_device_search(client, task, upnp_filter, upnp_query, sort_by); on_error: if (!cb_data->action) (void) g_idle_add(dls_async_task_complete, cb_data); g_free(sort_by); g_free(upnp_query); g_free(upnp_filter); 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) { dls_async_task_t *cb_data = (dls_async_task_t *)task; dls_async_get_all_t *cb_task_data; gchar *upnp_filter = NULL; DLEYNA_LOG_DEBUG("Enter"); DLEYNA_LOG_DEBUG("Protocol Info: %s ", task->ut.resource.protocol_info); cb_data->cb = cb; cb_task_data = &cb_data->ut.get_all; DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path, task->target.id); cb_task_data->filter_mask = dls_props_parse_filter(upnp->filter_map, task->ut.resource.filter, &upnp_filter); DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x", cb_task_data->filter_mask); dls_device_get_resource(client, task, upnp_filter); g_free(upnp_filter); DLEYNA_LOG_DEBUG("Exit"); } static gboolean prv_compute_mime_and_class(dls_task_t *task, dls_async_upload_t *cb_task_data, GError **error) { gchar *content_type = NULL; if (!g_file_test(task->ut.upload.file_path, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) { DLEYNA_LOG_WARNING( "File %s does not exist or is not a regular file", task->ut.upload.file_path); *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_OBJECT_NOT_FOUND, "File %s does not exist or is not a regular file", task->ut.upload.file_path); goto on_error; } content_type = g_content_type_guess(task->ut.upload.file_path, NULL, 0, NULL); if (!content_type) { DLEYNA_LOG_WARNING("Unable to determine Content Type for %s", task->ut.upload.file_path); *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME, "Unable to determine Content Type for %s", task->ut.upload.file_path); goto on_error; } cb_task_data->mime_type = g_content_type_get_mime_type(content_type); g_free(content_type); if (!cb_task_data->mime_type) { DLEYNA_LOG_WARNING("Unable to determine MIME Type for %s", task->ut.upload.file_path); *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME, "Unable to determine MIME Type for %s", task->ut.upload.file_path); goto on_error; } if (g_content_type_is_a(cb_task_data->mime_type, "image/*")) { cb_task_data->object_class = "object.item.imageItem"; } else if (g_content_type_is_a(cb_task_data->mime_type, "audio/*")) { cb_task_data->object_class = "object.item.audioItem"; } else if (g_content_type_is_a(cb_task_data->mime_type, "video/*")) { cb_task_data->object_class = "object.item.videoItem"; } else { DLEYNA_LOG_WARNING("Unsupported MIME Type %s", cb_task_data->mime_type); *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME, "Unsupported MIME Type %s", cb_task_data->mime_type); goto on_error; } return TRUE; on_error: return FALSE; } void dls_upnp_upload_to_any(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_upload_t *cb_task_data; DLEYNA_LOG_DEBUG("Enter"); cb_data->cb = cb; cb_task_data = &cb_data->ut.upload; DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path, task->target.id); if (strcmp(task->target.id, "0")) { DLEYNA_LOG_WARNING("Bad path %s", task->target.path); cb_data->error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH, "UploadToAnyContainer must be executed on a root path"); goto on_error; } if (!prv_compute_mime_and_class(task, cb_task_data, &cb_data->error)) goto on_error; DLEYNA_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type); DLEYNA_LOG_DEBUG("Object class %s", cb_task_data->object_class); dls_device_upload(client, task, "DLNA.ORG_AnyContainer"); on_error: if (!cb_data->action) (void) g_idle_add(dls_async_task_complete, cb_data); DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_upload(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_upload_t *cb_task_data; DLEYNA_LOG_DEBUG("Enter"); cb_data->cb = cb; cb_task_data = &cb_data->ut.upload; if (!prv_compute_mime_and_class(task, cb_task_data, &cb_data->error)) goto on_error; DLEYNA_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type); DLEYNA_LOG_DEBUG("Object class %s", cb_task_data->object_class); dls_device_upload(client, task, task->target.id); on_error: if (!cb_data->action) (void) g_idle_add(dls_async_task_complete, cb_data); DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_get_upload_status(dls_upnp_t *upnp, dls_task_t *task) { GError *error = NULL; DLEYNA_LOG_DEBUG("Enter"); DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path, task->target.id); if (strcmp(task->target.id, "0")) { DLEYNA_LOG_WARNING("Bad path %s", task->target.path); error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH, "GetUploadStatus must be executed on a root path"); goto on_error; } (void) dls_device_get_upload_status(task, &error); on_error: if (error) { dls_task_fail(task, error); g_error_free(error); } else { dls_task_complete(task); } DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_get_upload_ids(dls_upnp_t *upnp, dls_task_t *task) { GError *error = NULL; DLEYNA_LOG_DEBUG("Enter"); DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path, task->target.id); if (strcmp(task->target.id, "0")) { DLEYNA_LOG_WARNING("Bad path %s", task->target.path); error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH, "GetUploadIDs must be executed on a root path"); goto on_error; } dls_device_get_upload_ids(task); on_error: if (error) { dls_task_fail(task, error); g_error_free(error); } else { dls_task_complete(task); } DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_cancel_upload(dls_upnp_t *upnp, dls_task_t *task) { GError *error = NULL; DLEYNA_LOG_DEBUG("Enter"); DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path, task->target.id); if (strcmp(task->target.id, "0")) { DLEYNA_LOG_WARNING("Bad path %s", task->target.path); error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH, "CancelUpload must be executed on a root path"); goto on_error; } (void) dls_device_cancel_upload(task, &error); on_error: if (error) { dls_task_fail(task, error); g_error_free(error); } else { dls_task_complete(task); } DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_delete_object(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; DLEYNA_LOG_DEBUG("Enter"); cb_data->cb = cb; DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path, task->target.id); dls_device_delete_object(client, task); DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_create_container(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; DLEYNA_LOG_DEBUG("Enter"); cb_data->cb = cb; DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path, task->target.id); dls_device_create_container(client, task, task->target.id); if (!cb_data->action) (void) g_idle_add(dls_async_task_complete, cb_data); DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_create_container_in_any(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; DLEYNA_LOG_DEBUG("Enter"); cb_data->cb = cb; DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path, task->target.id); if (strcmp(task->target.id, "0")) { DLEYNA_LOG_WARNING("Bad path %s", task->target.path); cb_data->error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH, "CreateContainerInAnyContainer must be executed on a root path"); goto on_error; } dls_device_create_container(client, task, "DLNA.ORG_AnyContainer"); on_error: if (!cb_data->action) (void) g_idle_add(dls_async_task_complete, cb_data); DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_update_object(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_update_t *cb_task_data; dls_upnp_prop_mask mask; gchar *upnp_filter = NULL; dls_task_update_t *task_data; DLEYNA_LOG_DEBUG("Enter"); cb_data->cb = cb; cb_task_data = &cb_data->ut.update; task_data = &task->ut.update; DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path, task->target.id); if (!dls_props_parse_update_filter(upnp->filter_map, task_data->to_add_update, task_data->to_delete, &mask, &upnp_filter)) { DLEYNA_LOG_WARNING("Invalid Parameter"); cb_data->error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_OPERATION_FAILED, "Invalid Parameter"); goto on_error; } cb_task_data->map = upnp->filter_map; DLEYNA_LOG_DEBUG("Filter = %s", upnp_filter); DLEYNA_LOG_DEBUG("Mask = 0x%"G_GUINT64_FORMAT"x", mask); if (mask == 0) { DLEYNA_LOG_WARNING("Empty Parameters"); cb_data->error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_OPERATION_FAILED, "Empty Parameters"); goto on_error; } dls_device_update_object(client, task, upnp_filter); on_error: g_free(upnp_filter); if (!cb_data->action) (void) g_idle_add(dls_async_task_complete, cb_data); DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_get_object_metadata(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; DLEYNA_LOG_DEBUG("Enter"); cb_data->cb = cb; DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path, task->target.id); dls_device_get_object_metadata(client, task, task->target.id); DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_create_reference(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; DLEYNA_LOG_DEBUG("Enter"); cb_data->cb = cb; DLEYNA_LOG_DEBUG("Root Path: %s - Id: %s", task->target.root_path, task->target.id); dls_device_create_reference(client, task); DLEYNA_LOG_DEBUG("Exit"); return; } void dls_upnp_get_icon(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; DLEYNA_LOG_DEBUG("Enter"); cb_data->cb = cb; dls_device_get_icon(client, task); DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_wake(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; DLEYNA_LOG_DEBUG("Enter"); cb_data->cb = cb; dls_device_wake(client, task); DLEYNA_LOG_DEBUG("Exit"); } void dls_upnp_unsubscribe(dls_upnp_t *upnp) { GHashTableIter iter; gpointer value; dls_device_t *device; DLEYNA_LOG_DEBUG("Enter"); g_hash_table_iter_init(&iter, upnp->device_udn_map); while (g_hash_table_iter_next(&iter, NULL, &value)) { device = value; dls_device_unsubscribe(device); } DLEYNA_LOG_DEBUG("Exit"); } static gboolean prv_device_uc_find(gpointer key, gpointer value, gpointer user_data) { prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)value; return (priv_t->device == user_data) ? TRUE : FALSE; } static gboolean prv_device_find(gpointer key, gpointer value, gpointer user_data) { return (value == user_data) ? TRUE : FALSE; } gboolean dls_upnp_device_context_exist(dls_device_t *device, dls_device_context_t *context) { gpointer result; guint i; gboolean found = FALSE; dls_upnp_t *upnp = dls_server_get_upnp(); if (upnp == NULL) goto on_exit; /* Check if the device still exist */ result = g_hash_table_find(upnp->device_udn_map, prv_device_find, device); if (result == NULL) if (g_hash_table_find(upnp->device_uc_map, prv_device_uc_find, device) == NULL) goto on_exit; /* Search if the context still exist in the device */ for (i = 0; i < device->contexts->len; ++i) { if (g_ptr_array_index(device->contexts, i) == context) { found = TRUE; break; } } on_exit: return found; } void dls_upnp_rescan(dls_upnp_t *upnp) { DLEYNA_LOG_DEBUG("re-scanning control points"); gupnp_context_manager_rescan_control_points(upnp->context_manager); } GUPnPContextManager *dls_upnp_get_context_manager(dls_upnp_t *upnp) { return upnp->context_manager; }