Git init
[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->scheme == SOUP_URI_SCHEME_FILE &&
77             *uri->host &&
78             g_ascii_strcasecmp (uri->host, "localhost") != 0)
79                 return FALSE;
80
81         return TRUE;
82 }
83
84 static gboolean
85 soup_request_file_ensure_file (SoupRequestFile  *file,
86                                GCancellable     *cancellable,
87                                GError          **error)
88 {
89         SoupURI *uri;
90
91         if (file->priv->gfile)
92                 return TRUE;
93
94         uri = soup_request_get_uri (SOUP_REQUEST (file));
95         if (uri->scheme == SOUP_URI_SCHEME_FILE) {
96                 gchar *decoded_uri = soup_uri_decode (uri->path);
97
98                 if (decoded_uri) {
99                         file->priv->gfile = g_file_new_for_path (decoded_uri);
100                         g_free (decoded_uri);
101                 }
102
103                 return TRUE;
104         }
105
106         g_set_error (error, SOUP_REQUESTER_ERROR, SOUP_REQUESTER_ERROR_UNSUPPORTED_URI_SCHEME,
107                      _("Unsupported URI scheme '%s'"), uri->scheme);
108         return FALSE;
109 }
110
111 static GInputStream *
112 soup_request_file_send (SoupRequest          *request,
113                         GCancellable         *cancellable,
114                         GError              **error)
115 {
116         SoupRequestFile *file = SOUP_REQUEST_FILE (request);
117         GInputStream *stream;
118         GError *my_error = NULL;
119
120         if (!soup_request_file_ensure_file (file, cancellable, error))
121                 return NULL;
122
123         stream = G_INPUT_STREAM (g_file_read (file->priv->gfile,
124                                               cancellable, &my_error));
125         if (stream == NULL) {
126                 if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) {
127                         GFileEnumerator *enumerator;
128                         g_clear_error (&my_error);
129                         enumerator = g_file_enumerate_children (file->priv->gfile,
130                                                                 "*",
131                                                                 G_FILE_QUERY_INFO_NONE,
132                                                                 cancellable,
133                                                                 error);
134                         if (enumerator) {
135                                 stream = soup_directory_input_stream_new (enumerator,
136                                                                           soup_request_get_uri (request));
137                                 g_object_unref (enumerator);
138                                 file->priv->mime_type = g_strdup ("text/html");
139                         }
140                 } else
141                         g_propagate_error (error, my_error);
142         } else {
143                 GFileInfo *info = g_file_query_info (file->priv->gfile,
144                                                      G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
145                                                      G_FILE_ATTRIBUTE_STANDARD_SIZE,
146                                                      0, cancellable, NULL);
147                 if (info) {
148                         const char *content_type;
149                         file->priv->size = g_file_info_get_size (info);
150                         content_type = g_file_info_get_content_type (info);
151
152                         if (content_type)
153                                 file->priv->mime_type = g_content_type_get_mime_type (content_type);
154                         g_object_unref (info);
155                 }
156         }
157
158         return stream;
159 }
160
161 static void
162 soup_request_file_send_async_thread (GSimpleAsyncResult *res,
163                                      GObject            *object,
164                                      GCancellable       *cancellable)
165 {
166         GInputStream *stream;
167         SoupRequest *request;
168         GError *error = NULL;
169
170         request = SOUP_REQUEST (object);
171
172         stream = soup_request_file_send (request, cancellable, &error);
173
174         if (stream == NULL) {
175                 g_simple_async_result_set_from_error (res, error);
176                 g_error_free (error);
177         } else {
178                 g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
179         }
180 }
181
182 static void
183 soup_request_file_send_async (SoupRequest          *request,
184                               GCancellable         *cancellable,
185                               GAsyncReadyCallback callback,
186                               gpointer user_data)
187 {
188         GSimpleAsyncResult *res;
189
190         res = g_simple_async_result_new (G_OBJECT (request), callback, user_data, soup_request_file_send_async);
191
192         g_simple_async_result_run_in_thread (res, soup_request_file_send_async_thread, G_PRIORITY_DEFAULT, cancellable);
193         g_object_unref (res);
194 }
195
196 static GInputStream *
197 soup_request_file_send_finish (SoupRequest          *request,
198                                GAsyncResult         *result,
199                                GError              **error)
200 {
201         GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
202
203         g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == soup_request_file_send_async);
204
205         if (g_simple_async_result_propagate_error (simple, error))
206                 return NULL;
207
208         return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
209 }
210
211 static goffset
212 soup_request_file_get_content_length (SoupRequest *request)
213 {
214         SoupRequestFile *file = SOUP_REQUEST_FILE (request);
215
216         return file->priv->size;
217 }
218
219 static const char *
220 soup_request_file_get_content_type (SoupRequest *request)
221 {
222         SoupRequestFile *file = SOUP_REQUEST_FILE (request);
223
224         if (!file->priv->mime_type)
225                 return "application/octet-stream";
226
227         return file->priv->mime_type;
228 }
229
230 static const char *file_schemes[] = { "file", NULL };
231
232 static void
233 soup_request_file_class_init (SoupRequestFileClass *request_file_class)
234 {
235         GObjectClass *object_class = G_OBJECT_CLASS (request_file_class);
236         SoupRequestClass *request_class =
237                 SOUP_REQUEST_CLASS (request_file_class);
238
239         g_type_class_add_private (request_file_class, sizeof (SoupRequestFilePrivate));
240
241         request_class->schemes = file_schemes;
242
243         object_class->finalize = soup_request_file_finalize;
244
245         request_class->check_uri = soup_request_file_check_uri;
246         request_class->send = soup_request_file_send;
247         request_class->send_async = soup_request_file_send_async;
248         request_class->send_finish = soup_request_file_send_finish;
249         request_class->get_content_length = soup_request_file_get_content_length;
250         request_class->get_content_type = soup_request_file_get_content_type;
251 }
252
253 GFile *
254 soup_request_file_get_file (SoupRequestFile *file)
255 {
256         return g_object_ref (file->priv->gfile);
257 }