/*
 * 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 <mark.d.ryan@intel.com>
 * Regis Merlino <regis.merlino@intel.com>
 *
 */

#include <glib.h>
#include <string.h>

#include <libdleyna/core/connector.h>
#include <libdleyna/core/control-point.h>
#include <libdleyna/core/error.h>
#include <libdleyna/core/log.h>
#include <libdleyna/core/task-processor.h>
#include <libdleyna/core/white-list.h>

#include "async.h"
#include "client.h"
#include "control-point-server.h"
#include "device.h"
#include "interface.h"
#include "manager.h"
#include "path.h"
#include "server.h"
#include "upnp.h"

#ifdef UA_PREFIX
	#define DLS_PRG_NAME UA_PREFIX " dLeyna/" VERSION
#else
	#define DLS_PRG_NAME "dLeyna/" VERSION
#endif


typedef struct dls_server_context_t_ dls_server_context_t;
struct dls_server_context_t_ {
	dleyna_connector_id_t connection;
	dleyna_task_processor_t *processor;
	const dleyna_connector_t *connector;
	dleyna_settings_t *settings;
	guint dls_id[DLS_MANAGER_INTERFACE_INFO_MAX];
	GHashTable *watchers;
	dls_upnp_t *upnp;
	dls_manager_t *manager;
};

static dls_server_context_t g_context;

static const gchar g_root_introspection[] =
	"<node>"
	"  <interface name='"DLEYNA_SERVER_INTERFACE_MANAGER"'>"
	"    <method name='"DLS_INTERFACE_GET_VERSION"'>"
	"      <arg type='s' name='"DLS_INTERFACE_VERSION"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_RELEASE"'>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_GET_SERVERS"'>"
	"      <arg type='ao' name='"DLS_INTERFACE_SERVERS"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_RESCAN"'>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_SET_PROTOCOL_INFO"'>"
	"      <arg type='s' name='"DLS_INTERFACE_PROTOCOL_INFO"'"
	"           direction='in'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_PREFER_LOCAL_ADDRESSES"'>"
	"      <arg type='b' name='"DLS_INTERFACE_PREFER"'"
	"           direction='in'/>"
	"    </method>"
	"    <signal name='"DLS_INTERFACE_FOUND_SERVER"'>"
	"      <arg type='o' name='"DLS_INTERFACE_PATH"'/>"
	"    </signal>"
	"    <signal name='"DLS_INTERFACE_LOST_SERVER"'>"
	"      <arg type='o' name='"DLS_INTERFACE_PATH"'/>"
	"    </signal>"
	"    <property type='as' name='"DLS_INTERFACE_PROP_NEVER_QUIT"'"
	"       access='readwrite'/>"
	"    <property type='as' name='"DLS_INTERFACE_PROP_WHITE_LIST_ENTRIES"'"
	"       access='readwrite'/>"
	"    <property type='b' name='"DLS_INTERFACE_PROP_WHITE_LIST_ENABLED"'"
	"       access='readwrite'/>"
	"  </interface>"
	"  <interface name='"DLS_INTERFACE_PROPERTIES"'>"
	"    <method name='"DLS_INTERFACE_GET"'>"
	"      <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_PROPERTY_NAME"'"
	"           direction='in'/>"
	"      <arg type='v' name='"DLS_INTERFACE_VALUE"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_GET_ALL"'>"
	"      <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'"
	"           direction='in'/>"
	"      <arg type='a{sv}' name='"DLS_INTERFACE_PROPERTIES_VALUE"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_SET"'>"
	"      <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_PROPERTY_NAME"'"
	"           direction='in'/>"
	"      <arg type='v' name='"DLS_INTERFACE_VALUE"'"
	"           direction='in'/>"
	"    </method>"
	"    <signal name='"DLS_INTERFACE_PROPERTIES_CHANGED"'>"
	"      <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'/>"
	"      <arg type='a{sv}' name='"DLS_INTERFACE_CHANGED_PROPERTIES"'/>"
	"      <arg type='as' name='"
	DLS_INTERFACE_INVALIDATED_PROPERTIES"'/>"
	"    </signal>"
	"  </interface>"
	"</node>";

static const gchar g_server_introspection[] =
	"<node>"
	"  <interface name='"DLS_INTERFACE_PROPERTIES"'>"
	"    <method name='"DLS_INTERFACE_GET"'>"
	"      <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_PROPERTY_NAME"'"
	"           direction='in'/>"
	"      <arg type='v' name='"DLS_INTERFACE_VALUE"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_GET_ALL"'>"
	"      <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'"
	"           direction='in'/>"
	"      <arg type='a{sv}' name='"DLS_INTERFACE_PROPERTIES_VALUE"'"
	"           direction='out'/>"
	"    </method>"
	"    <signal name='"DLS_INTERFACE_PROPERTIES_CHANGED"'>"
	"      <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'/>"
	"      <arg type='a{sv}' name='"DLS_INTERFACE_CHANGED_PROPERTIES"'/>"
	"      <arg type='as' name='"DLS_INTERFACE_INVALIDATED_PROPERTIES"'/>"
	"    </signal>"
	"  </interface>"
	"  <interface name='"DLS_INTERFACE_MEDIA_OBJECT"'>"
	"    <property type='o' name='"DLS_INTERFACE_PROP_PARENT"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_TYPE_EX"'"
	"       access='read'/>"
	"    <property type='o' name='"DLS_INTERFACE_PROP_PATH"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_CREATOR"'"
	"       access='read'/>"
	"    <property type='b' name='"DLS_INTERFACE_PROP_RESTRICTED"'"
	"       access='read'/>"
	"    <property type='a{sb}' name='"DLS_INTERFACE_PROP_DLNA_MANAGED"'"
	"       access='read'/>"
	"    <property type='u' name='"DLS_INTERFACE_PROP_OBJECT_UPDATE_ID"'"
	"       access='read'/>"
	"    <method name='"DLS_INTERFACE_DELETE"'>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_UPDATE"'>"
	"      <arg type='a{sv}' name='"DLS_INTERFACE_TO_ADD_UPDATE"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_TO_DELETE"'"
	"           direction='in'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_GET_METADATA"'>"
	"      <arg type='s' name='"DLS_INTERFACE_METADATA"'"
	"           direction='out'/>"
	"    </method>"
	"  </interface>"
	"  <interface name='"DLS_INTERFACE_MEDIA_CONTAINER"'>"
	"    <method name='"DLS_INTERFACE_LIST_CHILDREN"'>"
	"      <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_MAX"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_FILTER"'"
	"           direction='in'/>"
	"      <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_LIST_CHILDREN_EX"'>"
	"      <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_MAX"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_FILTER"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
	"           direction='in'/>"
	"      <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_LIST_CONTAINERS"'>"
	"      <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_MAX"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_FILTER"'"
	"           direction='in'/>"
	"      <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_LIST_CONTAINERS_EX"'>"
	"      <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_MAX"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_FILTER"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
	"           direction='in'/>"
	"      <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_LIST_ITEMS"'>"
	"      <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_MAX"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_FILTER"'"
	"           direction='in'/>"
	"      <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_LIST_ITEMS_EX"'>"
	"      <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_MAX"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_FILTER"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
	"           direction='in'/>"
	"      <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_SEARCH_OBJECTS"'>"
	"      <arg type='s' name='"DLS_INTERFACE_QUERY"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_MAX"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_FILTER"'"
	"           direction='in'/>"
	"      <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_SEARCH_OBJECTS_EX"'>"
	"      <arg type='s' name='"DLS_INTERFACE_QUERY"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_MAX"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_FILTER"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
	"           direction='in'/>"
	"      <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
	"           direction='out'/>"
	"      <arg type='u' name='"DLS_INTERFACE_TOTAL_ITEMS"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_UPLOAD"'>"
	"      <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_FILE_PATH"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
	"           direction='out'/>"
	"      <arg type='o' name='"DLS_INTERFACE_PATH"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_CREATE_CONTAINER"'>"
	"      <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_CHILD_TYPES"'"
	"           direction='in'/>"
	"      <arg type='o' name='"DLS_INTERFACE_PATH"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_GET_COMPATIBLE_RESOURCE"'>"
	"      <arg type='s' name='"DLS_INTERFACE_PROTOCOL_INFO"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_FILTER"'"
	"           direction='in'/>"
	"      <arg type='a{sv}' name='"DLS_INTERFACE_PROPERTIES_VALUE"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_CREATE_REFERENCE"'>"
	"      <arg type='o' name='"DLS_INTERFACE_PATH"'"
	"           direction='in'/>"
	"      <arg type='o' name='"DLS_INTERFACE_REFPATH"'"
	"           direction='out'/>"
	"    </method>"
	"    <property type='u' name='"DLS_INTERFACE_PROP_CHILD_COUNT"'"
	"       access='read'/>"
	"    <property type='b' name='"DLS_INTERFACE_PROP_SEARCHABLE"'"
	"       access='read'/>"
	"    <property type='a(sb)' name='"DLS_INTERFACE_PROP_CREATE_CLASSES"'"
	"       access='read'/>"
	"    <property type='u' name='"DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID"'"
	"       access='read'/>"
	"    <property type='u' name='"
	DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT"'"
	"       access='read'/>"
	"    <property type='aa{sv}' name='"DLS_INTERFACE_PROP_RESOURCES"'"
	"       access='read'/>"
	"    <property type='as' name='"DLS_INTERFACE_PROP_URLS"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_MIME_TYPE"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_DLNA_PROFILE"'"
	"       access='read'/>"
	"    <property type='a{sb}' name='"DLS_INTERFACE_PROP_DLNA_CONVERSION"'"
	"       access='read'/>"
	"    <property type='a{sb}' name='"DLS_INTERFACE_PROP_DLNA_OPERATION"'"
	"       access='read'/>"
	"    <property type='a{sb}' name='"DLS_INTERFACE_PROP_DLNA_FLAGS"'"
	"       access='read'/>"
	"    <property type='x' name='"DLS_INTERFACE_PROP_SIZE"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_ARTIST"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_ALBUM_ART_URL"'"
	"       access='read'/>"
	"  </interface>"
	"  <interface name='"DLS_INTERFACE_MEDIA_ITEM"'>"
	"    <method name='"DLS_INTERFACE_GET_COMPATIBLE_RESOURCE"'>"
	"      <arg type='s' name='"DLS_INTERFACE_PROTOCOL_INFO"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_FILTER"'"
	"           direction='in'/>"
	"      <arg type='a{sv}' name='"DLS_INTERFACE_PROPERTIES_VALUE"'"
	"           direction='out'/>"
	"    </method>"
	"    <property type='as' name='"DLS_INTERFACE_PROP_URLS"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_MIME_TYPE"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_ARTIST"'"
	"       access='read'/>"
	"    <property type='as' name='"DLS_INTERFACE_PROP_ARTISTS"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_ALBUM"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_DATE"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_GENRE"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_DLNA_PROFILE"'"
	"       access='read'/>"
	"    <property type='a{sb}' name='"DLS_INTERFACE_PROP_DLNA_CONVERSION"'"
	"       access='read'/>"
	"    <property type='a{sb}' name='"DLS_INTERFACE_PROP_DLNA_OPERATION"'"
	"       access='read'/>"
	"    <property type='a{sb}' name='"DLS_INTERFACE_PROP_DLNA_FLAGS"'"
	"       access='read'/>"
	"    <property type='i' name='"DLS_INTERFACE_PROP_TRACK_NUMBER"'"
	"       access='read'/>"
	"    <property type='x' name='"DLS_INTERFACE_PROP_SIZE"'"
	"       access='read'/>"
	"    <property type='i' name='"DLS_INTERFACE_PROP_DURATION"'"
	"       access='read'/>"
	"    <property type='i' name='"DLS_INTERFACE_PROP_BITRATE"'"
	"       access='read'/>"
	"    <property type='i' name='"DLS_INTERFACE_PROP_SAMPLE_RATE"'"
	"       access='read'/>"
	"    <property type='i' name='"DLS_INTERFACE_PROP_BITS_PER_SAMPLE"'"
	"       access='read'/>"
	"    <property type='i' name='"DLS_INTERFACE_PROP_WIDTH"'"
	"       access='read'/>"
	"    <property type='i' name='"DLS_INTERFACE_PROP_HEIGHT"'"
	"       access='read'/>"
	"    <property type='i' name='"DLS_INTERFACE_PROP_COLOR_DEPTH"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_ALBUM_ART_URL"'"
	"       access='read'/>"
	"    <property type='o' name='"DLS_INTERFACE_PROP_REFPATH"'"
	"       access='read'/>"
	"    <property type='aa{sv}' name='"DLS_INTERFACE_PROP_RESOURCES"'"
	"       access='read'/>"
	"  </interface>"
	"  <interface name='"DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE"'>"
	"    <method name='"DLS_INTERFACE_UPLOAD_TO_ANY"'>"
	"      <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_FILE_PATH"'"
	"           direction='in'/>"
	"      <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
	"           direction='out'/>"
	"      <arg type='o' name='"DLS_INTERFACE_PATH"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_GET_UPLOAD_STATUS"'>"
	"      <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_UPLOAD_STATUS"'"
	"           direction='out'/>"
	"      <arg type='t' name='"DLS_INTERFACE_LENGTH"'"
	"           direction='out'/>"
	"      <arg type='t' name='"DLS_INTERFACE_TOTAL"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_GET_UPLOAD_IDS"'>"
	"      <arg type='au' name='"DLS_INTERFACE_TOTAL"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_CANCEL_UPLOAD"'>"
	"      <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
	"           direction='in'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_CREATE_CONTAINER_IN_ANY"'>"
	"      <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
	"           direction='in'/>"
	"      <arg type='as' name='"DLS_INTERFACE_CHILD_TYPES"'"
	"           direction='in'/>"
	"      <arg type='o' name='"DLS_INTERFACE_PATH"'"
	"           direction='out'/>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_CANCEL"'>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_WAKE"'>"
	"    </method>"
	"    <method name='"DLS_INTERFACE_GET_ICON"'>"
	"      <arg type='s' name='"DLS_INTERFACE_REQ_MIME_TYPE"'"
	"           direction='in'/>"
	"      <arg type='s' name='"DLS_INTERFACE_RESOLUTION"'"
	"           direction='in'/>"
	"      <arg type='ay' name='"DLS_INTERFACE_ICON_BYTES"'"
	"           direction='out'/>"
	"      <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"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_DEVICE_TYPE"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_FRIENDLY_NAME"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_MANUFACTURER"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_MANUFACTURER_URL"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_MODEL_DESCRIPTION"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_MODEL_NAME"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_MODEL_NUMBER"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_MODEL_URL"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_SERIAL_NUMBER"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_PRESENTATION_URL"'"
	"       access='read'/>"
	"    <property type='s' name='"DLS_INTERFACE_PROP_ICON_URL"'"
	"       access='read'/>"
	"    <property type='b' name='"DLS_INTERFACE_PROP_SLEEPING"'"
	"       access='read'/>"
	"    <property type='a{sv}'name='"
	DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES"'"
	"       access='read'/>"
	"    <property type='as' name='"
	DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES"'"
	"       access='read'/>"
	"    <property type='as' name='"
	DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES"'"
	"       access='read'/>"
	"    <property type='as' name='"
	DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES"'"
	"       access='read'/>"
	"    <property type='a(ssao)' name='"
	DLS_INTERFACE_PROP_SV_FEATURE_LIST"'"
	"       access='read'/>"
	"    <property type='u' name='"
	DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID"'"
	"       access='read'/>"
	"    <property type='s' name='"
	DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN"'"
	"       access='read'/>"
	"    <signal name='"DLS_INTERFACE_ESV_CONTAINER_UPDATE_IDS"'>"
	"      <arg type='a(ou)' name='"DLS_INTERFACE_CONTAINER_PATHS_ID"'/>"
	"    </signal>"
	"    <signal name='"DLS_INTERFACE_CHANGED_EVENT"'>"
	"      <arg type='aa{sv}' name='"DLS_INTERFACE_CHANGED_OBJECTS"'/>"
	"    </signal>"
	"    <signal name='"DLS_INTERFACE_UPLOAD_UPDATE"'>"
	"      <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'/>"
	"      <arg type='s' name='"DLS_INTERFACE_UPLOAD_STATUS"'/>"
	"      <arg type='t' name='"DLS_INTERFACE_LENGTH"'/>"
	"      <arg type='t' name='"DLS_INTERFACE_TOTAL"'/>"
	"    </signal>"
	"  </interface>"
	"</node>";

static const gchar *g_manager_interfaces[DLS_MANAGER_INTERFACE_INFO_MAX] = {
	/* MUST be in the exact same order as g_root_introspection */
	DLEYNA_SERVER_INTERFACE_MANAGER,
	DLS_INTERFACE_PROPERTIES
};

const dleyna_connector_t *dls_server_get_connector(void)
{
	return g_context.connector;
}

dleyna_task_processor_t *dls_server_get_task_processor(void)
{
	return g_context.processor;
}

static void prv_process_sync_task(dls_task_t *task)
{
	dls_client_t *client;
	const gchar *client_name;

	switch (task->type) {
	case DLS_TASK_GET_VERSION:
		task->result = g_variant_ref_sink(g_variant_new_string(
								VERSION));
		dls_task_complete(task);
		break;
	case DLS_TASK_GET_SERVERS:
		task->result = dls_upnp_get_device_ids(g_context.upnp);
		dls_task_complete(task);
		break;
	case DLS_TASK_RESCAN:
		dls_upnp_rescan(g_context.upnp);
		dls_task_complete(task);
		break;
	case DLS_TASK_SET_PROTOCOL_INFO:
		client_name = dleyna_task_queue_get_source(task->atom.queue_id);
		client = g_hash_table_lookup(g_context.watchers, client_name);
		if (client) {
			g_free(client->protocol_info);
			if (task->ut.protocol_info.protocol_info[0]) {
				client->protocol_info =
					task->ut.protocol_info.protocol_info;
				task->ut.protocol_info.protocol_info = NULL;
			} else {
				client->protocol_info = NULL;
			}
		}
		dls_task_complete(task);
		break;
	case DLS_TASK_SET_PREFER_LOCAL_ADDRESSES:
		client_name = dleyna_task_queue_get_source(task->atom.queue_id);
		client = g_hash_table_lookup(g_context.watchers, client_name);
		if (client) {
			client->prefer_local_addresses =
					task->ut.prefer_local_addresses.prefer;
		}
		dls_task_complete(task);
		break;
	case DLS_TASK_GET_UPLOAD_STATUS:
		dls_upnp_get_upload_status(g_context.upnp, task);
		break;
	case DLS_TASK_GET_UPLOAD_IDS:
		dls_upnp_get_upload_ids(g_context.upnp, task);
		break;
	case DLS_TASK_CANCEL_UPLOAD:
		dls_upnp_cancel_upload(g_context.upnp, task);
		break;
	default:
		goto finished;
		break;
	}

	dleyna_task_queue_task_completed(task->atom.queue_id);

finished:
	return;
}

static void prv_async_task_complete(dls_task_t *task, GError *error)
{
	DLEYNA_LOG_DEBUG("Enter");

	if (!error) {
		dls_task_complete(task);
	} else {
		dls_task_fail(task, error);
		g_error_free(error);
	}

	dleyna_task_queue_task_completed(task->atom.queue_id);

	DLEYNA_LOG_DEBUG("Exit");
}

static void prv_process_async_task(dls_task_t *task)
{
	dls_async_task_t *async_task = (dls_async_task_t *)task;
	dls_client_t *client;
	const gchar *client_name;

	DLEYNA_LOG_DEBUG("Enter");

	async_task->cancellable = g_cancellable_new();
	client_name = dleyna_task_queue_get_source(task->atom.queue_id);
	client = g_hash_table_lookup(g_context.watchers, client_name);

	switch (task->type) {
	case DLS_TASK_MANAGER_GET_PROP:
		dls_manager_get_prop(g_context.manager, g_context.settings,
				     task, prv_async_task_complete);
		break;
	case DLS_TASK_MANAGER_GET_ALL_PROPS:
		dls_manager_get_all_props(g_context.manager, g_context.settings,
					  task, prv_async_task_complete);
		break;
	case DLS_TASK_MANAGER_SET_PROP:
		dls_manager_set_prop(g_context.manager, g_context.settings,
				     task, prv_async_task_complete);
		break;
	case DLS_TASK_GET_CHILDREN:
		dls_upnp_get_children(g_context.upnp, client, task,
				      prv_async_task_complete);
		break;
	case DLS_TASK_GET_PROP:
		dls_upnp_get_prop(g_context.upnp, client, task,
				  prv_async_task_complete);
		break;
	case DLS_TASK_GET_ALL_PROPS:
		dls_upnp_get_all_props(g_context.upnp, client, task,
				       prv_async_task_complete);
		break;
	case DLS_TASK_SEARCH:
		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);
		break;
	case DLS_TASK_UPLOAD_TO_ANY:
		dls_upnp_upload_to_any(g_context.upnp, client, task,
				       prv_async_task_complete);
		break;
	case DLS_TASK_UPLOAD:
		dls_upnp_upload(g_context.upnp, client, task,
				prv_async_task_complete);
		break;
	case DLS_TASK_DELETE_OBJECT:
		dls_upnp_delete_object(g_context.upnp, client, task,
				       prv_async_task_complete);
		break;
	case DLS_TASK_CREATE_CONTAINER:
		dls_upnp_create_container(g_context.upnp, client, task,
					  prv_async_task_complete);
		break;
	case DLS_TASK_CREATE_CONTAINER_IN_ANY:
		dls_upnp_create_container_in_any(g_context.upnp, client, task,
						 prv_async_task_complete);
		break;
	case DLS_TASK_UPDATE_OBJECT:
		dls_upnp_update_object(g_context.upnp, client, task,
				       prv_async_task_complete);
		break;
	case DLS_TASK_GET_OBJECT_METADATA:
		dls_upnp_get_object_metadata(g_context.upnp, client, task,
					     prv_async_task_complete);
		break;
	case DLS_TASK_CREATE_REFERENCE:
		dls_upnp_create_reference(g_context.upnp, client, task,
					  prv_async_task_complete);
		break;
	case DLS_TASK_GET_ICON:
		dls_upnp_get_icon(g_context.upnp, client, task,
				  prv_async_task_complete);
		break;
	case DLS_TASK_WAKE:
		dls_upnp_wake(g_context.upnp, client, task,
			      prv_async_task_complete);
		break;
	default:
		break;
	}

	DLEYNA_LOG_DEBUG("Exit");
}

static void prv_process_task(dleyna_task_atom_t *task, gpointer user_data)
{
	dls_task_t *client_task = (dls_task_t *)task;

	if (client_task->synchronous)
		prv_process_sync_task(client_task);
	else
		prv_process_async_task(client_task);
}

static void prv_cancel_task(dleyna_task_atom_t *task, gpointer user_data)
{
	dls_task_cancel((dls_task_t *)task);
}

static void prv_delete_task(dleyna_task_atom_t *task, gpointer user_data)
{
	dls_task_delete((dls_task_t *)task);
}

static void prv_manager_root_method_call(dleyna_connector_id_t conn,
					 const gchar *sender,
					 const gchar *object,
					 const gchar *interface,
					 const gchar *method,
					 GVariant *parameters,
					 dleyna_connector_msg_id_t invocation);

static void prv_manager_props_method_call(dleyna_connector_id_t conn,
					  const gchar *sender,
					  const gchar *object,
					  const gchar *interface,
					  const gchar *method,
					  GVariant *parameters,
					  dleyna_connector_msg_id_t invocation);

static void prv_object_method_call(dleyna_connector_id_t conn,
				   const gchar *sender,
				   const gchar *object,
				   const gchar *interface,
				   const gchar *method,
				   GVariant *parameters,
				   dleyna_connector_msg_id_t invocation);

static void prv_item_method_call(dleyna_connector_id_t conn,
				 const gchar *sender,
				 const gchar *object,
				 const gchar *interface,
				 const gchar *method,
				 GVariant *parameters,
				 dleyna_connector_msg_id_t invocation);

static void prv_con_method_call(dleyna_connector_id_t conn,
				const gchar *sender,
				const gchar *object,
				const gchar *interface,
				const gchar *method,
				GVariant *parameters,
				dleyna_connector_msg_id_t invocation);

static void prv_props_method_call(dleyna_connector_id_t conn,
				  const gchar *sender,
				  const gchar *object,
				  const gchar *interface,
				  const gchar *method,
				  GVariant *parameters,
				  dleyna_connector_msg_id_t invocation);

static void prv_device_method_call(dleyna_connector_id_t conn,
				   const gchar *sender,
				   const gchar *object,
				   const gchar *interface,
				   const gchar *method,
				   GVariant *parameters,
				   dleyna_connector_msg_id_t invocation);

static const dleyna_connector_dispatch_cb_t
			g_root_vtables[DLS_MANAGER_INTERFACE_INFO_MAX] = {
	/* MUST be in the exact same order as g_root_introspection */
	prv_manager_root_method_call,
	prv_manager_props_method_call
};

static const dleyna_connector_dispatch_cb_t
				g_server_vtables[DLS_INTERFACE_INFO_MAX] = {
	/* MUST be in the exact same order as g_server_introspection */
	prv_props_method_call,
	prv_object_method_call,
	prv_con_method_call,
	prv_item_method_call,
	prv_device_method_call
};

static void prv_remove_client(const gchar *name)
{
	dleyna_task_processor_remove_queues_for_source(g_context.processor,
						       name);

	(void) g_hash_table_remove(g_context.watchers, name);

	if (g_hash_table_size(g_context.watchers) == 0)
		if (!dleyna_settings_is_never_quit(g_context.settings))
			dleyna_task_processor_set_quitting(g_context.processor);
}

static void prv_lost_client(const gchar *name)
{
	DLEYNA_LOG_DEBUG("Lost Client %s", name);

	prv_remove_client(name);
}

static void prv_add_task(dls_task_t *task, const gchar *source,
			 const gchar *sink)
{
	dls_client_t *client;
	const dleyna_task_queue_key_t *queue_id;

	if (!g_hash_table_lookup(g_context.watchers, source)) {
		client = g_new0(dls_client_t, 1);
		client->prefer_local_addresses = TRUE;
		g_context.connector->watch_client(source);
		g_hash_table_insert(g_context.watchers, g_strdup(source),
				    client);
	}

	queue_id = dleyna_task_processor_lookup_queue(g_context.processor,
						      source, sink);
	if (!queue_id)
		queue_id = dleyna_task_processor_add_queue(
					g_context.processor,
					source,
					sink,
					DLEYNA_TASK_QUEUE_FLAG_AUTO_START,
					prv_process_task,
					prv_cancel_task,
					prv_delete_task);

	dleyna_task_queue_add_task(queue_id, &task->atom);
}

static void prv_manager_root_method_call(
				dleyna_connector_id_t conn,
				const gchar *sender, const gchar *object,
				const gchar *interface,
				const gchar *method, GVariant *parameters,
				dleyna_connector_msg_id_t invocation)
{
	dls_task_t *task;

	if (!strcmp(method, DLS_INTERFACE_RELEASE)) {
		prv_remove_client(sender);
		g_context.connector->return_response(invocation, NULL);
		goto finished;
	} else if (!strcmp(method, DLS_INTERFACE_RESCAN)) {
		task = dls_task_rescan_new(invocation);
	} else if (!strcmp(method, DLS_INTERFACE_GET_VERSION)) {
		task = dls_task_get_version_new(invocation);
	} else if (!strcmp(method, DLS_INTERFACE_GET_SERVERS)) {
		task = dls_task_get_servers_new(invocation);
	} else if (!strcmp(method, DLS_INTERFACE_SET_PROTOCOL_INFO)) {
		task = dls_task_set_protocol_info_new(invocation,
						      parameters);
	} else if (!strcmp(method, DLS_INTERFACE_PREFER_LOCAL_ADDRESSES)) {
		task = dls_task_prefer_local_addresses_new(invocation,
							   parameters);
	} else {
		goto finished;
	}

	prv_add_task(task, sender, DLS_SERVER_SINK);

finished:

	return;
}

static void prv_manager_props_method_call(dleyna_connector_id_t conn,
					  const gchar *sender,
					  const gchar *object,
					  const gchar *interface,
					  const gchar *method,
					  GVariant *parameters,
					  dleyna_connector_msg_id_t invocation)
{
	dls_task_t *task;
	GError *error = NULL;

	if (!strcmp(method, DLS_INTERFACE_GET_ALL))
		task = dls_task_manager_get_props_new(invocation, object,
						      parameters, &error);
	else if (!strcmp(method, DLS_INTERFACE_GET))
		task = dls_task_manager_get_prop_new(invocation, object,
						     parameters, &error);
	else if (!strcmp(method, DLS_INTERFACE_SET))
		task = dls_task_manager_set_prop_new(invocation, object,
						     parameters, &error);
	else
		goto finished;

	if (!task) {
		g_context.connector->return_error(invocation, error);
		g_error_free(error);

		goto finished;
	}

	prv_add_task(task, sender, task->target.path);

finished:

	return;
}

gboolean dls_server_get_object_info(const gchar *object_path,
					   gchar **root_path,
					   gchar **object_id,
					   dls_device_t **device,
					   GError **error)
{
	if (!dls_path_get_path_and_id(object_path, root_path, object_id,
				      error)) {
		DLEYNA_LOG_WARNING("Bad object %s", object_path);

		goto on_error;
	}

	*device = dls_device_from_path(*root_path,
				dls_upnp_get_device_udn_map(g_context.upnp));

	if (*device == NULL) {
		*device = dls_device_from_path(*root_path,
				dls_upnp_get_sleeping_device_udn_map(
							      g_context.upnp));
	}

	if (*device == NULL) {
		DLEYNA_LOG_WARNING("Cannot locate device for %s", *root_path);

		*error = g_error_new(DLEYNA_SERVER_ERROR,
				     DLEYNA_ERROR_OBJECT_NOT_FOUND,
				     "Cannot locate device corresponding to"
				     " the specified path");

		g_free(*root_path);
		g_free(*object_id);

		goto on_error;
	}

	return TRUE;

on_error:

	return FALSE;
}

gboolean dls_server_is_device_sleeping(dls_device_t *dev)
{
	if (dev->sleeping_context != NULL)
		return TRUE;
	else
		return dev->sleeping;
}

void dls_server_delete_sleeping_device(dls_device_t *dev)
{
	if (dev->sleeping_context != NULL)
		dls_upnp_delete_sleeping_device(g_context.upnp, dev);
}

static const gchar *prv_get_device_id(const gchar *object, GError **error)
{
	dls_device_t *device;
	gchar *root_path;
	gchar *id;

	if (!dls_server_get_object_info(object, &root_path, &id, &device,
					error))
		goto on_error;

	g_free(id);
	g_free(root_path);

	return device->path;

on_error:

	return NULL;
}

static void prv_object_method_call(dleyna_connector_id_t conn,
				   const gchar *sender, const gchar *object,
				   const gchar *interface,
				   const gchar *method,
				   GVariant *parameters,
				   dleyna_connector_msg_id_t invocation)
{
	dls_task_t *task;
	GError *error = NULL;

	if (!strcmp(method, DLS_INTERFACE_DELETE))
		task = dls_task_delete_new(invocation, object, &error);
	else if (!strcmp(method, DLS_INTERFACE_UPDATE))
		task = dls_task_update_new(invocation, object,
					   parameters, &error);
	else if (!strcmp(method, DLS_INTERFACE_GET_METADATA))
		task = dls_task_get_metadata_new(invocation, object, &error);
	else
		goto finished;

	if (!task) {
		g_context.connector->return_error(invocation, error);
		g_error_free(error);

		goto finished;
	}

	prv_add_task(task, sender, task->target.device->path);

finished:

	return;
}

static void prv_item_method_call(dleyna_connector_id_t conn,
				 const gchar *sender,
				 const gchar *object,
				 const gchar *interface,
				 const gchar *method,
				 GVariant *parameters,
				 dleyna_connector_msg_id_t invocation)
{
	dls_task_t *task;
	GError *error = NULL;

	if (!strcmp(method, DLS_INTERFACE_GET_COMPATIBLE_RESOURCE)) {
		task = dls_task_get_resource_new(invocation, object,
						 parameters, &error);

		if (!task) {
			g_context.connector->return_error(invocation, error);
			g_error_free(error);

			goto finished;
		}

		prv_add_task(task, sender, task->target.device->path);
	}

finished:

	return;
}


static void prv_con_method_call(dleyna_connector_id_t conn,
				const gchar *sender,
				const gchar *object,
				const gchar *interface,
				const gchar *method,
				GVariant *parameters,
				dleyna_connector_msg_id_t invocation)
{
	dls_task_t *task;
	GError *error = NULL;

	if (!strcmp(method, DLS_INTERFACE_LIST_CHILDREN))
		task = dls_task_get_children_new(invocation, object,
						 parameters, TRUE,
						 TRUE, &error);
	else if (!strcmp(method, DLS_INTERFACE_LIST_CHILDREN_EX))
		task = dls_task_get_children_ex_new(invocation, object,
						    parameters, TRUE,
						    TRUE, &error);
	else if (!strcmp(method, DLS_INTERFACE_LIST_ITEMS))
		task = dls_task_get_children_new(invocation, object,
						 parameters, TRUE,
						 FALSE, &error);
	else if (!strcmp(method, DLS_INTERFACE_LIST_ITEMS_EX))
		task = dls_task_get_children_ex_new(invocation, object,
						    parameters, TRUE,
						    FALSE, &error);
	else if (!strcmp(method, DLS_INTERFACE_LIST_CONTAINERS))
		task = dls_task_get_children_new(invocation, object,
						 parameters, FALSE,
						 TRUE, &error);
	else if (!strcmp(method, DLS_INTERFACE_LIST_CONTAINERS_EX))
		task = dls_task_get_children_ex_new(invocation, object,
						    parameters, FALSE,
						    TRUE, &error);
	else if (!strcmp(method, DLS_INTERFACE_SEARCH_OBJECTS))
		task = dls_task_search_new(invocation, object,
					   parameters, &error);
	else if (!strcmp(method, DLS_INTERFACE_SEARCH_OBJECTS_EX))
		task = dls_task_search_ex_new(invocation, object,
					      parameters, &error);
	else if (!strcmp(method, DLS_INTERFACE_UPLOAD))
		task = dls_task_upload_new(invocation, object,
					   parameters, &error);
	else if (!strcmp(method, DLS_INTERFACE_CREATE_CONTAINER))
		task = dls_task_create_container_new_generic(invocation,
						DLS_TASK_CREATE_CONTAINER,
						object, parameters, &error);
	else if (!strcmp(method, DLS_INTERFACE_GET_COMPATIBLE_RESOURCE))
		task = dls_task_get_resource_new(invocation, object,
						 parameters, &error);
	else if (!strcmp(method, DLS_INTERFACE_CREATE_REFERENCE))
		task = dls_task_create_reference_new(invocation,
						    DLS_TASK_CREATE_REFERENCE,
						    object, parameters, &error);
	else
		goto finished;

	if (!task) {
		g_context.connector->return_error(invocation, error);
		g_error_free(error);

		goto finished;
	}

	prv_add_task(task, sender, task->target.device->path);

finished:

	return;
}

static void prv_props_method_call(dleyna_connector_id_t conn,
				  const gchar *sender,
				  const gchar *object,
				  const gchar *interface,
				  const gchar *method,
				  GVariant *parameters,
				  dleyna_connector_msg_id_t invocation)
{
	dls_task_t *task;
	GError *error = NULL;

	if (!strcmp(method, DLS_INTERFACE_GET_ALL))
		task = dls_task_get_props_new(invocation, object,
					      parameters, &error);
	else if (!strcmp(method, DLS_INTERFACE_GET))
		task = dls_task_get_prop_new(invocation, object,
					     parameters, &error);
	else
		goto finished;

	if (!task) {
		g_context.connector->return_error(invocation, error);
		g_error_free(error);

		goto finished;
	}

	prv_add_task(task, sender, task->target.device->path);

finished:

	return;
}

static void prv_device_method_call(dleyna_connector_id_t conn,
				   const gchar *sender,
				   const gchar *object,
				   const gchar *interface,
				   const gchar *method,
				   GVariant *parameters,
				   dleyna_connector_msg_id_t invocation)
{
	dls_task_t *task;
	GError *error = NULL;
	const gchar *device_id;
	const dleyna_task_queue_key_t *queue_id;

	if (!strcmp(method, DLS_INTERFACE_UPLOAD_TO_ANY)) {
		task = dls_task_upload_to_any_new(invocation,
						  object, parameters, &error);
	} else if (!strcmp(method, DLS_INTERFACE_CREATE_CONTAINER_IN_ANY)) {
		task = dls_task_create_container_new_generic(
					invocation,
					DLS_TASK_CREATE_CONTAINER_IN_ANY,
					object, parameters, &error);
	} else if (!strcmp(method, DLS_INTERFACE_GET_UPLOAD_STATUS)) {
		task = dls_task_get_upload_status_new(invocation,
						      object, parameters,
						      &error);
	} else if (!strcmp(method, DLS_INTERFACE_GET_UPLOAD_IDS)) {
		task = dls_task_get_upload_ids_new(invocation, object,
						   &error);
	} else if (!strcmp(method, DLS_INTERFACE_CANCEL_UPLOAD)) {
		task = dls_task_cancel_upload_new(invocation, object,
						  parameters, &error);
	} 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_WAKE)) {
		task = dls_task_wake_new(invocation, object, &error);
	} else if (!strcmp(method, DLS_INTERFACE_CANCEL)) {
		task = NULL;

		device_id = prv_get_device_id(object, &error);
		if (!device_id)
			goto on_error;

		queue_id = dleyna_task_processor_lookup_queue(
							g_context.processor,
							sender,
							device_id);
		if (queue_id)
			dleyna_task_processor_cancel_queue(queue_id);

		g_context.connector->return_response(invocation, NULL);

		goto finished;
	} else {
		goto finished;
	}

on_error:

	if (!task) {
		g_context.connector->return_error(invocation, error);
		g_error_free(error);

		goto finished;
	}

	prv_add_task(task, sender, task->target.device->path);

finished:

	return;
}

static void prv_found_media_server(const gchar *path, void *user_data)
{
	(void) g_context.connector->notify(g_context.connection,
					   DLEYNA_SERVER_OBJECT,
					   DLEYNA_SERVER_INTERFACE_MANAGER,
					   DLS_INTERFACE_FOUND_SERVER,
					   g_variant_new("(o)", path),
					   NULL);
}

static void prv_lost_media_server(const gchar *path, void *user_data)
{
	(void) g_context.connector->notify(g_context.connection,
					   DLEYNA_SERVER_OBJECT,
					   DLEYNA_SERVER_INTERFACE_MANAGER,
					   DLS_INTERFACE_LOST_SERVER,
					   g_variant_new("(o)", path),
					   NULL);

	dleyna_task_processor_remove_queues_for_sink(g_context.processor, path);
}

static void prv_unregister_client(gpointer user_data)
{
	dls_client_t *client = user_data;

	if (client) {
		g_free(client->protocol_info);
		g_free(client);
	}
}

dls_upnp_t *dls_server_get_upnp(void)
{
	return g_context.upnp;
}

static void prv_white_list_init(void)
{
	gboolean enabled;
	GVariant *entries;
	dleyna_white_list_t *wl;

	DLEYNA_LOG_DEBUG("Enter");

	enabled = dleyna_settings_is_white_list_enabled(g_context.settings);
	entries = dleyna_settings_white_list_entries(g_context.settings);

	wl = dls_manager_get_white_list(g_context.manager);

	dleyna_white_list_enable(wl, enabled);
	dleyna_white_list_add_entries(wl, entries);

	DLEYNA_LOG_DEBUG("Exit");
}

static gboolean prv_control_point_start_service(
					dleyna_connector_id_t connection)
{
	gboolean retval = TRUE;
	uint i;

	g_context.connection = connection;

	for (i = 0; i < DLS_MANAGER_INTERFACE_INFO_MAX; i++)
		g_context.dls_id[i] = g_context.connector->publish_object(
						connection,
						DLEYNA_SERVER_OBJECT,
						TRUE,
						g_manager_interfaces[i],
						g_root_vtables + i);

	if (g_context.dls_id[DLS_MANAGER_INTERFACE_MANAGER]) {
		g_context.upnp = dls_upnp_new(connection,
					      dleyna_settings_port(g_context.settings),
					      g_server_vtables,
					      prv_found_media_server,
					      prv_lost_media_server,
					      NULL);

		g_context.manager = dls_manager_new(connection,
						dls_upnp_get_context_manager(
							g_context.upnp));

		prv_white_list_init();
	} else {
		retval = FALSE;
	}

	return retval;
}

static void prv_control_point_stop_service(void)
{
	uint i;

	if (g_context.manager) {
		dls_manager_delete(g_context.manager);
		g_context.manager = NULL;
	}

	if (g_context.upnp) {
		dls_upnp_unsubscribe(g_context.upnp);
		dls_upnp_delete(g_context.upnp);
		g_context.upnp = NULL;
	}

	if (g_context.connection) {
		for (i = 0; i < DLS_MANAGER_INTERFACE_INFO_MAX; i++)
			if (g_context.dls_id[i])
				g_context.connector->unpublish_object(
							g_context.connection,
							g_context.dls_id[i]);
	}
}

static void prv_control_point_initialize(const dleyna_connector_t *connector,
					 dleyna_task_processor_t *processor,
					 dleyna_settings_t *settings)
{
	memset(&g_context, 0, sizeof(g_context));

	g_context.connector = connector;
	g_context.processor = processor;
	g_context.settings = settings;

	g_context.connector->set_client_lost_cb(prv_lost_client);

	g_context.watchers = g_hash_table_new_full(g_str_hash, g_str_equal,
						 g_free, prv_unregister_client);

	g_set_prgname(DLS_PRG_NAME);
}

static void prv_control_point_free(void)
{
	if (g_context.watchers)
		g_hash_table_unref(g_context.watchers);
}

static const gchar *prv_control_point_server_name(void)
{
	return DLEYNA_SERVER_NAME;
}

static const gchar *prv_control_point_server_introspection(void)
{
	return g_server_introspection;
}

static const gchar *prv_control_point_root_introspection(void)
{
	return g_root_introspection;
}

static const gchar *prv_control_point_get_version(void)
{
	return VERSION;
}

static const dleyna_control_point_t g_control_point = {
	prv_control_point_initialize,
	prv_control_point_free,
	prv_control_point_server_name,
	prv_control_point_server_introspection,
	prv_control_point_root_introspection,
	prv_control_point_start_service,
	prv_control_point_stop_service,
	prv_control_point_get_version
};

const dleyna_control_point_t *dleyna_control_point_get_server(void)
{
	return &g_control_point;
}
