/** * korva_upnp_host_data_get_protocol_info: * * Get the UPnP-AV/DLNA protocol info string for the :file. The transport is * always "http-get". The ci-param is "0" (original source) and op-param is "01" * (byte seek only). If :meta-data contains a DLNA profile it will be added as * well as the the file's content type. * * The function lazy-creates the string when used for the first time. * * @self: An instance of #KorvaUPnPHostData * * Returns: (transfer none): A protocol info string. */ const char * korva_upnp_host_data_get_protocol_info (KorvaUPnPHostData *self) { if (self->priv->protocol_info == NULL) { GVariant *value; const char *dlna_profile; GUPnPProtocolInfo *info; info = gupnp_protocol_info_new_from_string ("http-get:*:*:DLNA.ORG_CI=0;DLNA.ORG_OP=01", NULL); gupnp_protocol_info_set_mime_type (info, korva_upnp_host_data_get_content_type (self)); value = g_hash_table_lookup (self->priv->meta_data, "DLNAProfile"); if (value != NULL) { dlna_profile = g_variant_get_string (value, NULL); gupnp_protocol_info_set_dlna_profile (info, dlna_profile); } self->priv->protocol_info = gupnp_protocol_info_to_string (info); g_object_unref (info); } return self->priv->protocol_info; }
static void korva_upnp_file_server_handle_request (SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *client, gpointer user_data) { KorvaUPnPFileServer *self = KORVA_UPNP_FILE_SERVER (user_data); GMatchInfo *info; char *id; GFile *file; KorvaUPnPHostData *data; ServeData *serve_data; SoupRange *ranges = NULL; int length; const char *content_features; GError *error = NULL; goffset size; if (msg->method != SOUP_METHOD_HEAD && msg->method != SOUP_METHOD_GET) { soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED); return; } g_debug ("Got %s request for uri: %s", msg->method, path); soup_message_headers_foreach (msg->request_headers, print_header, NULL); soup_server_pause_message (server, msg); soup_message_set_status (msg, SOUP_STATUS_OK); if (!g_regex_match (self->priv->path_regex, path, 0, &info)) { soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND); g_match_info_free (info); goto out; } id = g_match_info_fetch (info, 1); g_match_info_free (info); file = g_hash_table_lookup (self->priv->id_map, id); g_free (id); if (file == NULL) { soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND); goto out; } data = g_hash_table_lookup (self->priv->host_data, file); if (data == NULL) { soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND); goto out; } if (!korva_upnp_host_data_valid_for_peer (data, soup_client_context_get_host (client))) { soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND); goto out; } serve_data = g_slice_new0 (ServeData); serve_data->host_data = data; g_object_add_weak_pointer (G_OBJECT (data), (gpointer *) &(serve_data->host_data)); korva_upnp_host_data_add_request (data); size = korva_upnp_host_data_get_size (data); if (soup_message_headers_get_ranges (msg->request_headers, size, &ranges, &length)) { goffset start, end; start = ranges[0].start; end = ranges[0].end; if (start > size || start > end || end > size) { soup_message_set_status (msg, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE); g_slice_free (ServeData, serve_data); goto out; } else { soup_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT); soup_message_headers_set_content_range (msg->response_headers, start, end, size); } serve_data->start = start; serve_data->end = end; } else { serve_data->start = 0; serve_data->end = size - 1; soup_message_set_status (msg, SOUP_STATUS_OK); } soup_message_headers_set_content_length (msg->response_headers, serve_data->end - serve_data->start + 1); soup_message_headers_set_content_type (msg->response_headers, korva_upnp_host_data_get_content_type (data), NULL); content_features = soup_message_headers_get_one (msg->request_headers, "getContentFeatures.dlna.org"); if (content_features != NULL && atol (content_features) == 1) { const GVariant *value; value = korva_upnp_host_data_lookup_meta_data (data, "DLNAProfile"); if (value == NULL) { soup_message_headers_append (msg->response_headers, "contentFeatures.dlna.org", "*"); } else { soup_message_headers_append (msg->response_headers, "contentFeatures.dlna.org", korva_upnp_host_data_get_protocol_info (data)); } } soup_message_headers_append (msg->response_headers, "Connection", "close"); g_debug ("Response headers:"); soup_message_headers_foreach (msg->response_headers, print_header, NULL); if (g_ascii_strcasecmp (msg->method, "HEAD") == 0) { g_debug ("Handled HEAD request of %s: %d", path, msg->status_code); g_slice_free (ServeData, serve_data); goto out; } soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_CONTENT_LENGTH); soup_message_body_set_accumulate (msg->response_body, FALSE); serve_data->stream = G_INPUT_STREAM (g_file_read (file, NULL, &error)); serve_data->server = server; if (error != NULL) { g_warning ("Failed to MMAP file %s: %s", path, error->message); g_error_free (error); g_slice_free (ServeData, serve_data); soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); goto out; } g_seekable_seek (G_SEEKABLE (serve_data->stream), serve_data->start, G_SEEK_SET, NULL, NULL); /* Drop timeout until the message is done */ korva_upnp_host_data_cancel_timeout (data); g_signal_connect (msg, "wrote-chunk", G_CALLBACK (korva_upnp_file_server_on_wrote_chunk), serve_data); g_signal_connect (msg, "wrote-headers", G_CALLBACK (korva_upnp_file_server_on_wrote_chunk), serve_data); g_signal_connect (msg, "finished", G_CALLBACK (korva_upnp_file_server_on_finished), serve_data); out: if (ranges != NULL) { soup_message_headers_free_ranges (msg->request_headers, ranges); } soup_server_unpause_message (server, msg); }