Reapplying patch to disable attempts to use gtk-doc
[profile/ivi/libsoup2.4.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 #define LIBSOUP_USE_UNSTABLE_REQUEST_API
29
30 #include <glib/gi18n.h>
31
32 #include "soup-request-file.h"
33 #include "soup-directory-input-stream.h"
34 #include "soup-requester.h"
35 #include "soup-uri.h"
36
37 G_DEFINE_TYPE (SoupRequestFile, soup_request_file, SOUP_TYPE_REQUEST)
38
39 struct _SoupRequestFilePrivate {
40         GFile *gfile;
41
42         char *mime_type;
43         goffset size;
44 };
45
46 static void
47 soup_request_file_init (SoupRequestFile *file)
48 {
49         file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, SOUP_TYPE_REQUEST_FILE, SoupRequestFilePrivate);
50
51         file->priv->size = -1;
52 }
53
54 static void
55 soup_request_file_finalize (GObject *object)
56 {
57         SoupRequestFile *file = SOUP_REQUEST_FILE (object);
58
59         if (file->priv->gfile)
60                 g_object_unref (file->priv->gfile);
61         g_free (file->priv->mime_type);
62
63         G_OBJECT_CLASS (soup_request_file_parent_class)->finalize (object);
64 }
65
66 static gboolean
67 soup_request_file_check_uri (SoupRequest  *request,
68                              SoupURI      *uri,
69                              GError      **error)
70 {
71         /* "file:/foo" is not valid */
72         if (!uri->host)
73                 return FALSE;
74
75         /* but it must be "file:///..." or "file://localhost/..." */
76         if (*uri->host &&
77             g_ascii_strcasecmp (uri->host, "localhost") != 0)
78                 return FALSE;
79         return TRUE;
80 }
81
82 #ifdef G_OS_WIN32
83 static void
84 windowsify_file_uri_path (char *path)
85 {
86         char *p, *slash;
87
88         /* Copied from g_filename_from_uri(), which we can't use
89          * directly because it rejects invalid URIs that we need to
90          * keep.
91          */
92
93         /* Turn slashes into backslashes, because that's the canonical spelling */
94         p = path;
95         while ((slash = strchr (p, '/')) != NULL) {
96                 *slash = '\\';
97                 p = slash + 1;
98         }
99
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
103          * drive letter.
104          */
105         if (g_ascii_isalpha (path[1])) {
106                 if (path[2] == '|')
107                         path[2] = ':';
108                 if (path[2] == ':')
109                         memmove (path, path + 1, strlen (path));
110         }
111 }
112 #endif
113
114 static gboolean
115 soup_request_file_ensure_file (SoupRequestFile  *file,
116                                GCancellable     *cancellable,
117                                GError          **error)
118 {
119         SoupURI *uri;
120         char *decoded_path;
121
122         if (file->priv->gfile)
123                 return TRUE;
124
125         uri = soup_request_get_uri (SOUP_REQUEST (file));
126         decoded_path = soup_uri_decode (uri->path);
127
128 #ifdef G_OS_WIN32
129         windowsify_file_uri_path (decoded_path);
130 #endif
131
132         file->priv->gfile = g_file_new_for_path (decoded_path);
133         g_free (decoded_path);
134         return TRUE;
135 }
136
137 static GInputStream *
138 soup_request_file_send (SoupRequest          *request,
139                         GCancellable         *cancellable,
140                         GError              **error)
141 {
142         SoupRequestFile *file = SOUP_REQUEST_FILE (request);
143         GInputStream *stream;
144         GError *my_error = NULL;
145
146         if (!soup_request_file_ensure_file (file, cancellable, error))
147                 return NULL;
148
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,
156                                                                 "*",
157                                                                 G_FILE_QUERY_INFO_NONE,
158                                                                 cancellable,
159                                                                 error);
160                         if (enumerator) {
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");
165                         }
166                 } else
167                         g_propagate_error (error, my_error);
168         } else {
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);
173                 if (info) {
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);
177
178                         if (content_type)
179                                 file->priv->mime_type = g_content_type_get_mime_type (content_type);
180                         g_object_unref (info);
181                 }
182         }
183
184         return stream;
185 }
186
187 static void
188 soup_request_file_send_async_thread (GSimpleAsyncResult *res,
189                                      GObject            *object,
190                                      GCancellable       *cancellable)
191 {
192         GInputStream *stream;
193         SoupRequest *request;
194         GError *error = NULL;
195
196         request = SOUP_REQUEST (object);
197
198         stream = soup_request_file_send (request, cancellable, &error);
199
200         if (stream == NULL)
201                 g_simple_async_result_take_error (res, error);
202         else
203                 g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
204 }
205
206 static void
207 soup_request_file_send_async (SoupRequest          *request,
208                               GCancellable         *cancellable,
209                               GAsyncReadyCallback callback,
210                               gpointer user_data)
211 {
212         GSimpleAsyncResult *res;
213
214         res = g_simple_async_result_new (G_OBJECT (request), callback, user_data, soup_request_file_send_async);
215
216         g_simple_async_result_run_in_thread (res, soup_request_file_send_async_thread, G_PRIORITY_DEFAULT, cancellable);
217         g_object_unref (res);
218 }
219
220 static GInputStream *
221 soup_request_file_send_finish (SoupRequest          *request,
222                                GAsyncResult         *result,
223                                GError              **error)
224 {
225         GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
226
227         g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == soup_request_file_send_async);
228
229         if (g_simple_async_result_propagate_error (simple, error))
230                 return NULL;
231
232         return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
233 }
234
235 static goffset
236 soup_request_file_get_content_length (SoupRequest *request)
237 {
238         SoupRequestFile *file = SOUP_REQUEST_FILE (request);
239
240         return file->priv->size;
241 }
242
243 static const char *
244 soup_request_file_get_content_type (SoupRequest *request)
245 {
246         SoupRequestFile *file = SOUP_REQUEST_FILE (request);
247
248         if (!file->priv->mime_type)
249                 return "application/octet-stream";
250
251         return file->priv->mime_type;
252 }
253
254 static const char *file_schemes[] = { "file", NULL };
255
256 static void
257 soup_request_file_class_init (SoupRequestFileClass *request_file_class)
258 {
259         GObjectClass *object_class = G_OBJECT_CLASS (request_file_class);
260         SoupRequestClass *request_class =
261                 SOUP_REQUEST_CLASS (request_file_class);
262
263         g_type_class_add_private (request_file_class, sizeof (SoupRequestFilePrivate));
264
265         request_class->schemes = file_schemes;
266
267         object_class->finalize = soup_request_file_finalize;
268
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;
275 }
276
277 /**
278  * soup_request_file_get_file:
279  * @file: a #SoupRequestFile
280  *
281  * Gets a #GFile corresponding to @file's URI
282  *
283  * Return value: (transfer full): a #GFile corresponding to @file
284  *
285  * Since: 2.34
286  */
287 GFile *
288 soup_request_file_get_file (SoupRequestFile *file)
289 {
290         return g_object_ref (file->priv->gfile);
291 }