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.
28 #include "soup-request-file.h"
30 #include "soup-directory-input-stream.h"
31 #include "soup-requester.h"
34 * SECTION:soup-request-file
35 * @short_description: SoupRequest support for "file" and "resource" URIs
37 * #SoupRequestFile implements #SoupRequest for "file" and "resource"
41 G_DEFINE_TYPE (SoupRequestFile, soup_request_file, SOUP_TYPE_REQUEST)
43 struct _SoupRequestFilePrivate {
51 soup_request_file_init (SoupRequestFile *file)
53 file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, SOUP_TYPE_REQUEST_FILE, SoupRequestFilePrivate);
55 file->priv->size = -1;
59 soup_request_file_finalize (GObject *object)
61 SoupRequestFile *file = SOUP_REQUEST_FILE (object);
63 g_clear_object (&file->priv->gfile);
64 g_free (file->priv->mime_type);
66 G_OBJECT_CLASS (soup_request_file_parent_class)->finalize (object);
70 soup_request_file_check_uri (SoupRequest *request,
74 /* "file:/foo" is not valid */
78 /* but it must be "file:///..." or "file://localhost/..." */
80 g_ascii_strcasecmp (uri->host, "localhost") != 0)
87 windowsify_file_uri_path (char *path)
91 /* Copied from g_filename_from_uri(), which we can't use
92 * directly because it rejects invalid URIs that we need to
96 /* Turn slashes into backslashes, because that's the canonical spelling */
98 while ((slash = strchr (p, '/')) != NULL) {
103 /* Windows URIs with a drive letter can be like
104 * "file://host/c:/foo" or "file://host/c|/foo" (some Netscape
105 * versions). In those cases, start the filename from the
108 if (g_ascii_isalpha (path[1])) {
112 memmove (path, path + 1, strlen (path));
118 soup_request_file_ensure_file (SoupRequestFile *file,
119 GCancellable *cancellable,
125 if (file->priv->gfile)
128 uri = soup_request_get_uri (SOUP_REQUEST (file));
129 decoded_path = soup_uri_decode (uri->path);
132 windowsify_file_uri_path (decoded_path);
135 if (uri->scheme == SOUP_URI_SCHEME_RESOURCE) {
138 uri_str = g_strdup_printf ("resource://%s", decoded_path);
139 file->priv->gfile = g_file_new_for_uri (uri_str);
142 file->priv->gfile = g_file_new_for_path (decoded_path);
144 g_free (decoded_path);
148 static GInputStream *
149 soup_request_file_send (SoupRequest *request,
150 GCancellable *cancellable,
153 SoupRequestFile *file = SOUP_REQUEST_FILE (request);
154 GInputStream *stream;
155 GError *my_error = NULL;
157 if (!soup_request_file_ensure_file (file, cancellable, error))
160 stream = G_INPUT_STREAM (g_file_read (file->priv->gfile,
161 cancellable, &my_error));
162 if (stream == NULL) {
163 if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) {
164 GFileEnumerator *enumerator;
165 g_clear_error (&my_error);
166 enumerator = g_file_enumerate_children (file->priv->gfile,
168 G_FILE_QUERY_INFO_NONE,
172 stream = soup_directory_input_stream_new (enumerator,
173 soup_request_get_uri (request));
174 g_object_unref (enumerator);
175 file->priv->mime_type = g_strdup ("text/html");
178 g_propagate_error (error, my_error);
180 GFileInfo *info = g_file_query_info (file->priv->gfile,
181 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
182 G_FILE_ATTRIBUTE_STANDARD_SIZE,
183 0, cancellable, NULL);
185 const char *content_type;
186 file->priv->size = g_file_info_get_size (info);
187 content_type = g_file_info_get_content_type (info);
190 file->priv->mime_type = g_content_type_get_mime_type (content_type);
191 g_object_unref (info);
199 soup_request_file_send_async_thread (GTask *task,
200 gpointer source_object,
202 GCancellable *cancellable)
204 SoupRequest *request = source_object;
205 GInputStream *stream;
206 GError *error = NULL;
208 stream = soup_request_file_send (request, cancellable, &error);
210 g_task_return_error (task, error);
212 g_task_return_pointer (task, stream, g_object_unref);
216 soup_request_file_send_async (SoupRequest *request,
217 GCancellable *cancellable,
218 GAsyncReadyCallback callback,
223 task = g_task_new (request, cancellable, callback, user_data);
224 g_task_run_in_thread (task, soup_request_file_send_async_thread);
225 g_object_unref (task);
228 static GInputStream *
229 soup_request_file_send_finish (SoupRequest *request,
230 GAsyncResult *result,
233 g_return_val_if_fail (g_task_is_valid (result, request), NULL);
235 return g_task_propagate_pointer (G_TASK (result), error);
239 soup_request_file_get_content_length (SoupRequest *request)
241 SoupRequestFile *file = SOUP_REQUEST_FILE (request);
243 return file->priv->size;
247 soup_request_file_get_content_type (SoupRequest *request)
249 SoupRequestFile *file = SOUP_REQUEST_FILE (request);
251 if (!file->priv->mime_type)
252 return "application/octet-stream";
254 return file->priv->mime_type;
257 static const char *file_schemes[] = { "file", "resource", NULL };
260 soup_request_file_class_init (SoupRequestFileClass *request_file_class)
262 GObjectClass *object_class = G_OBJECT_CLASS (request_file_class);
263 SoupRequestClass *request_class =
264 SOUP_REQUEST_CLASS (request_file_class);
266 g_type_class_add_private (request_file_class, sizeof (SoupRequestFilePrivate));
268 request_class->schemes = file_schemes;
270 object_class->finalize = soup_request_file_finalize;
272 request_class->check_uri = soup_request_file_check_uri;
273 request_class->send = soup_request_file_send;
274 request_class->send_async = soup_request_file_send_async;
275 request_class->send_finish = soup_request_file_send_finish;
276 request_class->get_content_length = soup_request_file_get_content_length;
277 request_class->get_content_type = soup_request_file_get_content_type;
281 * soup_request_file_get_file:
282 * @file: a #SoupRequestFile
284 * Gets a #GFile corresponding to @file's URI
286 * Return value: (transfer full): a #GFile corresponding to @file
291 soup_request_file_get_file (SoupRequestFile *file)
293 return g_object_ref (file->priv->gfile);