📄 gupnp-service.c
字号:
g_string_append (action->response_str, "s:Client"); xml_util_end_element (action->response_str, "faultcode"); xml_util_start_element (action->response_str, "faultstring"); g_string_append (action->response_str, "UPnPError"); xml_util_end_element (action->response_str, "faultstring"); xml_util_start_element (action->response_str, "detail"); xml_util_start_element (action->response_str, "UPnPError " "xmlns=\"urn:schemas-upnp-org:control-1-0\""); xml_util_start_element (action->response_str, "errorCode"); g_string_append_printf (action->response_str, "%u", error_code); xml_util_end_element (action->response_str, "errorCode"); xml_util_start_element (action->response_str, "errorDescription"); xml_util_add_content (action->response_str, error_description); xml_util_end_element (action->response_str, "errorDescription"); xml_util_end_element (action->response_str, "UPnPError"); xml_util_end_element (action->response_str, "detail"); xml_util_end_element (action->response_str, "s:Fault"); soup_message_set_status (action->msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);}static voidgupnp_service_init (GUPnPService *device){ device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, GUPNP_TYPE_SERVICE, GUPnPServicePrivate); device->priv->subscriptions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) subscription_data_free); device->priv->notify_queue = g_queue_new ();}/* Generate a new action response node for @action_name */static GString *new_action_response_str (GUPnPService *service, const char *action_name){ GString *str; const char *type; str = xml_util_new_string (); g_string_append (str, "<u:"); g_string_append (str, action_name); g_string_append (str, "Response xmlns:u="); type = gupnp_service_info_get_service_type (GUPNP_SERVICE_INFO (service)); if (type != NULL) { g_string_append_c (str, '"'); g_string_append (str, type); g_string_append_c (str, '"'); } else { g_warning ("No serviceType defined. Control may not work " "correctly."); } g_string_append_c (str, '>'); return str;}/* Handle QueryStateVariable action */static voidquery_state_variable (GUPnPService *service, GUPnPServiceAction *action){ xmlNode *node; /* Iterate requested variables */ for (node = action->node->children; node; node = node->next) { xmlChar *var_name; GValue value = {0,}; if (strcmp ((char *) node->name, "varName") != 0) continue; /* varName */ var_name = xmlNodeGetContent (node); if (!var_name) { gupnp_service_action_return_error (action, 402, "Invalid Args"); return; } /* Query variable */ g_signal_emit (service, signals[QUERY_VARIABLE], g_quark_from_string ((char *) var_name), (char *) var_name, &value); if (!G_IS_VALUE (&value)) { gupnp_service_action_return_error (action, 402, "Invalid Args"); xmlFree (var_name); return; } /* Add variable to response */ gupnp_service_action_set_value (action, (char *) var_name, &value); /* Cleanup */ g_value_unset (&value); xmlFree (var_name); } gupnp_service_action_return (action);}/* controlURL handler */static voidcontrol_server_handler (SoupServer *soup_server, SoupMessage *msg, const char *server_path, GHashTable *query, SoupClientContext *soup_client, gpointer user_data){ GUPnPService *service; GUPnPContext *context; GSSDPClient *client; xmlDoc *doc; xmlNode *action_node; const char *soap_action, *action_name; char *end; gpointer response_body; GUPnPServiceAction *action; service = GUPNP_SERVICE (user_data); if (msg->method != SOUP_METHOD_POST) { soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); return; } context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (service)); client = GSSDP_CLIENT (context); /* Get action name */ soap_action = soup_message_headers_get (msg->request_headers, "SOAPAction"); action_name = strchr (soap_action, '#'); if (!action_name) { soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED); return; } action_name += 1; /* This memory is libsoup-owned so we can do this */ end = strrchr (action_name, '"'); *end = '\0'; /* Parse action_node */ doc = xmlParseMemory (msg->request_body->data, msg->request_body->length); action_node = xml_util_get_element ((xmlNode *) doc, "Envelope", "Body", action_name, NULL); if (!action_node) { soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED); return; } /* Create action structure */ action = g_slice_new (GUPnPServiceAction); action->name = action_name; action->msg = msg; action->node = action_node; action->response_str = new_action_response_str (service, action_name); /* QueryStateVariable? */ if (strcmp (action_name, "QueryStateVariable") == 0) query_state_variable (service, action); else { /* Emit signal. Handler parses request and fills in response. */ g_signal_emit (service, signals[ACTION_INVOKED], g_quark_from_string (action_name), action); /* Was it handled? */ if (msg->status_code == SOUP_STATUS_NONE) { /* No. */ gupnp_service_action_return_error (action, 401, "Invalid Action"); } } /* Embed action->response_str in a SOAP document */ g_string_prepend (action->response_str, "<?xml version=\"1.0\"?>" "<s:Envelope xmlns:s=" "\"http://schemas.xmlsoap.org/soap/envelope/\" " "s:encodingStyle=" "\"http://schemas.xmlsoap.org/soap/encoding/\">" "<s:Body>"); if (action->msg->status_code != SOUP_STATUS_INTERNAL_SERVER_ERROR) { g_string_append (action->response_str, "</u:"); g_string_append (action->response_str, action_name); g_string_append (action->response_str, "Response>"); } g_string_append (action->response_str, "</s:Body>" "</s:Envelope>"); response_body = g_string_free (action->response_str, FALSE); soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE, response_body, strlen (response_body)); /* Cleanup */ g_slice_free (GUPnPServiceAction, action); /* Server header on response */ soup_message_headers_append (msg->response_headers, "Server", gssdp_client_get_server_id (client));}/* Generates a standard (re)subscription response */static voidsubscription_response (GUPnPService *service, SoupMessage *msg, const char *sid, int timeout){ GUPnPContext *context; GSSDPClient *client; char *tmp; context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (service)); client = GSSDP_CLIENT (context); /* Server header on response */ soup_message_headers_append (msg->response_headers, "Server", gssdp_client_get_server_id (client)); /* SID header */ soup_message_headers_append (msg->response_headers, "SID", sid); /* Timeout header */ if (timeout > 0) tmp = g_strdup_printf ("Second-%d", timeout); else tmp = g_strdup ("infinite"); soup_message_headers_append (msg->response_headers, "Timeout", tmp); g_free (tmp); /* 200 OK */ soup_message_set_status (msg, SOUP_STATUS_OK);}/* Generates a new SID */static char *generate_sid (void){ uuid_t id; char out[39]; uuid_generate (id); uuid_unparse (id, out); return g_strdup_printf ("uuid:%s", out);}/* Subscription expired */static gbooleansubscription_timeout (gpointer user_data){ SubscriptionData *data; data = user_data; g_hash_table_remove (data->service->priv->subscriptions, data->sid); return FALSE;}/* Parse timeout header and return its value in seconds, or 0 * if infinite */static intparse_and_limit_timeout (const char *timeout){ int timeout_seconds; timeout_seconds = 0; if (strncmp (timeout, "Second-", strlen ("Second-")) == 0) { /* We have a finite timeout */ timeout_seconds = CLAMP (atoi (timeout + strlen ("Second-")), GENA_MIN_TIMEOUT, GENA_MAX_TIMEOUT); } else timeout_seconds = GENA_MAX_TIMEOUT; return timeout_seconds;}/* Subscription request */static voidsubscribe (GUPnPService *service, SoupMessage *msg, const char *callback, const char *timeout){ SubscriptionData *data; char *start, *end, *uri; int timeout_seconds; GQueue *queue; char *mem; GList *l; data = g_slice_new0 (SubscriptionData); /* Parse callback list */ start = (char *) callback; while ((start = strchr (start, '<'))) { start += 1; if (!start || !*start) break; end = strchr (start, '>'); if (!end || !*end) break; if (strncmp (start, "http://", strlen ("http://")) == 0) { uri = g_strndup (start, end - start); data->callbacks = g_list_append (data->callbacks, uri); } start = end; } if (!data->callbacks) { soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED); g_slice_free (SubscriptionData, data); return; } /* Add service and SID */ data->service = service; data->sid = generate_sid (); /* Add timeout */ timeout_seconds = parse_and_limit_timeout (timeout); if (timeout_seconds > 0) { data->timeout_id = g_timeout_add_seconds (timeout_seconds, subscription_timeout, data); } /* Add to hash */ g_hash_table_insert (service->priv->subscriptions, data->sid, data); /* Respond */ subscription_response (service, msg, data->sid, timeout_seconds); /* Send initial event message */ queue = g_queue_new (); for (l = service->priv->state_variables; l; l = l->next) { NotifyData *ndata; ndata = g_slice_new0 (NotifyData); g_signal_emit (service, signals[QUERY_VARIABLE], g_quark_from_string (l->data), l->data, &ndata->value); if (!G_IS_VALUE (&ndata->value)) { g_slice_free (NotifyData, ndata); continue; } ndata->variable = g_strdup (l->data); g_queue_push_tail (queue, ndata); } mem = create_property_set (queue); notify_subscriber (data->sid, data, mem); /* Cleanup */ g_queue_free (queue); g_free (mem);}/* Resubscription request */static voidresubscribe (GUPnPService *service, SoupMessage *msg, const char *sid, const char *timeout){ SubscriptionData *data; int timeout_seconds; data = g_hash_table_lookup (service->priv->subscriptions, sid); if (!data) { soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED); return; } /* Update timeout */ if (data->timeout_id) { g_source_remove (data->timeout_id); data->timeout_id = 0; } timeout_seconds = parse_and_limit_timeout (timeout); if (timeout_seconds > 0) { data->timeout_id = g_timeout_add_seconds (timeout_seconds, subscription_timeout, data); } /* Respond */ subscription_response (service, msg, sid, timeout_seconds);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -