/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * gsf-input-http.c: retrieves input via HTTP * * Copyright (C) 2006 Michael Lawrence (lawremi@iastate.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2.1 of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #include #include #include #include #include struct _GsfInputHTTP { GsfInput input; /*< private > */ gchar *url; gchar *content_type; gpointer ctx; guint8 *buf; gsize buf_size; }; typedef struct { GsfInputClass input; } GsfInputHTTPClass; static void gsf_input_http_set_property (GObject *object, guint property_id, GValue const *value, GParamSpec *pspec); static void gsf_input_http_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void gsf_input_http_init (GObject *obj); static void gsf_input_http_class_init (GsfInputHTTPClass *c); static GsfInput *gsf_input_http_dup (GsfInput *src, GError **err); static guint8 const *gsf_input_http_read (GsfInput *input, size_t num_bytes, guint8 *buffer); static gboolean gsf_input_http_seek (GsfInput *input, gsf_off_t offset, GSeekType whence); enum { PROP_0, PROP_URL, PROP_CONTENT_TYPE }; /* pointer to the class of our parent */ static GsfInputClass *parent_class = NULL; static void gsf_input_http_finalize (GObject *obj_input) { GsfInputHTTP *input = GSF_INPUT_HTTP (obj_input); if (input->url) { g_free (input->url); input->url = NULL; } if (input->content_type) { g_free (input->content_type); input->content_type = NULL; } if (input->ctx) { xmlNanoHTTPClose ((gpointer) input->ctx); input->ctx = NULL; } if (input->buf) { g_free (input->buf); input->buf = NULL; } ((GObjectClass *)parent_class)->finalize (obj_input); } static void gsf_input_http_init (GObject *obj) { GsfInputHTTP *http = GSF_INPUT_HTTP (obj); http->url = NULL; http->content_type = NULL; http->ctx = NULL; http->buf = NULL; http->buf_size = 0; } static void gsf_input_http_class_init (GsfInputHTTPClass *c) { GObjectClass *g_object_class = (GObjectClass *) c; GsfInputClass *gsf_input_class = (GsfInputClass *) c; GParamSpec *param_spec; parent_class = g_type_class_ref (gsf_input_get_type ()); gsf_input_class->Dup = gsf_input_http_dup; gsf_input_class->Read = gsf_input_http_read; gsf_input_class->Seek = gsf_input_http_seek; g_object_class->finalize = gsf_input_http_finalize; g_object_class->get_property = gsf_input_http_get_property; g_object_class->set_property = gsf_input_http_set_property; param_spec = g_param_spec_string ("url" /* name */ , "URL" /* nick */ , "HTTP URL accessed by this stream" /* blurb */ , NULL /* default_value */ , (GParamFlags) (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (g_object_class, PROP_URL, param_spec); param_spec = g_param_spec_string ("content_type" /* name */ , "mime type" /* nick */ , "Content-Type in HTTP header" /* blurb */ , NULL /* default_value */ , (GParamFlags) (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (g_object_class, PROP_CONTENT_TYPE, param_spec); } static void gsf_input_http_set_property (GObject *object, guint property_id, GValue const *VAL, GParamSpec *pspec) { GsfInputHTTP *input; char *old; input = GSF_INPUT_HTTP (object); switch (property_id) { case PROP_URL: old = input->url; input->url = g_value_dup_string (VAL); g_free (old); break; case PROP_CONTENT_TYPE: old = input->content_type; input->content_type = g_value_dup_string (VAL); g_free (old); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gsf_input_http_get_property (GObject *object, guint property_id, GValue *VAL, GParamSpec *pspec) { GsfInputHTTP *input; input = GSF_INPUT_HTTP (object); switch (property_id) { case PROP_URL: g_value_set_string (VAL, input->url); break; case PROP_CONTENT_TYPE: g_value_set_string (VAL, input->content_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } /** * gsf_input_http_get_url : * @input: #GsfInputHTTP * * Returns: an allocated string containing the URL used for input. **/ gchar * gsf_input_http_get_url (GsfInputHTTP *input) { gchar *url; g_return_val_if_fail(GSF_IS_INPUT_HTTP(input), NULL); g_object_get (G_OBJECT (input), "url", &url, NULL); return url; } /** * gsf_input_http_get_content_type : * @input: #GsfInputHTTP * * Returns: an allocated string containing the Content-Type field of the HTTP response. **/ gchar * gsf_input_http_get_content_type (GsfInputHTTP *input) { gchar *content_type; g_return_val_if_fail(GSF_IS_INPUT_HTTP(input), NULL); g_object_get (G_OBJECT (input), "content_type", &content_type, NULL); return content_type; } /** * gsf_input_http_new : * @url: A string containing the URL to retrieve * @error: Holds any errors encountered when establishing the HTTP connection * * Returns: an open HTTP connection, ready for reading. **/ GsfInput * gsf_input_http_new (gchar const * url, GError **error G_GNUC_UNUSED) { GObject *obj; gpointer ctx; char *content_type; g_return_val_if_fail(url != NULL, NULL); ctx = xmlNanoHTTPOpen (url, &content_type); if (!ctx) /* no meaningful errors provided by nanohttp */ return NULL; obj = g_object_new (GSF_INPUT_HTTP_TYPE, "url", url, "content-type", content_type, NULL); if (G_UNLIKELY (NULL == obj)) return NULL; gsf_input_set_size (GSF_INPUT (obj), xmlNanoHTTPContentLength (ctx)); GSF_INPUT_HTTP (obj)->ctx = ctx; return GSF_INPUT (obj); } static GsfInput * gsf_input_http_dup (GsfInput *src, GError **err) { return gsf_input_http_new (GSF_INPUT_HTTP (src)->url, err); } static guint8 const * gsf_input_http_read (GsfInput *input, size_t num_bytes, guint8 *buffer) { int nread; size_t total_read; gpointer ctx = GSF_INPUT_HTTP (input)->ctx; GsfInputHTTP *input_http = GSF_INPUT_HTTP (input); if (buffer == NULL) { if (input_http->buf_size < num_bytes) { input_http->buf_size = num_bytes; g_free (input_http->buf); input_http->buf = g_new (guint8, input_http->buf_size); } buffer = input_http->buf; } for (total_read = 0; total_read < num_bytes; total_read += nread) { nread = xmlNanoHTTPRead (ctx, buffer, num_bytes - total_read); if (nread <= 0) return NULL; } return buffer; } static gboolean gsf_input_http_seek (GsfInput *input G_GNUC_UNUSED, gsf_off_t offset G_GNUC_UNUSED, GSeekType whence G_GNUC_UNUSED) { return FALSE; } GSF_CLASS (GsfInputHTTP, gsf_input_http, gsf_input_http_class_init, gsf_input_http_init, GSF_INPUT_TYPE)