"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-input-http.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-input-http.c: retrieves input via HTTP
4  *
5  * Copyright (C) 2006 Michael Lawrence (lawremi@iastate.edu)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21  
22 #include <gsf-config.h>
23 #include <gsf/gsf-input-http.h>
24 #include <gsf/gsf-input-impl.h>
25 #include <gsf/gsf-impl-utils.h>
26 #include <libxml/nanohttp.h>
27
28 struct _GsfInputHTTP {
29         GsfInput input;
30         /*< private > */
31         gchar *url;
32         gchar *content_type;
33         gpointer ctx;
34         guint8 *buf;
35         gsize buf_size;
36 };
37
38 typedef struct {
39         GsfInputClass input;
40 } GsfInputHTTPClass;
41
42 static void gsf_input_http_set_property (GObject *object, guint property_id,
43                                          GValue const *value,
44                                          GParamSpec *pspec);
45 static void gsf_input_http_get_property (GObject *object, guint property_id,
46                                          GValue *value, GParamSpec *pspec);
47 static void gsf_input_http_init (GObject *obj);
48 static void gsf_input_http_class_init (GsfInputHTTPClass *c);
49 static GsfInput *gsf_input_http_dup (GsfInput *src, GError **err);
50 static guint8 const *gsf_input_http_read (GsfInput *input, size_t num_bytes,
51                                           guint8 *buffer);
52 static gboolean gsf_input_http_seek (GsfInput *input, gsf_off_t offset,
53                                      GSeekType whence);
54
55 enum
56 {
57         PROP_0,
58         PROP_URL,
59         PROP_CONTENT_TYPE
60 };
61
62 /* pointer to the class of our parent */
63 static GsfInputClass *parent_class = NULL;
64
65 static void
66 gsf_input_http_finalize (GObject *obj_input)
67 {
68         GsfInputHTTP *input = GSF_INPUT_HTTP (obj_input);
69
70         g_free (input->url);
71         input->url = NULL;
72
73         g_free (input->content_type);
74         input->content_type = NULL;
75
76         if (input->ctx) {
77                 xmlNanoHTTPClose ((gpointer) input->ctx);
78                 input->ctx = NULL;
79         }
80
81         g_free (input->buf);
82         input->buf = NULL;
83
84         ((GObjectClass *)parent_class)->finalize (obj_input);
85 }
86
87 static void
88 gsf_input_http_init (GObject *obj)
89 {
90         GsfInputHTTP *http = GSF_INPUT_HTTP (obj);
91
92         http->url = NULL;
93         http->content_type = NULL;
94         http->ctx = NULL;
95         http->buf = NULL;
96         http->buf_size = 0;
97 }
98
99 static void
100 gsf_input_http_class_init (GsfInputHTTPClass *c)
101 {
102         GObjectClass *g_object_class = (GObjectClass *) c;
103         GsfInputClass *gsf_input_class = (GsfInputClass *) c;
104         GParamSpec *param_spec;
105
106         parent_class = g_type_class_ref (gsf_input_get_type ());
107
108         gsf_input_class->Dup = gsf_input_http_dup;
109         gsf_input_class->Read = gsf_input_http_read;
110         gsf_input_class->Seek = gsf_input_http_seek;
111         g_object_class->finalize = gsf_input_http_finalize;
112         g_object_class->get_property = gsf_input_http_get_property;
113         g_object_class->set_property = gsf_input_http_set_property;
114
115         param_spec = g_param_spec_string ("url" /* name */ ,
116                                           "URL" /* nick */ ,
117                                           "HTTP URL accessed by this stream"
118                                           /* blurb */ ,
119                                           NULL /* default_value */ ,
120                                           (GParamFlags) (G_PARAM_READABLE |
121                                                          G_PARAM_WRITABLE |
122                                                          G_PARAM_CONSTRUCT_ONLY));
123         g_object_class_install_property (g_object_class, PROP_URL,
124                                          param_spec);
125
126         param_spec = g_param_spec_string ("content_type" /* name */ ,
127                                           "mime type" /* nick */ ,
128                                           "Content-Type in HTTP header" /* blurb */
129                                           , NULL /* default_value */ ,
130                                           (GParamFlags) (G_PARAM_READABLE |
131                                                          G_PARAM_WRITABLE |
132                                                          G_PARAM_CONSTRUCT_ONLY));
133         g_object_class_install_property (g_object_class, PROP_CONTENT_TYPE,
134                                          param_spec);
135 }
136
137 static void
138 gsf_input_http_set_property (GObject *object,
139                              guint property_id,
140                              GValue const *VAL, GParamSpec *pspec)
141 {
142         GsfInputHTTP *input;
143         char *old;
144         
145         input = GSF_INPUT_HTTP (object);
146         
147         switch (property_id) {
148         case PROP_URL:
149                 old = input->url;
150                 input->url = g_value_dup_string (VAL);
151                 g_free (old);
152                 break;
153         case PROP_CONTENT_TYPE:
154                 old = input->content_type;
155                 input->content_type = g_value_dup_string (VAL);
156                 g_free (old);
157                 break;
158         default:
159                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
160                                                    pspec);
161                 break;
162         }
163 }
164
165 static void
166 gsf_input_http_get_property (GObject *object,
167                              guint property_id,
168                              GValue *VAL, GParamSpec *pspec)
169 {
170         GsfInputHTTP *input;
171
172         input = GSF_INPUT_HTTP (object);
173
174         switch (property_id) {
175         case PROP_URL:
176                 g_value_set_string (VAL, input->url);
177                 break;
178         case PROP_CONTENT_TYPE:
179                 g_value_set_string (VAL, input->content_type);
180                 break;
181         default:
182                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
183                                                    pspec);
184                 break;
185         }
186 }
187
188 /**
189  * gsf_input_http_get_url :
190  * @input: #GsfInputHTTP
191  *
192  * Returns: an allocated string containing the URL used for input.
193  **/
194 gchar *
195 gsf_input_http_get_url (GsfInputHTTP *input)
196 {
197         gchar *url;
198         g_return_val_if_fail(GSF_IS_INPUT_HTTP(input), NULL);
199         g_object_get (G_OBJECT (input), "url", &url, NULL);
200         return url;
201 }
202
203 /**
204  * gsf_input_http_get_content_type :
205  * @input: #GsfInputHTTP
206  * 
207  * Returns: an allocated string containing the Content-Type field of the HTTP response.
208  **/
209 gchar *
210 gsf_input_http_get_content_type (GsfInputHTTP *input)
211 {
212         gchar *content_type;
213         g_return_val_if_fail(GSF_IS_INPUT_HTTP(input), NULL);
214         g_object_get (G_OBJECT (input), "content_type", &content_type, NULL);
215         return content_type;
216 }
217
218 /**
219  * gsf_input_http_new :
220  * @url: A string containing the URL to retrieve
221  * @error: Holds any errors encountered when establishing the HTTP connection
222  *
223  * Returns: an open HTTP connection, ready for reading.
224  **/
225 GsfInput *
226 gsf_input_http_new (gchar const * url, GError **error G_GNUC_UNUSED)
227 {
228         GObject *obj;
229         gpointer ctx;
230         char *content_type;
231
232         g_return_val_if_fail(url != NULL, NULL);
233
234         ctx = xmlNanoHTTPOpen (url, &content_type);
235         if (!ctx)               /* no meaningful errors provided by nanohttp */
236                 return NULL;
237
238         obj = g_object_new (GSF_INPUT_HTTP_TYPE,
239                 "url",          url, 
240                 "content-type", content_type,
241                 NULL);
242         if (G_UNLIKELY (NULL == obj)) return NULL;
243         
244         gsf_input_set_size (GSF_INPUT (obj), xmlNanoHTTPContentLength (ctx));
245         GSF_INPUT_HTTP (obj)->ctx = ctx;
246         
247         return GSF_INPUT (obj);
248 }
249
250 static GsfInput *
251 gsf_input_http_dup (GsfInput *src, GError **err)
252 {
253         return gsf_input_http_new (GSF_INPUT_HTTP (src)->url, err);
254 }
255
256 static guint8 const *
257 gsf_input_http_read (GsfInput *input, size_t num_bytes, guint8 *buffer)
258 {
259         int nread;
260         size_t total_read;
261         gpointer ctx = GSF_INPUT_HTTP (input)->ctx;
262         GsfInputHTTP *input_http = GSF_INPUT_HTTP (input);
263
264         if (buffer == NULL) {
265                 if (input_http->buf_size < num_bytes) {
266                         input_http->buf_size = num_bytes;
267                         g_free (input_http->buf);
268                         input_http->buf = g_new (guint8, input_http->buf_size);
269                 }
270                 buffer = input_http->buf;
271         }
272
273         for (total_read = 0; total_read < num_bytes; total_read += nread) {
274                 nread = xmlNanoHTTPRead (ctx, buffer, num_bytes - total_read);
275                 if (nread <= 0)
276                         return NULL;
277         }
278         return buffer;
279 }
280
281 static gboolean
282 gsf_input_http_seek (GsfInput *input G_GNUC_UNUSED, 
283                      gsf_off_t offset G_GNUC_UNUSED, GSeekType whence G_GNUC_UNUSED)
284 {
285         return FALSE;
286 }
287
288 GSF_CLASS (GsfInputHTTP, gsf_input_http,
289            gsf_input_http_class_init, gsf_input_http_init,
290            GSF_INPUT_TYPE)