diff options
Diffstat (limited to 'src/io/http.cpp')
-rw-r--r-- | src/io/http.cpp | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/src/io/http.cpp b/src/io/http.cpp new file mode 100644 index 0000000..6f28db7 --- /dev/null +++ b/src/io/http.cpp @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Inkscape::IO::HTTP - make internet requests using libsoup + *//* + * Authors: see git history + * + * Copyright (C) 2018 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +/* + * How to use: + * + * #include "io/http.cpp" + * void _async_test_call(Glib::ustring filename) { + * g_warning("HTTP request saved to %s", filename.c_str()); + * } + * uint timeout = 20 * 60 * 60; // 20 hours + * Glib::ustring filename = Inkscape::IO::HTTP::get_file("https://media.inkscape.org/media/messages.xml", timeout, _async_test_call); + * + */ + +#include <glib/gstdio.h> +#include <libsoup/soup.h> +#include <string> +#include <ctime> + +#include "io/sys.h" +#include "io/http.h" +#include "io/resource.h" + +typedef std::function<void(Glib::ustring)> callback; + +namespace Resource = Inkscape::IO::Resource; + +namespace Inkscape { +namespace IO { +namespace HTTP { + +void _save_data_as_file(Glib::ustring filename, const char *result) { + FILE *fileout = Inkscape::IO::fopen_utf8name(filename.c_str(), "wb"); + if (!fileout) { + g_warning("HTTP Cache: Can't open %s for write.", filename.c_str()); + return; + } + + fputs(result, fileout); + fflush(fileout); + if (ferror(fileout)) { + g_warning("HTTP Cache: Error writing data to %s.", filename.c_str()); + } + + fclose(fileout); +} + +void _get_file_callback(SoupSession *session, SoupMessage *msg, gpointer user_data) { + auto data = static_cast<std::pair<callback, Glib::ustring>*>(user_data); + data->first(data->second); + delete data; +} + +/* + * Downloads a file, caches it in the local filesystem and then returns the + * new filename of the cached file. + * + * Returns: filename where the file has been (blocking) or will be (async) stored. + * + * uri - The uri of the desired resource, the filename comes from this uri + * timeout - Number of seconds to keep the cache, default is forever + * func - An optional function, if given the function becomes asynchronous + * the returned filename will be where the file 'hopes' to be saved + * and this function will be called when the http request is complete. + * + * NOTE: If the cache file exists, then it's returned without any async request + * your func will be called in a blocking way BEFORE this function returns. + * + */ +Glib::ustring get_file(Glib::ustring uri, unsigned int timeout, callback func) { + + SoupURI *s_uri = soup_uri_new(uri.c_str()); + std::string path = std::string(soup_uri_decode(soup_uri_get_path(s_uri))); + std::string filepart; + + // Parse the url into a filename suitable for caching. + if(path.back() == '/') { + filepart = path.replace(path.begin(), path.end(), '/', '_'); + filepart += ".url"; + } else { + filepart = path.substr(path.rfind("/") + 1); + } + + const char *ret = get_path(Resource::CACHE, Resource::NONE, filepart.c_str()); + Glib::ustring filename = Glib::ustring(ret); + + // We test first if the cache already exists + if(file_test(filename.c_str(), G_FILE_TEST_EXISTS) && timeout > 0) { + GStatBuf st; + if(g_stat(filename.c_str(), &st) != -1) { + time_t changed = st.st_mtime; + time_t now = time(nullptr); + // The cache hasn't timed out, so return the filename. + if(now - changed < timeout) { + if(func) { + // Non-async func callback return, may block. + func(filename); + } + return filename; + } + g_debug("HTTP Cache is stale: %s", filename.c_str()); + } + } + + // Only then do we get the http request + SoupMessage *msg = soup_message_new_from_uri("GET", s_uri); + SoupSession *session = soup_session_new(); + +#ifdef DEBUG_HTTP + SoupLogger *logger; + logger = soup_logger_new(SOUP_LOGGER_LOG_BODY, -1); + soup_session_add_feature(session, SOUP_SESSION_FEATURE (logger)); + g_object_unref (logger); +#endif + + if(func) { + auto *user_data = new std::pair<callback, Glib::ustring>(func, filename); + soup_session_queue_message(session, msg, _get_file_callback, user_data); + } else { + guint status = soup_session_send_message (session, msg); + if(status == SOUP_STATUS_OK) { + g_debug("HTTP Cache saved to: %s", filename.c_str()); + _save_data_as_file(filename, msg->response_body->data); + } else { + g_warning("Can't download %s", uri.c_str()); + } + } + return filename; +} + + +} +} +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |