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