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 #define LIBSOUP_USE_UNSTABLE_REQUEST_API
30 #include <glib/gi18n.h>
32 #include "soup-request-file.h"
33 #include "soup-directory-input-stream.h"
34 #include "soup-requester.h"
37 G_DEFINE_TYPE (SoupRequestFile, soup_request_file, SOUP_TYPE_REQUEST)
39 struct _SoupRequestFilePrivate {
47 soup_request_file_init (SoupRequestFile *file)
49 file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, SOUP_TYPE_REQUEST_FILE, SoupRequestFilePrivate);
51 file->priv->size = -1;
55 soup_request_file_finalize (GObject *object)
57 SoupRequestFile *file = SOUP_REQUEST_FILE (object);
59 if (file->priv->gfile)
60 g_object_unref (file->priv->gfile);
61 g_free (file->priv->mime_type);
63 G_OBJECT_CLASS (soup_request_file_parent_class)->finalize (object);
67 soup_request_file_check_uri (SoupRequest *request,
71 /* "file:/foo" is not valid */
75 /* but it must be "file:///..." or "file://localhost/..." */
77 g_ascii_strcasecmp (uri->host, "localhost") != 0)
84 windowsify_file_uri_path (char *path)
88 /* Copied from g_filename_from_uri(), which we can't use
89 * directly because it rejects invalid URIs that we need to
93 /* Turn slashes into backslashes, because that's the canonical spelling */
95 while ((slash = strchr (p, '/')) != NULL) {
100 /* Windows URIs with a drive letter can be like
101 * "file://host/c:/foo" or "file://host/c|/foo" (some Netscape
102 * versions). In those cases, start the filename from the
105 if (g_ascii_isalpha (path[1])) {
109 memmove (path, path + 1, strlen (path));
115 soup_request_file_ensure_file (SoupRequestFile *file,
116 GCancellable *cancellable,
122 if (file->priv->gfile)
125 uri = soup_request_get_uri (SOUP_REQUEST (file));
126 decoded_path = soup_uri_decode (uri->path);
129 windowsify_file_uri_path (decoded_path);
132 file->priv->gfile = g_file_new_for_path (decoded_path);
133 g_free (decoded_path);
137 static GInputStream *
138 soup_request_file_send (SoupRequest *request,
139 GCancellable *cancellable,
142 SoupRequestFile *file = SOUP_REQUEST_FILE (request);
143 GInputStream *stream;
144 GError *my_error = NULL;
146 if (!soup_request_file_ensure_file (file, cancellable, error))
149 stream = G_INPUT_STREAM (g_file_read (file->priv->gfile,
150 cancellable, &my_error));
151 if (stream == NULL) {
152 if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) {
153 GFileEnumerator *enumerator;
154 g_clear_error (&my_error);
155 enumerator = g_file_enumerate_children (file->priv->gfile,
157 G_FILE_QUERY_INFO_NONE,
161 stream = soup_directory_input_stream_new (enumerator,
162 soup_request_get_uri (request));
163 g_object_unref (enumerator);
164 file->priv->mime_type = g_strdup ("text/html");
167 g_propagate_error (error, my_error);
169 GFileInfo *info = g_file_query_info (file->priv->gfile,
170 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
171 G_FILE_ATTRIBUTE_STANDARD_SIZE,
172 0, cancellable, NULL);
174 const char *content_type;
175 file->priv->size = g_file_info_get_size (info);
176 content_type = g_file_info_get_content_type (info);
179 file->priv->mime_type = g_content_type_get_mime_type (content_type);
180 g_object_unref (info);
188 soup_request_file_send_async_thread (GSimpleAsyncResult *res,
190 GCancellable *cancellable)
192 GInputStream *stream;
193 SoupRequest *request;
194 GError *error = NULL;
196 request = SOUP_REQUEST (object);
198 stream = soup_request_file_send (request, cancellable, &error);
201 g_simple_async_result_take_error (res, error);
203 g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
207 soup_request_file_send_async (SoupRequest *request,
208 GCancellable *cancellable,
209 GAsyncReadyCallback callback,
212 GSimpleAsyncResult *res;
214 res = g_simple_async_result_new (G_OBJECT (request), callback, user_data, soup_request_file_send_async);
216 g_simple_async_result_run_in_thread (res, soup_request_file_send_async_thread, G_PRIORITY_DEFAULT, cancellable);
217 g_object_unref (res);
220 static GInputStream *
221 soup_request_file_send_finish (SoupRequest *request,
222 GAsyncResult *result,
225 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
227 g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == soup_request_file_send_async);
229 if (g_simple_async_result_propagate_error (simple, error))
232 return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
236 soup_request_file_get_content_length (SoupRequest *request)
238 SoupRequestFile *file = SOUP_REQUEST_FILE (request);
240 return file->priv->size;
244 soup_request_file_get_content_type (SoupRequest *request)
246 SoupRequestFile *file = SOUP_REQUEST_FILE (request);
248 if (!file->priv->mime_type)
249 return "application/octet-stream";
251 return file->priv->mime_type;
254 static const char *file_schemes[] = { "file", NULL };
257 soup_request_file_class_init (SoupRequestFileClass *request_file_class)
259 GObjectClass *object_class = G_OBJECT_CLASS (request_file_class);
260 SoupRequestClass *request_class =
261 SOUP_REQUEST_CLASS (request_file_class);
263 g_type_class_add_private (request_file_class, sizeof (SoupRequestFilePrivate));
265 request_class->schemes = file_schemes;
267 object_class->finalize = soup_request_file_finalize;
269 request_class->check_uri = soup_request_file_check_uri;
270 request_class->send = soup_request_file_send;
271 request_class->send_async = soup_request_file_send_async;
272 request_class->send_finish = soup_request_file_send_finish;
273 request_class->get_content_length = soup_request_file_get_content_length;
274 request_class->get_content_type = soup_request_file_get_content_type;
278 * soup_request_file_get_file:
279 * @file: a #SoupRequestFile
281 * Gets a #GFile corresponding to @file's URI
283 * Return value: (transfer full): a #GFile corresponding to @file
288 soup_request_file_get_file (SoupRequestFile *file)
290 return g_object_ref (file->priv->gfile);