/* * Copyright (C) 2007 Zeeshan Ali (Khattak) * Copyright (C) 2006, 2007 OpenedHand Ltd. * * Author: Zeeshan Ali (Khattak) * Jorn Baayen * * SPDX-License-Identifier: LGPL-2.1-or-later * */ /** * GUPnPResourceFactory: * * Associating custom Services, Devices, ServiceProxies and DeviceProxies with UPnP types. * * #GUPnPResourceFactory objects are used by [class@GUPnP.ControlPoint], * [class@GUPnP.DeviceProxy] and [class@GUPnP.Device] to create resource proxy and resource * objects. * * Register UPnP type - [alias@GLib.Type] pairs to have resource or resource proxy * objects created with the specified #GType whenever an object for a resource * of the specified UPnP type is requested. The #GType needs * to be derived from the relevant resource or resource proxy type (e.g. * a device proxy type needs to be derived from [class@GUPnP.DeviceProxy]). */ #define G_LOG_DOMAIN "gupnp-resource-factory" #include #include #include "gupnp-device-info-private.h" #include "gupnp-resource-factory-private.h" #include "gupnp-root-device.h" struct _GUPnPResourceFactoryPrivate { GHashTable *resource_type_hash; GHashTable *proxy_type_hash; }; typedef struct _GUPnPResourceFactoryPrivate GUPnPResourceFactoryPrivate; G_DEFINE_TYPE_WITH_PRIVATE (GUPnPResourceFactory, gupnp_resource_factory, G_TYPE_OBJECT) static void gupnp_resource_factory_init (GUPnPResourceFactory *factory) { GUPnPResourceFactoryPrivate *priv; priv = gupnp_resource_factory_get_instance_private (factory); priv->resource_type_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); priv->proxy_type_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } static void gupnp_resource_factory_finalize (GObject *object) { GUPnPResourceFactory *self; GObjectClass *object_class; GUPnPResourceFactoryPrivate *priv; self = GUPNP_RESOURCE_FACTORY (object); priv = gupnp_resource_factory_get_instance_private (self); if (priv->resource_type_hash) { g_hash_table_destroy (priv->resource_type_hash); priv->resource_type_hash = NULL; } if (priv->proxy_type_hash) { g_hash_table_destroy (priv->proxy_type_hash); priv->proxy_type_hash = NULL; } object_class = G_OBJECT_CLASS (gupnp_resource_factory_parent_class); object_class->finalize (object); } static void gupnp_resource_factory_class_init (GUPnPResourceFactoryClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = gupnp_resource_factory_finalize; } /** * gupnp_resource_factory_new: * * Create a new #GUPnPResourceFactory object. * * Return value: A #GUPnPResourceFactory object. **/ GUPnPResourceFactory * gupnp_resource_factory_new (void) { return g_object_new (GUPNP_TYPE_RESOURCE_FACTORY, NULL); } /** * gupnp_resource_factory_get_default: * * Get the default singleton #GUPnPResourceFactory object. * * Returns: (transfer none): A @GUPnPResourceFactory object. **/ GUPnPResourceFactory * gupnp_resource_factory_get_default (void) { static GUPnPResourceFactory *default_factory = NULL; if (g_once_init_enter (&default_factory)) { GUPnPResourceFactory *factory = g_object_new (GUPNP_TYPE_RESOURCE_FACTORY, NULL); g_once_init_leave (&default_factory, factory); } return default_factory; } static GType lookup_type_with_fallback (GHashTable *resource_types, const char *requested_type, const char *child_node, xmlNode *element, GType fallback) { GType type = fallback; char *upnp_type = NULL; if (requested_type == NULL) { g_debug ("Looking up type from XML"); upnp_type = xml_util_get_child_element_content_glib (element, child_node); } else { g_debug ("Using passed type %s", requested_type); upnp_type = g_strdup (requested_type); } if (upnp_type != NULL) { g_debug ("Found type from XML: %s", upnp_type); gpointer value; char *needle = NULL; value = g_hash_table_lookup (resource_types, upnp_type); if (value == NULL) { g_debug ("Trying to use version-less type..."); needle = g_strrstr (upnp_type, ":"); if (needle != NULL) { *needle = '\0'; g_debug ("Version-less type is %s", upnp_type); value = g_hash_table_lookup (resource_types, upnp_type); } } if (value != NULL) { type = GPOINTER_TO_SIZE (value); } g_debug ("Will return type %s for UPnP type %s", g_type_name (type), upnp_type); g_free (upnp_type); } else { g_debug ("Will return fall-back type %s", upnp_type); } return type; } /** * gupnp_resource_factory_create_device_proxy: * @factory: A #GUPnPResourceFactory * @context: A #GUPnPContext * @doc: A #GUPnPXMLDoc * @element: The #xmlNode ponting to the right device element * @udn: The UDN of the device to create a proxy for * @location: The location of the device description file * @url_base: The URL base for this device, or %NULL if none * * Create a #GUPnPDeviceProxy for the device with element @element, as * read from the device description file specified by @location. * * Return value:(nullable)(transfer full): A new #GUPnPDeviceProxy. **/ GUPnPDeviceProxy * gupnp_resource_factory_create_device_proxy (GUPnPResourceFactory *factory, GUPnPContext *context, GUPnPXMLDoc *doc, xmlNode *element, const char *udn, const char *location, const GUri *url_base) { GUPnPDeviceProxy *proxy; GType proxy_type; GUPnPResourceFactoryPrivate *priv; g_return_val_if_fail (GUPNP_IS_RESOURCE_FACTORY (factory), NULL); g_return_val_if_fail (GUPNP_IS_CONTEXT (context), NULL); g_return_val_if_fail (GUPNP_IS_XML_DOC (doc), NULL); g_return_val_if_fail (element != NULL, NULL); g_return_val_if_fail (location != NULL, NULL); g_return_val_if_fail (url_base != NULL, NULL); priv = gupnp_resource_factory_get_instance_private (factory); proxy_type = lookup_type_with_fallback (priv->proxy_type_hash, NULL, "deviceType", element, GUPNP_TYPE_DEVICE_PROXY); proxy = g_object_new (proxy_type, "resource-factory", factory, "context", context, "location", location, "udn", udn, "url-base", url_base, "document", doc, "element", element, NULL); return proxy; } /** * gupnp_resource_factory_create_service_proxy: * @factory: A #GUPnPResourceFactory * @context: A #GUPnPContext * @doc: A #GUPnPXMLDoc * @element: The #xmlNode pointing to the right service element * @location: The location of the service description file * @udn: The UDN of the device the service is contained in * @service_type: (nullable): The service type, or %NULL to use service * type from @element * @url_base: The URL base for this service, or %NULL if none * * Create a #GUPnPServiceProxy for the service with element @element, as * read from the service description file specified by @location. * * Return value:(nullable)(transfer full): A new #GUPnPServiceProxy. **/ GUPnPServiceProxy * gupnp_resource_factory_create_service_proxy (GUPnPResourceFactory *factory, GUPnPContext *context, GUPnPXMLDoc *doc, xmlNode *element, const char *udn, const char *service_type, const char *location, const GUri *url_base) { GUPnPServiceProxy *proxy; GType proxy_type; GUPnPResourceFactoryPrivate *priv; g_return_val_if_fail (GUPNP_IS_RESOURCE_FACTORY (factory), NULL); g_return_val_if_fail (GUPNP_IS_CONTEXT (context), NULL); g_return_val_if_fail (GUPNP_IS_XML_DOC (doc), NULL); g_return_val_if_fail (element != NULL, NULL); g_return_val_if_fail (location != NULL, NULL); g_return_val_if_fail (url_base != NULL, NULL); priv = gupnp_resource_factory_get_instance_private (factory); proxy_type = lookup_type_with_fallback (priv->proxy_type_hash, service_type, "serviceType", element, GUPNP_TYPE_SERVICE_PROXY); proxy = g_object_new (proxy_type, "context", context, "location", location, "udn", udn, "service-type", service_type, "url-base", url_base, "document", doc, "element", element, NULL); return proxy; } /** * gupnp_resource_factory_create_device: * @factory: A #GUPnPResourceFactory * @context: A #GUPnPContext * @root_device: The #GUPnPRootDevice * @element: The #xmlNode ponting to the right device element * @udn: The UDN of the device to create a device for * @location: The location of the device description file * @url_base: The URL base for this device * * Create a #GUPnPDevice for the device with element @element, as * read from the device description file specified by @location. * * Return value: (nullable)(transfer full): A new #GUPnPDevice. **/ GUPnPDevice * gupnp_resource_factory_create_device (GUPnPResourceFactory *factory, GUPnPContext *context, GUPnPDevice *root_device, xmlNode *element, const char *udn, const char *location, const GUri *url_base) { GUPnPDevice *device; GType device_type; GUPnPResourceFactoryPrivate *priv; g_return_val_if_fail (GUPNP_IS_RESOURCE_FACTORY (factory), NULL); g_return_val_if_fail (GUPNP_IS_CONTEXT (context), NULL); g_return_val_if_fail (GUPNP_IS_ROOT_DEVICE (root_device), NULL); g_return_val_if_fail (element != NULL, NULL); g_return_val_if_fail (url_base != NULL, NULL); priv = gupnp_resource_factory_get_instance_private (factory); device_type = lookup_type_with_fallback (priv->resource_type_hash, NULL, "deviceType", element, GUPNP_TYPE_DEVICE); GUPnPXMLDoc *doc = _gupnp_device_info_get_document ( GUPNP_DEVICE_INFO (root_device)); device = g_object_new (device_type, "resource-factory", factory, "context", context, "root-device", root_device, "location", location, "udn", udn, "url-base", url_base, "document", doc, "element", element, NULL); return device; } /** * gupnp_resource_factory_create_service: * @factory: A #GUPnPResourceFactory * @context: A #GUPnPContext * @root_device: The #GUPnPRootDevice * @element: The #xmlNode ponting to the right service element * @udn: The UDN of the device the service is contained in * @location: The location of the service description file * @url_base: The URL base for this service * * Create a #GUPnPService for the service with element @element, as * read from the service description file specified by @location. * * Return value: (nullable)(transfer full): A new #GUPnPService. **/ GUPnPService * gupnp_resource_factory_create_service (GUPnPResourceFactory *factory, GUPnPContext *context, GUPnPDevice *root_device, xmlNode *element, const char *udn, const char *location, const GUri *url_base) { GUPnPService *service; GType service_type; GUPnPResourceFactoryPrivate *priv; g_return_val_if_fail (GUPNP_IS_RESOURCE_FACTORY (factory), NULL); g_return_val_if_fail (GUPNP_IS_CONTEXT (context), NULL); g_return_val_if_fail (GUPNP_IS_ROOT_DEVICE (root_device), NULL); g_return_val_if_fail (element != NULL, NULL); g_return_val_if_fail (location != NULL, NULL); g_return_val_if_fail (url_base != NULL, NULL); priv = gupnp_resource_factory_get_instance_private (factory); service_type = lookup_type_with_fallback (priv->resource_type_hash, NULL, "serviceType", element, GUPNP_TYPE_SERVICE); GUPnPXMLDoc *doc = _gupnp_device_info_get_document ( GUPNP_DEVICE_INFO (root_device)); service = g_object_new (service_type, "context", context, "root-device", root_device, "location", location, "udn", udn, "url-base", url_base, "document", doc, "element", element, NULL); return service; } /** * gupnp_resource_factory_register_resource_type: * @factory: A #GUPnPResourceFactory. * @upnp_type: The UPnP type name of the resource. * @type: The requested GType assignment for the resource. * * Registers the GType @type for the resource of UPnP type @upnp_type. After * this call, the factory @factory will create object of GType @type each time * it is asked to create a resource object for UPnP type @upnp_type. * * You can either register a type for a concrete version of a device or service * such as urn:schemas-upnp-org:service:AVTransport:2 or version-independently, * urn:schemas-upnp-org:service:AVTransport. If you register for an explicit * version of a service, it will be an exact match. * * Note: GType @type must be a derived type of #GUPNP_TYPE_DEVICE if resource is * a device or #GUPNP_TYPE_SERVICE if its a service. **/ void gupnp_resource_factory_register_resource_type (GUPnPResourceFactory *factory, const char *upnp_type, GType type) { GUPnPResourceFactoryPrivate *priv; priv = gupnp_resource_factory_get_instance_private (factory); g_hash_table_insert (priv->resource_type_hash, g_strdup (upnp_type), GSIZE_TO_POINTER (type)); } /** * gupnp_resource_factory_unregister_resource_type: * @factory: A #GUPnPResourceFactory. * @upnp_type: The UPnP type name of the resource. * * Unregisters the GType assignment for the resource of UPnP type @upnp_type. * * Return value: %TRUE if GType assignment was removed successfully, %FALSE * otherwise. **/ gboolean gupnp_resource_factory_unregister_resource_type (GUPnPResourceFactory *factory, const char *upnp_type) { GUPnPResourceFactoryPrivate *priv; priv = gupnp_resource_factory_get_instance_private (factory); return g_hash_table_remove (priv->resource_type_hash, upnp_type); } /** * gupnp_resource_factory_register_resource_proxy_type: * @factory: A #GUPnPResourceFactory. * @upnp_type: The UPnP type name of the resource. * @type: The requested GType assignment for the resource proxy. * * Registers the GType @type for the proxy of resource of UPnP type @upnp_type. * After this call, the factory @factory will create object of GType @type each * time it is asked to create a resource proxy object for UPnP type @upnp_type. * * Note: GType @type must be a derived type of #GUPNP_TYPE_DEVICE_PROXY if * resource is a device or #GUPNP_TYPE_SERVICE_PROXY if its a service. **/ void gupnp_resource_factory_register_resource_proxy_type (GUPnPResourceFactory *factory, const char *upnp_type, GType type) { GUPnPResourceFactoryPrivate *priv; priv = gupnp_resource_factory_get_instance_private (factory); g_hash_table_insert (priv->proxy_type_hash, g_strdup (upnp_type), GSIZE_TO_POINTER (type)); } /** * gupnp_resource_factory_unregister_resource_proxy_type: * @factory: A #GUPnPResourceFactory. * @upnp_type: The UPnP type name of the resource. * * Unregisters the GType assignment for the proxy of resource of UPnP type * @upnp_type. * * Return value: %TRUE if GType assignment was removed successfully, %FALSE * otherwise. **/ gboolean gupnp_resource_factory_unregister_resource_proxy_type (GUPnPResourceFactory *factory, const char *upnp_type) { GUPnPResourceFactoryPrivate *priv; priv = gupnp_resource_factory_get_instance_private (factory); return g_hash_table_remove (priv->proxy_type_hash, upnp_type); }