📄 gupnp-service.c
字号:
/* * Copyright (C) 2007, 2008 OpenedHand Ltd. * * Author: Jorn Baayen <jorn@openedhand.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. *//** * SECTION:gupnp-service * @short_description: Class for service implementations. * * #GUPnPService allows for handling incoming actions and state variable * notification. #GUPnPService implements the #GUPnPServiceInfo interface. */#include <gobject/gvaluecollector.h>#include <gmodule.h>#include <libsoup/soup-date.h>#include <uuid/uuid.h>#include <string.h>#include "gupnp-service.h"#include "gupnp-root-device.h"#include "gupnp-context-private.h"#include "gupnp-marshal.h"#include "gupnp-error.h"#include "http-headers.h"#include "gena-protocol.h"#include "xml-util.h"#include "gvalue-util.h"G_DEFINE_TYPE (GUPnPService, gupnp_service, GUPNP_TYPE_SERVICE_INFO);struct _GUPnPServicePrivate { GUPnPRootDevice *root_device; guint notify_available_id; GHashTable *subscriptions; GList *state_variables; GQueue *notify_queue; gboolean notify_frozen;};enum { PROP_0, PROP_ROOT_DEVICE};enum { ACTION_INVOKED, QUERY_VARIABLE, NOTIFY_FAILED, LAST_SIGNAL};static guint signals[LAST_SIGNAL];static char *create_property_set (GQueue *queue);static voidnotify_subscriber (gpointer key, gpointer value, gpointer user_data);typedef struct { GUPnPService *service; GList *callbacks; char *sid; int seq; guint timeout_id; GList *pending_messages; /* Pending SoupMessages from this subscription */} SubscriptionData;static voidsubscription_data_free (SubscriptionData *data){ GUPnPContext *context; SoupSession *session; context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (data->service)); session = _gupnp_context_get_session (context); /* Cancel pending messages */ while (data->pending_messages) { SoupMessage *msg; msg = data->pending_messages->data; soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED); data->pending_messages = g_list_delete_link (data->pending_messages, data->pending_messages); } /* Further cleanup */ while (data->callbacks) { g_free (data->callbacks->data); data->callbacks = g_list_delete_link (data->callbacks, data->callbacks); } g_free (data->sid); if (data->timeout_id) g_source_remove (data->timeout_id); g_slice_free (SubscriptionData, data);}typedef struct { char *variable; GValue value;} NotifyData;static voidnotify_data_free (NotifyData *data){ g_free (data->variable); g_value_unset (&data->value); g_slice_free (NotifyData, data);}struct _GUPnPServiceAction { const char *name; SoupMessage *msg; xmlNode *node; GString *response_str;};GTypegupnp_service_action_get_type (void){ static GType our_type = 0; if (our_type == 0) our_type = g_pointer_type_register_static ("GUPnPServiceAction"); return our_type;}/** * gupnp_service_action_get_name * @action: A #GUPnPServiceAction * * Get the name of @action. * * Return value: The name of @action **/const char *gupnp_service_action_get_name (GUPnPServiceAction *action){ g_return_val_if_fail (action != NULL, NULL); return action->name;}/** * gupnp_service_action_get_locales * @action: A #GUPnPServiceAction * * Get an ordered (preferred first) #GList of locales preferred by * the client. Free list and elements after use. * * Return value: A #GList of #char* locale names. **/GList *gupnp_service_action_get_locales (GUPnPServiceAction *action){ g_return_val_if_fail (action != NULL, NULL); return http_request_get_accept_locales (action->msg);}/** * gupnp_service_action_get * @action: A #GUPnPServiceAction * @Varargs: tuples of argument name, argument type, and argument value * location, terminated with %NULL. * * Retrieves the specified action arguments. **/voidgupnp_service_action_get (GUPnPServiceAction *action, ...){ va_list var_args; g_return_if_fail (action != NULL); va_start (var_args, action); gupnp_service_action_get_valist (action, var_args); va_end (var_args);}/** * gupnp_service_action_get_valist * @action: A #GUPnPServiceAction * @var_args: va_list of tuples of argument name, argument type, and argument * value location. * * See gupnp_service_action_get(); this version takes a va_list for * use by language bindings. **/voidgupnp_service_action_get_valist (GUPnPServiceAction *action, va_list var_args){ const char *arg_name; GType arg_type; GValue value = {0, }; char *copy_error; g_return_if_fail (action != NULL); copy_error = NULL; arg_name = va_arg (var_args, const char *); while (arg_name) { arg_type = va_arg (var_args, GType); g_value_init (&value, arg_type); gupnp_service_action_get_value (action, arg_name, &value); G_VALUE_LCOPY (&value, var_args, 0, ©_error); g_value_unset (&value); if (copy_error) { g_warning ("Error lcopying value: %s\n", copy_error); g_free (copy_error); } arg_name = va_arg (var_args, const char *); }}/** * gupnp_service_action_get_value * @action: A #GUPnPServiceAction * @argument: The name of the argument to retrieve * @value: The #GValue to store the value of the argument, initialized * to the correct type. * * Retrieves the value of @argument into @value. **/voidgupnp_service_action_get_value (GUPnPServiceAction *action, const char *argument, GValue *value){ xmlNode *node; gboolean found; g_return_if_fail (action != NULL); g_return_if_fail (argument != NULL); g_return_if_fail (value != NULL); found = FALSE; for (node = action->node->children; node; node = node->next) { if (strcmp ((char *) node->name, argument) != 0) continue; found = gvalue_util_set_value_from_xml_node (value, node); break; } if (!found) g_warning ("Failed to retreive '%s' argument of '%s' action", argument, action->name);}/** * gupnp_service_action_set * @action: A #GUPnPServiceAction * @Varargs: tuples of return value name, return value type, and * actual return value, terminated with %NULL. * * Sets the specified action return values. **/voidgupnp_service_action_set (GUPnPServiceAction *action, ...){ va_list var_args; g_return_if_fail (action != NULL); va_start (var_args, action); gupnp_service_action_set_valist (action, var_args); va_end (var_args);}/** * gupnp_service_action_set_valist * @action: A #GUPnPServiceAction * @var_args: va_list of tuples of return value name, return value type, and * actual return value. * * See gupnp_service_action_set(); this version takes a va_list for * use by language bindings. **/voidgupnp_service_action_set_valist (GUPnPServiceAction *action, va_list var_args){ const char *arg_name; GType arg_type; GValue value = {0, }; char *collect_error; g_return_if_fail (action != NULL); collect_error = NULL; arg_name = va_arg (var_args, const char *); while (arg_name) { arg_type = va_arg (var_args, GType); g_value_init (&value, arg_type); G_VALUE_COLLECT (&value, var_args, 0, &collect_error); if (!collect_error) { gupnp_service_action_set_value (action, arg_name, &value); g_value_unset (&value); } else { g_warning ("Error collecting value: %s\n", collect_error); g_free (collect_error); } arg_name = va_arg (var_args, const char *); }}/** * gupnp_service_action_set_value * @action: A #GUPnPServiceAction * @argument: The name of the return value to retrieve * @value: The #GValue to store the return value * * Sets the value of @argument to @value. **/voidgupnp_service_action_set_value (GUPnPServiceAction *action, const char *argument, const GValue *value){ g_return_if_fail (action != NULL); g_return_if_fail (argument != NULL); g_return_if_fail (value != NULL); if (action->msg->status_code == SOUP_STATUS_INTERNAL_SERVER_ERROR) { g_warning ("Calling gupnp_service_action_set_value() after " "having called gupnp_service_action_return_error() " "is not allowed."); return; } /* Append to response */ xml_util_start_element (action->response_str, argument); gvalue_util_value_append_to_xml_string (value, action->response_str); xml_util_end_element (action->response_str, argument);}/** * gupnp_service_action_return * @action: A #GUPnPServiceAction * * Return succesfully. **/voidgupnp_service_action_return (GUPnPServiceAction *action){ g_return_if_fail (action != NULL); soup_message_set_status (action->msg, SOUP_STATUS_OK);}/** * gupnp_service_action_return_error * @action: A #GUPnPServiceAction * @error_code: The error code * @error_description: The error description, or %NULL if @error_code is * one of #GUPNP_CONTROL_ERROR_INVALID_ACTION, * #GUPNP_CONTROL_ERROR_INVALID_ARGS, #GUPNP_CONTROL_ERROR_OUT_OF_SYNC or * #GUPNP_CONTROL_ERROR_ACTION_FAILED, in which case a description is * provided automatically. * * Return @error_code. **/voidgupnp_service_action_return_error (GUPnPServiceAction *action, guint error_code, const char *error_description){ g_return_if_fail (action != NULL); switch (error_code) { case GUPNP_CONTROL_ERROR_INVALID_ACTION: if (error_description == NULL) error_description = "Invalid Action"; break; case GUPNP_CONTROL_ERROR_INVALID_ARGS: if (error_description == NULL) error_description = "Invalid Args"; break; case GUPNP_CONTROL_ERROR_OUT_OF_SYNC: if (error_description == NULL) error_description = "Out of Sync"; break; case GUPNP_CONTROL_ERROR_ACTION_FAILED: if (error_description == NULL) error_description = "Action Failed"; break; default: g_return_if_fail (error_description != NULL); break; } /* Replace response_str with a SOAP Fault */ g_string_erase (action->response_str, 0, -1); xml_util_start_element (action->response_str, "s:Fault"); xml_util_start_element (action->response_str, "faultcode");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -