summaryrefslogtreecommitdiffstats
path: root/src/io/http.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/io/http.cpp')
-rw-r--r--src/io/http.cpp153
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 :