soup-auth-manager: add soup_auth_manager_use_auth()
[platform/upstream/libsoup.git] / libsoup / soup-request-file.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-request-file.c: file: URI request object
4  *
5  * Copyright (C) 2009, 2010 Red Hat, Inc.
6  * Copyright (C) 2010 Igalia, S.L.
7  *
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.
12  *
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.
17  *
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.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include "soup-request-file.h"
29 #include "soup.h"
30 #include "soup-directory-input-stream.h"
31 #include "soup-requester.h"
32
33 /**
34  * SECTION:soup-request-file
35  * @short_description: SoupRequest support for "file" and "resource" URIs
36  *
37  * #SoupRequestFile implements #SoupRequest for "file" and "resource"
38  * URIs.
39  */
40
41 G_DEFINE_TYPE (SoupRequestFile, soup_request_file, SOUP_TYPE_REQUEST)
42
43 struct _SoupRequestFilePrivate {
44         GFile *gfile;
45
46         char *mime_type;
47         goffset size;
48 };
49
50 static void
51 soup_request_file_init (SoupRequestFile *file)
52 {
53         file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, SOUP_TYPE_REQUEST_FILE, SoupRequestFilePrivate);
54
55         file->priv->size = -1;
56 }
57
58 static void
59 soup_request_file_finalize (GObject *object)
60 {
61         SoupRequestFile *file = SOUP_REQUEST_FILE (object);
62
63         g_clear_object (&file->priv->gfile);
64         g_free (file->priv->mime_type);
65
66         G_OBJECT_CLASS (soup_request_file_parent_class)->finalize (object);
67 }
68
69 static gboolean
70 soup_request_file_check_uri (SoupRequest  *request,
71                              SoupURI      *uri,
72                              GError      **error)
73 {
74         /* "file:/foo" is not valid */
75         if (!uri->host)
76                 return FALSE;
77
78         /* but it must be "file:///..." or "file://localhost/..." */
79         if (*uri->host &&
80             g_ascii_strcasecmp (uri->host, "localhost") != 0)
81                 return FALSE;
82         return TRUE;
83 }
84
85 #ifdef G_OS_WIN32
86 static void
87 windowsify_file_uri_path (char *path)
88 {
89         char *p, *slash;
90
91         /* Copied from g_filename_from_uri(), which we can't use
92          * directly because it rejects invalid URIs that we need to
93          * keep.
94          */
95
96         /* Turn slashes into backslashes, because that's the canonical spelling */
97         p = path;
98         while ((slash = strchr (p, '/')) != NULL) {
99                 *slash = '\\';
100                 p = slash + 1;
101         }
102
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
106          * drive letter.
107          */
108         if (g_ascii_isalpha (path[1])) {
109                 if (path[2] == '|')
110                         path[2] = ':';
111                 if (path[2] == ':')
112                         memmove (path, path + 1, strlen (path));
113         }
114 }
115 #endif
116
117 static gboolean
118 soup_request_file_ensure_file (SoupRequestFile  *file,
119                                GCancellable     *cancellable,
120                                GError          **error)
121 {
122         SoupURI *uri;
123         char *decoded_path;
124
125         if (file->priv->gfile)
126                 return TRUE;
127
128         uri = soup_request_get_uri (SOUP_REQUEST (file));
129         decoded_path = soup_uri_decode (uri->path);
130
131 #ifdef G_OS_WIN32
132         windowsify_file_uri_path (decoded_path);
133 #endif
134
135         if (uri->scheme == SOUP_URI_SCHEME_RESOURCE) {
136                 char *uri_str;
137
138                 uri_str = g_strdup_printf ("resource://%s", decoded_path);
139                 file->priv->gfile = g_file_new_for_uri (uri_str);
140                 g_free (uri_str);
141         } else
142                 file->priv->gfile = g_file_new_for_path (decoded_path);
143
144         g_free (decoded_path);
145         return TRUE;
146 }
147
148 static GInputStream *
149 soup_request_file_send (SoupRequest          *request,
150                         GCancellable         *cancellable,
151                         GError              **error)
152 {
153         SoupRequestFile *file = SOUP_REQUEST_FILE (request);
154         GInputStream *stream;
155         GError *my_error = NULL;
156
157         if (!soup_request_file_ensure_file (file, cancellable, error))
158                 return NULL;
159
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,
167                                                                 "*",
168                                                                 G_FILE_QUERY_INFO_NONE,
169                                                                 cancellable,
170                                                                 error);
171                         if (enumerator) {
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");
176                         }
177                 } else
178                         g_propagate_error (error, my_error);
179         } else {
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);
184                 if (info) {
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);
188
189                         if (content_type)
190                                 file->priv->mime_type = g_content_type_get_mime_type (content_type);
191                         g_object_unref (info);
192                 }
193         }
194
195         return stream;
196 }
197
198 static void
199 soup_request_file_send_async_thread (GTask        *task,
200                                      gpointer      source_object,
201                                      gpointer      task_data,
202                                      GCancellable *cancellable)
203 {
204         SoupRequest *request = source_object;
205         GInputStream *stream;
206         GError *error = NULL;
207
208         stream = soup_request_file_send (request, cancellable, &error);
209         if (stream == NULL)
210                 g_task_return_error (task, error);
211         else
212                 g_task_return_pointer (task, stream, g_object_unref);
213 }
214
215 static void
216 soup_request_file_send_async (SoupRequest          *request,
217                               GCancellable         *cancellable,
218                               GAsyncReadyCallback   callback,
219                               gpointer              user_data)
220 {
221         GTask *task;
222
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);
226 }
227
228 static GInputStream *
229 soup_request_file_send_finish (SoupRequest          *request,
230                                GAsyncResult         *result,
231                                GError              **error)
232 {
233         g_return_val_if_fail (g_task_is_valid (result, request), NULL);
234
235         return g_task_propagate_pointer (G_TASK (result), error);
236 }
237
238 static goffset
239 soup_request_file_get_content_length (SoupRequest *request)
240 {
241         SoupRequestFile *file = SOUP_REQUEST_FILE (request);
242
243         return file->priv->size;
244 }
245
246 static const char *
247 soup_request_file_get_content_type (SoupRequest *request)
248 {
249         SoupRequestFile *file = SOUP_REQUEST_FILE (request);
250
251         if (!file->priv->mime_type)
252                 return "application/octet-stream";
253
254         return file->priv->mime_type;
255 }
256
257 static const char *file_schemes[] = { "file", "resource", NULL };
258
259 static void
260 soup_request_file_class_init (SoupRequestFileClass *request_file_class)
261 {
262         GObjectClass *object_class = G_OBJECT_CLASS (request_file_class);
263         SoupRequestClass *request_class =
264                 SOUP_REQUEST_CLASS (request_file_class);
265
266         g_type_class_add_private (request_file_class, sizeof (SoupRequestFilePrivate));
267
268         request_class->schemes = file_schemes;
269
270         object_class->finalize = soup_request_file_finalize;
271
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;
278 }
279
280 /**
281  * soup_request_file_get_file:
282  * @file: a #SoupRequestFile
283  *
284  * Gets a #GFile corresponding to @file's URI
285  *
286  * Return value: (transfer full): a #GFile corresponding to @file
287  *
288  * Since: 2.40
289  */
290 GFile *
291 soup_request_file_get_file (SoupRequestFile *file)
292 {
293         return g_object_ref (file->priv->gfile);
294 }