1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-request-file.c: file: URI request object
5 * Copyright (C) 2009, 2010 Red Hat, Inc.
6 * Copyright (C) 2010 Igalia, S.L.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
30 #include "soup-request-file.h"
32 #include "soup-directory-input-stream.h"
33 #include "soup-requester.h"
36 * SECTION:soup-request-file
37 * @short_description: SoupRequest support for "file" and "resource" URIs
39 * #SoupRequestFile implements #SoupRequest for "file" and "resource"
43 G_DEFINE_TYPE (SoupRequestFile, soup_request_file, SOUP_TYPE_REQUEST)
45 struct _SoupRequestFilePrivate {
53 soup_request_file_init (SoupRequestFile *file)
55 file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, SOUP_TYPE_REQUEST_FILE, SoupRequestFilePrivate);
57 file->priv->size = -1;
61 soup_request_file_finalize (GObject *object)
63 SoupRequestFile *file = SOUP_REQUEST_FILE (object);
65 g_clear_object (&file->priv->gfile);
66 g_free (file->priv->mime_type);
68 G_OBJECT_CLASS (soup_request_file_parent_class)->finalize (object);
72 soup_request_file_check_uri (SoupRequest *request,
76 /* "file:/foo" is not valid */
80 /* but it must be "file:///..." or "file://localhost/..." */
82 g_ascii_strcasecmp (uri->host, "localhost") != 0)
89 windowsify_file_uri_path (char *path)
93 /* Copied from g_filename_from_uri(), which we can't use
94 * directly because it rejects invalid URIs that we need to
98 /* Turn slashes into backslashes, because that's the canonical spelling */
100 while ((slash = strchr (p, '/')) != NULL) {
105 /* Windows URIs with a drive letter can be like
106 * "file://host/c:/foo" or "file://host/c|/foo" (some Netscape
107 * versions). In those cases, start the filename from the
110 if (g_ascii_isalpha (path[1])) {
114 memmove (path, path + 1, strlen (path));
120 soup_request_file_ensure_file (SoupRequestFile *file,
121 GCancellable *cancellable,
127 if (file->priv->gfile)
130 uri = soup_request_get_uri (SOUP_REQUEST (file));
131 decoded_path = soup_uri_decode (uri->path);
134 windowsify_file_uri_path (decoded_path);
137 if (uri->scheme == SOUP_URI_SCHEME_RESOURCE) {
140 uri_str = g_strdup_printf ("resource://%s", decoded_path);
141 file->priv->gfile = g_file_new_for_uri (uri_str);
144 file->priv->gfile = g_file_new_for_path (decoded_path);
146 g_free (decoded_path);
150 static GInputStream *
151 soup_request_file_send (SoupRequest *request,
152 GCancellable *cancellable,
155 SoupRequestFile *file = SOUP_REQUEST_FILE (request);
156 GInputStream *stream;
157 GError *my_error = NULL;
159 if (!soup_request_file_ensure_file (file, cancellable, error))
162 stream = G_INPUT_STREAM (g_file_read (file->priv->gfile,
163 cancellable, &my_error));
164 if (stream == NULL) {
165 if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) {
166 GFileEnumerator *enumerator;
167 g_clear_error (&my_error);
168 enumerator = g_file_enumerate_children (file->priv->gfile,
170 G_FILE_QUERY_INFO_NONE,
174 stream = soup_directory_input_stream_new (enumerator,
175 soup_request_get_uri (request));
176 g_object_unref (enumerator);
177 file->priv->mime_type = g_strdup ("text/html");
180 g_propagate_error (error, my_error);
182 GFileInfo *info = g_file_query_info (file->priv->gfile,
183 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
184 G_FILE_ATTRIBUTE_STANDARD_SIZE,
185 0, cancellable, NULL);
187 const char *content_type;
188 file->priv->size = g_file_info_get_size (info);
189 content_type = g_file_info_get_content_type (info);
192 file->priv->mime_type = g_content_type_get_mime_type (content_type);
193 g_object_unref (info);
201 soup_request_file_send_async_thread (GTask *task,
202 gpointer source_object,
204 GCancellable *cancellable)
206 SoupRequest *request = source_object;
207 GInputStream *stream;
208 GError *error = NULL;
210 stream = soup_request_file_send (request, cancellable, &error);
212 g_task_return_error (task, error);
214 g_task_return_pointer (task, stream, g_object_unref);
218 soup_request_file_send_async (SoupRequest *request,
219 GCancellable *cancellable,
220 GAsyncReadyCallback callback,
225 task = g_task_new (request, cancellable, callback, user_data);
226 g_task_run_in_thread (task, soup_request_file_send_async_thread);
227 g_object_unref (task);
230 static GInputStream *
231 soup_request_file_send_finish (SoupRequest *request,
232 GAsyncResult *result,
235 g_return_val_if_fail (g_task_is_valid (result, request), NULL);
237 return g_task_propagate_pointer (G_TASK (result), error);
241 soup_request_file_get_content_length (SoupRequest *request)
243 SoupRequestFile *file = SOUP_REQUEST_FILE (request);
245 return file->priv->size;
249 soup_request_file_get_content_type (SoupRequest *request)
251 SoupRequestFile *file = SOUP_REQUEST_FILE (request);
253 if (!file->priv->mime_type)
254 return "application/octet-stream";
256 return file->priv->mime_type;
259 static const char *file_schemes[] = { "file", "resource", NULL };
262 soup_request_file_class_init (SoupRequestFileClass *request_file_class)
264 GObjectClass *object_class = G_OBJECT_CLASS (request_file_class);
265 SoupRequestClass *request_class =
266 SOUP_REQUEST_CLASS (request_file_class);
268 g_type_class_add_private (request_file_class, sizeof (SoupRequestFilePrivate));
270 request_class->schemes = file_schemes;
272 object_class->finalize = soup_request_file_finalize;
274 request_class->check_uri = soup_request_file_check_uri;
275 request_class->send = soup_request_file_send;
276 request_class->send_async = soup_request_file_send_async;
277 request_class->send_finish = soup_request_file_send_finish;
278 request_class->get_content_length = soup_request_file_get_content_length;
279 request_class->get_content_type = soup_request_file_get_content_type;
283 * soup_request_file_get_file:
284 * @file: a #SoupRequestFile
286 * Gets a #GFile corresponding to @file's URI
288 * Return value: (transfer full): a #GFile corresponding to @file
293 soup_request_file_get_file (SoupRequestFile *file)
295 return g_object_ref (file->priv->gfile);