📄 gupnp-service.c
字号:
} else { g_warning ("Error collecting value: %s\n", collect_error); g_free (collect_error); } var_name = va_arg (var_args, const char *); }}/* Received notify response. */static voidnotify_got_response (SoupSession *session, SoupMessage *msg, gpointer user_data){ SubscriptionData *data; /* Cancelled? */ if (msg->status_code == SOUP_STATUS_CANCELLED) return; data = user_data; /* Remove from pending messages list */ data->pending_messages = g_list_remove (data->pending_messages, msg); if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { /* Success: reset callbacks pointer */ data->callbacks = g_list_first (data->callbacks); } else if (msg->status_code == SOUP_STATUS_PRECONDITION_FAILED) { /* Precondition failed: Cancel subscription */ g_hash_table_remove (data->service->priv->subscriptions, data->sid); } else { /* Other failure: Try next callback or signal failure. */ if (data->callbacks->next) { SoupURI *uri; GUPnPContext *context; SoupSession *session; /* Call next callback */ data->callbacks = data->callbacks->next; uri = soup_uri_new (data->callbacks->data); soup_message_set_uri (msg, uri); soup_uri_free (uri); /* And re-queue */ data->pending_messages = g_list_prepend (data->pending_messages, msg); context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (data->service)); session = _gupnp_context_get_session (context); soup_session_requeue_message (session, msg); } else { /* Emit 'notify-failed' signal */ GError *error; error = g_error_new (GUPNP_EVENTING_ERROR, GUPNP_EVENTING_ERROR_NOTIFY_FAILED, msg->reason_phrase); g_signal_emit (data->service, signals[NOTIFY_FAILED], 0, data->callbacks, error); g_error_free (error); /* Reset callbacks pointer */ data->callbacks = g_list_first (data->callbacks); } }}/* Send notification @user_data to subscriber @value */static voidnotify_subscriber (gpointer key, gpointer value, gpointer user_data){ SubscriptionData *data; const char *property_set; char *tmp; SoupMessage *msg; GUPnPContext *context; SoupSession *session; data = value; property_set = user_data; /* Create message */ msg = soup_message_new (GENA_METHOD_NOTIFY, data->callbacks->data); if (!msg) { g_warning ("Invalid callback URL: %s", (char *) data->callbacks->data); return; } soup_message_headers_append (msg->request_headers, "NT", "upnp:event"); soup_message_headers_append (msg->request_headers, "NTS", "upnp:propchange"); soup_message_headers_append (msg->request_headers, "SID", data->sid); tmp = g_strdup_printf ("%d", data->seq); soup_message_headers_append (msg->request_headers, "SEQ", tmp); g_free (tmp); /* Handle overflow */ if (data->seq < G_MAXINT32) data->seq++; else data->seq = 1; /* Add body */ soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE, g_strdup (property_set), strlen (property_set)); /* Queue */ data->pending_messages = g_list_prepend (data->pending_messages, msg); context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (data->service)); session = _gupnp_context_get_session (context); soup_session_queue_message (session, msg, notify_got_response, data);}/* Create a property set from @queue */static char *create_property_set (GQueue *queue){ NotifyData *data; GString *str; /* Compose property set */ str = xml_util_new_string (); g_string_append (str, "<?xml version=\"1.0\"?>" "<e:propertyset xmlns:e=" "\"urn:schemas-upnp-org:event-1-0\">" "<e:property>"); /* Add variables */ while ((data = g_queue_pop_head (queue))) { xml_util_start_element (str, data->variable); gvalue_util_value_append_to_xml_string (&data->value, str); xml_util_end_element (str, data->variable); /* Cleanup */ notify_data_free (data); } g_string_append (str, "</e:property>" "</e:propertyset>"); /* Cleanup & return */ return g_string_free (str, FALSE);}/* Flush all queued notifications */static voidflush_notifications (GUPnPService *service){ char *mem; /* Create property set */ mem = create_property_set (service->priv->notify_queue); /* And send it off */ g_hash_table_foreach (service->priv->subscriptions, notify_subscriber, mem); /* Cleanup */ g_free (mem);}/** * gupnp_service_notify_value * @service: A #GUPnPService * @variable: The name of the variable to notify * @value: The value of the variable * * Notifies listening clients that @variable has changed to @value. **/voidgupnp_service_notify_value (GUPnPService *service, const char *variable, const GValue *value){ g_return_if_fail (GUPNP_IS_SERVICE (service)); g_return_if_fail (variable != NULL); g_return_if_fail (G_IS_VALUE (value)); /* Queue */ NotifyData *data; data = g_slice_new0 (NotifyData); data->variable = g_strdup (variable); g_value_init (&data->value, G_VALUE_TYPE (value)); g_value_copy (value, &data->value); g_queue_push_tail (service->priv->notify_queue, data); /* And flush, if not frozen */ if (!service->priv->notify_frozen) flush_notifications (service);}/** * gupnp_service_freeze_notify * @service: A #GUPnPService * * Causes new notifications to be queued up until gupnp_service_thaw_notify() * is called. **/voidgupnp_service_freeze_notify (GUPnPService *service){ g_return_if_fail (GUPNP_IS_SERVICE (service)); service->priv->notify_frozen = TRUE;}/** * gupnp_service_thaw_notify * @service: A #GUPnPService * * Sends out any pending notifications, and stops queuing of new ones. **/voidgupnp_service_thaw_notify (GUPnPService *service){ g_return_if_fail (GUPNP_IS_SERVICE (service)); service->priv->notify_frozen = FALSE; if (g_queue_get_length (service->priv->notify_queue) == 0) return; /* Empty notify queue */ flush_notifications (service);}/* Convert a CamelCase string to a lowercase string with underscores */static char *strip_camel_case (char *camel_str){ char *stripped; int i, j; /* Keep enough space for underscores */ stripped = g_malloc (strlen (camel_str) * 2); for (i = 0, j = 0; i <= strlen (camel_str); i++) { /* Convert every upper case letter to lower case and unless * it's the first character, the last charachter, in the * middle of an abbreviation or there is already an underscore * before it, add an underscore before it */ if (g_ascii_isupper (camel_str[i])) { if (i != 0 && camel_str[i + 1] != '\0' && camel_str[i - 1] != '_' && !g_ascii_isupper (camel_str[i - 1])) { stripped[j++] = '_'; } stripped[j++] = g_ascii_tolower (camel_str[i]); } else stripped[j++] = camel_str[i]; } return stripped;}static GCallbackfind_callback_by_name (GModule *module, const char *name){ GCallback callback; char *full_name; /* First try with 'on_' prefix */ full_name = g_strjoin ("_", "on", name, NULL); if (!g_module_symbol (module, full_name, (gpointer) &callback)) { g_free (full_name); /* Now try with '_cb' postfix */ full_name = g_strjoin ("_", name, "cb", NULL); g_module_symbol (module, full_name, (gpointer) &callback); } g_free (full_name); return callback;}/* Use the strings from @name_list as details to @signal_name, and connect * callbacks with names based on these same strings to @signal_name::string. */static voidconnect_names_to_signal_handlers (GUPnPService *service, GModule *module, const GList *name_list, const char *signal_name, const char *callback_prefix, gpointer user_data){ const GList *name_node; for (name_node = name_list; name_node; name_node = name_node->next) { GCallback callback; char *callback_name; char *signal_detail; signal_detail = (char *) name_node->data; callback_name = strip_camel_case (signal_detail); if (callback_prefix) { char *tmp; tmp = g_strjoin ("_", callback_prefix, callback_name, NULL); g_free (callback_name); callback_name = tmp; } callback = find_callback_by_name (module, callback_name); g_free (callback_name); if (callback == NULL) continue; signal_detail = g_strjoin ("::", signal_name, signal_detail, NULL); g_signal_connect (service, signal_detail, callback, user_data); g_free (signal_detail); }}/** * gupnp_service_signals_autoconnect * @service: A #GUPnPService * @user_data: the data to pass to each of the callbacks * @error: return location for a #GError, or %NULL * * A convenience function that attempts to connect all possible * #GUPnPService::action-invoked and #GUPnPService::query-variable signals to * appropriate callbacks for the service @service. It uses service introspection * and GModule's introspective features. It is very simillar to * glade_xml_signal_autoconnect() except that it attempts to guess the names of * the signal handlers on its own. * * For this function to do its magic, the application must name the callback * functions for #GUPnPService::action-invoked signals by striping the CamelCase * off the action names and either prepend "on_" or append "_cb" to them. Same * goes for #GUPnPService::query-variable signals, except that "query_" should * be prepended to the variable name. For example, callback function for * "GetSystemUpdateID" action should be either named as * "get_system_update_id_cb" or "on_get_system_update_id" and callback function * for the query of "SystemUpdateID" state variable should be named * "query_system_update_id_cb" or "on_query_system_update_id". * * Note that this function will not work correctly if GModule is not supported * on the platform or introspection is not available for service @service. * * WARNING: This function can not and therefore does not guarantee that the * resulting signal connections will be correct as it depends heavily on a * particular naming schemes described above. **/voidgupnp_service_signals_autoconnect (GUPnPService *service, gpointer user_data, GError **error){ GUPnPServiceIntrospection *introspection; const GList *names; GModule *module; g_return_if_fail (GUPNP_IS_SERVICE (service)); introspection = gupnp_service_info_get_introspection (GUPNP_SERVICE_INFO (service), error); if (!introspection) return; /* Get a handle on the main executable -- use this to find symbols */ module = g_module_open (NULL, 0); if (module == NULL) { g_error ("Failed to open module: %s", g_module_error ()); g_object_unref (introspection); return; } names = gupnp_service_introspection_list_action_names (introspection); connect_names_to_signal_handlers (service, module, names, "action-invoked", NULL, user_data); names = gupnp_service_introspection_list_state_variable_names (introspection); connect_names_to_signal_handlers (service, module, names, "query-variable", "query", user_data); g_module_close (module); g_object_unref (introspection);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -