2 * Copyright (C) 2009-2010 Andy Spencer <andy753421@gmail.com>
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @short_description: Hyper Text Transfer Protocol
22 * #GisHttp is a small wrapper around libsoup to provide data access using the
23 * Hyper Text Transfer Protocol. Each #GisHttp should be associated with a
24 * particular server or dataset, all the files downloaded for this dataset will
25 * be cached together in $HOME.cache/libgis/
30 #include <glib/gstdio.h>
31 #include <libsoup/soup.h>
35 gchar *_get_cache_path(GisHttp *http, const gchar *local)
37 return g_build_filename(g_get_user_cache_dir(), PACKAGE,
38 http->prefix, local, NULL);
43 * @prefix: The prefix in the cache to store the downloaded files.
44 * For example: * "/nexrad/level2/".
46 * Create a new #GisHttp for the given prefix
48 * Returns: the new #GisHttp
50 GisHttp *gis_http_new(const gchar *prefix)
52 g_debug("GisHttp: new - %s", prefix);
53 GisHttp *http = g_new0(GisHttp, 1);
54 http->soup = soup_session_sync_new();
55 http->prefix = g_strdup(prefix);
56 g_object_set(http->soup, "user-agent", PACKAGE_STRING, NULL);
62 * @http: the #GisHttp to free
64 * Frees resources used by @http and cancels any pending requests.
66 void gis_http_free(GisHttp *http)
68 g_debug("GisHttp: free - %s", http->prefix);
69 soup_session_abort(http->soup);
70 g_object_unref(http->soup);
75 /* For passing data to the chunck callback */
79 GisChunkCallback callback;
84 * Append data to the file and call the users callback if they supplied one.
86 static void _chunk_cb(SoupMessage *message, SoupBuffer *chunk, gpointer _info)
88 struct _CacheInfo *info = _info;
90 if (!SOUP_STATUS_IS_SUCCESSFUL(message->status_code)) {
91 g_warning("GisHttp: _chunk_cb - soup failed with %d",
92 message->status_code);
96 if (!fwrite(chunk->data, chunk->length, 1, info->fp))
97 g_error("GisHttp: _chunk_cb - Unable to write data");
100 goffset cur = ftell(info->fp);
101 goffset st=0, end=0, total=0;
102 soup_message_headers_get_content_range(message->response_headers,
104 info->callback(info->path, cur, total, info->user_data);
110 * @http: the #GisHttp connection to use
111 * @uri: the URI to fetch
112 * @local: the local name to give to the file
113 * @mode: the update type to use when fetching data
114 * @callback: callback to call when a chunk of data is received
115 * @user_data: user data to pass to the callback
117 * Fetch a file from the cache. Whether the file is actually loaded from the
118 * remote server depends on the value of @mode.
120 * Returns: The local path to the complete file
122 /* TODO: use .part extentions and continue even when using GIS_ONCE */
123 gchar *gis_http_fetch(GisHttp *http, const gchar *uri, const char *local,
124 GisCacheType mode, GisChunkCallback callback, gpointer user_data)
126 g_debug("GisHttp: fetch - %s... >> %s/%s mode=%d",
127 uri, http->prefix, local, mode);
129 gchar *path = _get_cache_path(http, local);
131 /* Unlink the file if we're refreshing it */
132 if (mode == GIS_REFRESH)
135 /* Do the cache if necessasairy */
136 if (!(mode == GIS_ONCE && g_file_test(path, G_FILE_TEST_EXISTS)) &&
138 g_debug("GisHttp: fetch - Caching file %s", local);
140 /* Open the file for writting */
142 if (!g_file_test(path, G_FILE_TEST_EXISTS))
143 part = g_strdup_printf("%s.part", path);
144 FILE *fp = fopen_p(part, "a");
147 struct _CacheInfo info = {
150 .callback = callback,
151 .user_data = user_data,
154 /* Download the file */
155 SoupMessage *message = soup_message_new("GET", uri);
157 g_error("message is null, cannot parse uri");
158 g_signal_connect(message, "got-chunk", G_CALLBACK(_chunk_cb), &info);
159 soup_message_headers_set_range(message->request_headers, ftell(fp), -1);
160 soup_session_send_message(http->soup, message);
164 if (path != part && SOUP_STATUS_IS_SUCCESSFUL(message->status_code)) {
165 g_rename(part, path);
170 if (message->status_code == 416) {
171 /* Range unsatisfiable, file already complete */
172 } else if (!SOUP_STATUS_IS_SUCCESSFUL(message->status_code)) {
173 g_warning("GisHttp: done_cb - error copying file, status=%d\n"
176 message->status_code, uri, path);
181 /* TODO: free everything.. */