diff --git a/SOURCES/modular-soup.diff b/SOURCES/modular-soup.diff new file mode 100644 index 0000000..019453d --- /dev/null +++ b/SOURCES/modular-soup.diff @@ -0,0 +1,3431 @@ +From 35bb13da8e53b01a62b8fe45831134df95bdaffc Mon Sep 17 00:00:00 2001 +From: Carlos Garcia Campos +Date: Tue, 23 Mar 2021 15:59:16 +0100 +Subject: [PATCH 01/11] Add support for building with libsoup3 + +Add soup2 build option, enabled by default. When disabled, it builds +against soup3 instead. +--- + meson.build | 6 ++ + meson_options.txt | 2 + + src/libtracker-sparql/remote/meson.build | 8 ++- + .../remote/tracker-remote.vala | 37 +++++++--- + src/libtracker-sparql/tracker-endpoint-http.c | 68 +++++++++++++++++-- + 5 files changed, 105 insertions(+), 16 deletions(-) + +diff --git a/meson.build b/meson.build +index ce4c62261..12f93d966 100644 +--- a/meson.build ++++ b/meson.build +@@ -52,6 +52,12 @@ libxml2 = dependency('libxml-2.0', version: '> 2.6') + sqlite = dependency('sqlite3', version: '>' + sqlite_required) + dbus = dependency('dbus-1') + ++if get_option('soup2') ++ libsoup = dependency('libsoup-2.4', version: '> 2.40', required: true) ++else ++ libsoup = dependency('libsoup-3.0', version: '>= 2.99.2', required: true) ++endif ++ + libmath = cc.find_library('m', required: false) + + if get_option('man') +diff --git a/meson_options.txt b/meson_options.txt +index 99c569502..46e9c130f 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -26,3 +26,5 @@ option('test_utils_dir', type: 'string', value: '', + description: 'Directory to install trackertestutils Python package (or empty to use the default)') + option('tests_tap_protocol', type: 'boolean', value: false, + description: 'Whether to enable TAP protocol on tests') ++option('soup2', type: 'boolean', value: true, ++ description: 'Whether to build with libsoup2') +diff --git a/src/libtracker-sparql/remote/meson.build b/src/libtracker-sparql/remote/meson.build +index f4d589d61..916b72a8a 100644 +--- a/src/libtracker-sparql/remote/meson.build ++++ b/src/libtracker-sparql/remote/meson.build +@@ -7,13 +7,19 @@ sources = [ + '../../libtracker-common/libtracker-common.vapi' + ] + ++if get_option('soup2') ++ vala_defines = ['--define=SOUP2'] ++else ++ vala_defines = [] ++endif ++ + libtracker_remote = static_library('tracker-remote', sources, + dependencies: tracker_remote_dependencies + [tracker_common_dep, tracker_sparql_intermediate_dep], + c_args: tracker_c_args + [ + '-include', 'config.h', + '-include', 'libtracker-sparql/tracker-private.h', + ], +- vala_args: [ ++ vala_args: vala_defines + [ + '--debug', + '--pkg', 'posix', + # FIXME: Meson has code to add --target-glib automatically, but it +diff --git a/src/libtracker-sparql/remote/tracker-remote.vala b/src/libtracker-sparql/remote/tracker-remote.vala +index 206c237fc..50dc612f3 100644 +--- a/src/libtracker-sparql/remote/tracker-remote.vala ++++ b/src/libtracker-sparql/remote/tracker-remote.vala +@@ -39,7 +39,11 @@ public class Tracker.Remote.Connection : Tracker.Sparql.Connection { + private Soup.Message create_request (string sparql) { + var uri = _base_uri + "?query=" + sparql; + var message = new Soup.Message ("GET", uri); ++#if SOUP2 + var headers = message.request_headers; ++#else ++ var headers = message.get_request_headers(); ++#endif + + headers.append ("User-Agent", USER_AGENT); + headers.append ("Accept", JSON_TYPE); +@@ -48,15 +52,20 @@ public class Tracker.Remote.Connection : Tracker.Sparql.Connection { + return message; + } + +- private Sparql.Cursor create_cursor (Soup.Message message) throws GLib.Error, Sparql.Error { +- string document = (string) message.response_body.flatten ().data; ++ private Sparql.Cursor create_cursor (Soup.Message message, string document) throws GLib.Error, Sparql.Error { ++#if SOUP2 ++ var status_code = message.status_code; ++ var headers = message.response_headers; ++#else ++ var status_code = message.get_status(); ++ var headers = message.get_response_headers(); ++#endif + +- if (message.status_code != Soup.Status.OK) { ++ if (status_code != Soup.Status.OK) { + throw new Sparql.Error.UNSUPPORTED ("Unhandled status code %u, document is: %s", +- message.status_code, document); ++ status_code, document); + } + +- var headers = message.response_headers; + var content_type = headers.get_content_type (null); + long length = document.length; + +@@ -72,20 +81,32 @@ public class Tracker.Remote.Connection : Tracker.Sparql.Connection { + public override Sparql.Cursor query (string sparql, Cancellable? cancellable) throws GLib.Error, Sparql.Error, IOError { + var message = create_request (sparql); + ++#if SOUP2 + _session.send_message (message); ++#else ++ var body = _session.send_and_read (message); ++#endif + + if (cancellable != null && cancellable.is_cancelled ()) + throw new IOError.CANCELLED ("Operation was cancelled"); + +- return create_cursor (message); ++#if SOUP2 ++ return create_cursor (message, (string) message.response_body.flatten ().data); ++#else ++ return create_cursor (message, (string) body.get_data()); ++#endif + } + + public async override Sparql.Cursor query_async (string sparql, Cancellable? cancellable) throws GLib.Error, Sparql.Error, IOError { + var message = create_request (sparql); + ++#if SOUP2 + yield _session.send_async (message, cancellable); +- +- return create_cursor (message); ++ return create_cursor (message, (string) message.response_body.flatten ().data); ++#else ++ var body = yield _session.send_and_read_async (message, GLib.Priority.DEFAULT, cancellable); ++ return create_cursor (message, (string) body.get_data()); ++#endif + } + + public override void close () { +diff --git a/src/libtracker-sparql/tracker-endpoint-http.c b/src/libtracker-sparql/tracker-endpoint-http.c +index de231ca59..5aa82b03d 100644 +--- a/src/libtracker-sparql/tracker-endpoint-http.c ++++ b/src/libtracker-sparql/tracker-endpoint-http.c +@@ -41,7 +41,11 @@ struct _TrackerEndpointHttp { + + typedef struct { + TrackerEndpoint *endpoint; ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ SoupServerMessage *message; ++#else + SoupMessage *message; ++#endif + GInputStream *istream; + GTask *task; + TrackerSerializerFormat format; +@@ -90,9 +94,11 @@ handle_request_in_thread (GTask *task, + GError *error = NULL; + gssize count; + +- g_object_get (request->message, +- "response-body", &message_body, +- NULL); ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ message_body = soup_server_message_get_response_body (request->message); ++#else ++ message_body = request->message->response_body; ++#endif + + while (!finished) { + count = g_input_stream_read (request->istream, +@@ -127,12 +133,22 @@ request_finished_cb (GObject *object, + endpoint_http = TRACKER_ENDPOINT_HTTP (request->endpoint); + + if (!g_task_propagate_boolean (G_TASK (result), &error)) { ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ soup_server_message_set_status (request->message, 500, ++ error ? error->message : ++ "No error message"); ++#else + soup_message_set_status_full (request->message, 500, + error ? error->message : + "No error message"); ++#endif + g_clear_error (&error); + } else { ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ soup_server_message_set_status (request->message, 200, NULL); ++#else + soup_message_set_status (request->message, 200); ++#endif + } + + soup_server_unpause_message (endpoint_http->server, request->message); +@@ -153,7 +169,11 @@ query_async_cb (GObject *object, + cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (object), + result, &error); + if (error) { ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ soup_server_message_set_status (request->message, 500, error->message); ++#else + soup_message_set_status_full (request->message, 500, error->message); ++#endif + soup_server_unpause_message (endpoint_http->server, request->message); + request_free (request); + return; +@@ -167,16 +187,25 @@ query_async_cb (GObject *object, + g_task_run_in_thread (request->task, handle_request_in_thread); + } + ++#if SOUP_CHECK_VERSION (2, 99, 2) ++static gboolean ++pick_format (SoupServerMessage *message, ++ TrackerSerializerFormat *format) ++#else + static gboolean + pick_format (SoupMessage *message, + TrackerSerializerFormat *format) ++#endif + { + SoupMessageHeaders *request_headers, *response_headers; + +- g_object_get (message, +- "request-headers", &request_headers, +- "response-headers", &response_headers, +- NULL); ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ request_headers = soup_server_message_get_request_headers (message); ++ response_headers = soup_server_message_get_response_headers (message); ++#else ++ request_headers = message->request_headers; ++ response_headers = message->response_headers; ++#endif + + if (soup_message_headers_header_contains (request_headers, "Accept", JSON_TYPE)) { + soup_message_headers_set_content_type (response_headers, JSON_TYPE, NULL); +@@ -193,6 +222,14 @@ pick_format (SoupMessage *message, + return FALSE; + } + ++#if SOUP_CHECK_VERSION (2, 99, 2) ++static void ++server_callback (SoupServer *server, ++ SoupServerMessage *message, ++ const char *path, ++ GHashTable *query, ++ gpointer user_data) ++#else + static void + server_callback (SoupServer *server, + SoupMessage *message, +@@ -200,6 +237,7 @@ server_callback (SoupServer *server, + GHashTable *query, + SoupClientContext *client, + gpointer user_data) ++#endif + { + TrackerEndpoint *endpoint = user_data; + TrackerSparqlConnection *conn; +@@ -209,25 +247,41 @@ server_callback (SoupServer *server, + const gchar *sparql; + Request *request; + ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ remote_address = soup_server_message_get_remote_address (message); ++#else + remote_address = soup_client_context_get_remote_address (client); ++#endif + if (remote_address) { + g_signal_emit (endpoint, signals[BLOCK_REMOTE_ADDRESS], 0, + remote_address, &block); + } + + if (block) { ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ soup_server_message_set_status (message, 500, "Remote address disallowed"); ++#else + soup_message_set_status_full (message, 500, "Remote address disallowed"); ++#endif + return; + } + + sparql = g_hash_table_lookup (query, "query"); + if (!sparql) { ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ soup_server_message_set_status (message, 500, "No query given"); ++#else + soup_message_set_status_full (message, 500, "No query given"); ++#endif + return; + } + + if (!pick_format (message, &format)) { ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ soup_server_message_set_status (message, 500, "No recognized accepted formats"); ++#else + soup_message_set_status_full (message, 500, "No recognized accepted formats"); ++#endif + return; + } + +-- +2.38.1 + + +From da2c34a2d7b7cb717360653c627531d04607a4ee Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Wed, 28 Jul 2021 12:53:47 +0200 +Subject: [PATCH 02/11] libtracker-sparql: Allow building against multiple + versions of libsoup + +Tracker is an innocent bystander hindering libsoup3 port, since the +libsoup2/3->tracker->gtk3 dependency chain imposes a libsoup version, +graphical applications using libsoup cannot port at their own pace. + +Make our remote code (connection & endpoint) be a private module +that is built against both versions of libsoup (if found), then we +pick one at runtime, with a preference on libsoup3 if libsoup2 .so +file is not seen in the already loaded libraries. + +This patch should be reverted ASAP, once we can consider libsoup2 +deprecated. + +Fixes: https://gitlab.gnome.org/GNOME/tracker/-/issues/320 +--- + config.h.meson.in | 3 + + meson.build | 24 +++- + meson_options.txt | 2 - + src/libtracker-sparql/meson.build | 60 ++++++++- + src/libtracker-sparql/remote/meson.build | 36 ------ + src/libtracker-sparql/tracker-backend.vala | 5 - + src/libtracker-sparql/tracker-remote-module.c | 115 ++++++++++++++++++ + 7 files changed, 192 insertions(+), 53 deletions(-) + delete mode 100644 src/libtracker-sparql/remote/meson.build + create mode 100644 src/libtracker-sparql/tracker-remote-module.c + +diff --git a/config.h.meson.in b/config.h.meson.in +index 9d1439e91..4aff15f72 100644 +--- a/config.h.meson.in ++++ b/config.h.meson.in +@@ -57,3 +57,6 @@ + + /* Define to the Tracker minor version */ + #mesondefine TRACKER_MINOR_VERSION ++ ++/* Whether RTLD_NOLOAD is defined */ ++#mesondefine HAVE_RTLD_NOLOAD +diff --git a/meson.build b/meson.build +index 12f93d966..3b487835b 100644 +--- a/meson.build ++++ b/meson.build +@@ -51,14 +51,23 @@ libsoup = dependency('libsoup-2.4', version: '> 2.40', required: true) + libxml2 = dependency('libxml-2.0', version: '> 2.6') + sqlite = dependency('sqlite3', version: '>' + sqlite_required) + dbus = dependency('dbus-1') ++libsoup2 = dependency('libsoup-2.4', version: '> 2.40', required: false) ++libsoup3 = dependency('libsoup-3.0', version: '>= 2.99.2', required: false) + +-if get_option('soup2') +- libsoup = dependency('libsoup-2.4', version: '> 2.40', required: true) +-else +- libsoup = dependency('libsoup-3.0', version: '>= 2.99.2', required: true) ++libmath = cc.find_library('m', required: false) ++libdl = cc.find_library('dl') ++ ++soup_backends = '' ++if libsoup2.found() ++ soup_backends = soup_backends + '2.x ' ++endif ++if libsoup3.found() ++ soup_backends = soup_backends + '3.x ' + endif + +-libmath = cc.find_library('m', required: false) ++if not libsoup2.found() and not libsoup3.found() ++ error('At least one of libsoup2 or libsoup3 is required') ++endif + + if get_option('man') + asciidoc = find_program('asciidoc') +@@ -272,6 +281,10 @@ conf.set('TRACKER_MICRO_VERSION', tracker_micro_version) + conf.set('TRACKER_INTERFACE_AGE', 0) + conf.set('TRACKER_BINARY_AGE', 100 * tracker_minor_version + tracker_micro_version) + ++# Check for RTLD_NOLOAD ++have_rtld_noload = cc.has_header_symbol('dlfcn.h', 'RTLD_NOLOAD') ++conf.set('HAVE_RTLD_NOLOAD', have_rtld_noload) ++ + # Config that goes in some other generated files (.desktop, .service, etc) + conf.set('abs_top_builddir', meson.current_build_dir()) + conf.set('libexecdir', join_paths(get_option('prefix'), get_option('libexecdir'))) +@@ -362,6 +375,7 @@ summary = [ + ' Unicode support library: ' + unicode_library_name, + ' Use external FTS module: ' + (not sqlite3_has_builtin_fts5).to_string(), + ' Build with Stemming support: ' + have_libstemmer.to_string(), ++ ' Libsoup backends: ' + soup_backends, + ] + + if get_option('bash_completion') +diff --git a/meson_options.txt b/meson_options.txt +index 46e9c130f..99c569502 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -26,5 +26,3 @@ option('test_utils_dir', type: 'string', value: '', + description: 'Directory to install trackertestutils Python package (or empty to use the default)') + option('tests_tap_protocol', type: 'boolean', value: false, + description: 'Whether to enable TAP protocol on tests') +-option('soup2', type: 'boolean', value: true, +- description: 'Whether to build with libsoup2') +diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build +index 570f4b83b..87b8167cf 100644 +--- a/src/libtracker-sparql/meson.build ++++ b/src/libtracker-sparql/meson.build +@@ -20,7 +20,6 @@ libtracker_sparql_c_sources = files( + 'tracker-cursor.c', + 'tracker-endpoint.c', + 'tracker-endpoint-dbus.c', +- 'tracker-endpoint-http.c', + 'tracker-error.c', + 'tracker-namespace-manager.c', + 'tracker-notifier.c', +@@ -54,7 +53,7 @@ libtracker_sparql_c_public_headers = files( + libtracker_sparql_intermediate = static_library('tracker-sparql-intermediate', + enum_types, + libtracker_sparql_c_sources, +- dependencies: [tracker_common_dep, json_glib, libxml2, libsoup], ++ dependencies: [tracker_common_dep, json_glib, libxml2], + gnu_symbol_visibility: 'hidden', + ) + +@@ -63,7 +62,7 @@ sparqlinc = [include_directories('.'), meson.current_build_dir()] + tracker_sparql_intermediate_dep = declare_dependency( + link_with: [libtracker_sparql_intermediate], + include_directories: [srcinc, include_directories('.')], +- dependencies: [ tracker_sparql_vapi_dep ], ++ dependencies: [ tracker_sparql_vapi_dep, tracker_data_dep ], + sources: enum_types[1], + ) + +@@ -88,13 +87,61 @@ install_data( + + subdir('bus') + subdir('direct') +-subdir('remote') ++ ++tracker_remote_dependencies = [json_glib, libxml2] ++ ++remote_sources = [ ++ 'tracker-endpoint-http.c', ++ 'remote/tracker-json-cursor.vala', ++ 'remote/tracker-xml-cursor.vala', ++ 'remote/tracker-remote.vala', ++] ++ ++if libsoup2.found() ++ libtracker_remote_soup2 = shared_module('tracker-remote-soup2', remote_sources, ++ dependencies: tracker_remote_dependencies + [tracker_common_dep, tracker_sparql_intermediate_dep, libsoup2], ++ c_args: tracker_c_args + [ ++ '-include', 'config.h', ++ '-include', 'libtracker-sparql/tracker-private.h', ++ ], ++ vala_args: [ ++ '--debug', ++ '--pkg', 'posix', ++ # FIXME: Meson has code to add --target-glib automatically, but it ++ # doesn't seem to work here. ++ '--target-glib', glib_required, ++ '--define=SOUP2', ++ ], ++ install: true, ++ install_dir: tracker_internal_libs_dir, ++ ) ++endif ++ ++if libsoup3.found() ++ libtracker_remote_soup3 = shared_module('tracker-remote-soup3', remote_sources, ++ dependencies: tracker_remote_dependencies + [tracker_common_dep, tracker_sparql_intermediate_dep, libsoup3], ++ c_args: tracker_c_args + [ ++ '-include', 'config.h', ++ '-include', 'libtracker-sparql/tracker-private.h', ++ ], ++ vala_args: [ ++ '--debug', ++ '--pkg', 'posix', ++ # FIXME: Meson has code to add --target-glib automatically, but it ++ # doesn't seem to work here. ++ '--target-glib', glib_required, ++ ], ++ install: true, ++ install_dir: tracker_internal_libs_dir, ++ ) ++endif + + libtracker_sparql = library('tracker-sparql-' + tracker_api_version, + '../libtracker-common/libtracker-common.vapi', + '../libtracker-data/libtracker-data.vapi', + 'direct/tracker-direct.vapi', + 'tracker-backend.vala', ++ 'tracker-remote-module.c', + tracker_gresources, + + gnu_symbol_visibility: 'hidden', +@@ -107,11 +154,14 @@ libtracker_sparql = library('tracker-sparql-' + tracker_api_version, + + c_args: [ + '-include', 'libtracker-sparql/tracker-private.h', ++ '-DPRIVATE_LIBDIR="@0@"'.format(tracker_internal_libs_dir), ++ '-DBUILD_LIBDIR="@0@"'.format(meson.current_build_dir()), ++ '-DBUILDROOT="@0@"'.format(meson.project_build_root()), + ], + + link_whole: [libtracker_sparql_intermediate], + +- dependencies: [tracker_common_dep, tracker_sparql_remote_dep, tracker_sparql_bus_dep, tracker_sparql_direct_dep, tracker_sparql_vapi_dep], ++ dependencies: [tracker_common_dep, tracker_sparql_bus_dep, tracker_sparql_direct_dep, tracker_sparql_vapi_dep, gmodule, libdl], + ) + + tracker_sparql_dep = declare_dependency( +diff --git a/src/libtracker-sparql/remote/meson.build b/src/libtracker-sparql/remote/meson.build +deleted file mode 100644 +index 916b72a8a..000000000 +--- a/src/libtracker-sparql/remote/meson.build ++++ /dev/null +@@ -1,36 +0,0 @@ +-tracker_remote_dependencies = [json_glib, libsoup, libxml2] +- +-sources = [ +- 'tracker-json-cursor.vala', +- 'tracker-xml-cursor.vala', +- 'tracker-remote.vala', +- '../../libtracker-common/libtracker-common.vapi' +-] +- +-if get_option('soup2') +- vala_defines = ['--define=SOUP2'] +-else +- vala_defines = [] +-endif +- +-libtracker_remote = static_library('tracker-remote', sources, +- dependencies: tracker_remote_dependencies + [tracker_common_dep, tracker_sparql_intermediate_dep], +- c_args: tracker_c_args + [ +- '-include', 'config.h', +- '-include', 'libtracker-sparql/tracker-private.h', +- ], +- vala_args: vala_defines + [ +- '--debug', +- '--pkg', 'posix', +- # FIXME: Meson has code to add --target-glib automatically, but it +- # doesn't seem to work here. +- '--target-glib', glib_required, +- ], +- gnu_symbol_visibility: 'hidden', +-) +- +-tracker_sparql_remote_dep = declare_dependency( +- link_with: libtracker_remote, +- include_directories: include_directories('.'), +- dependencies: tracker_remote_dependencies, +-) +diff --git a/src/libtracker-sparql/tracker-backend.vala b/src/libtracker-sparql/tracker-backend.vala +index ae6313118..af1102d5a 100644 +--- a/src/libtracker-sparql/tracker-backend.vala ++++ b/src/libtracker-sparql/tracker-backend.vala +@@ -22,11 +22,6 @@ + * effect of printing the 'help' message if TRACKER_DEBUG=help is set. + */ + +-public static Tracker.Sparql.Connection tracker_sparql_connection_remote_new (string url_base) { +- Tracker.get_debug_flags (); +- return new Tracker.Remote.Connection (url_base); +-} +- + public static Tracker.Sparql.Connection tracker_sparql_connection_bus_new (string service, string? object_path, DBusConnection? conn) throws Tracker.Sparql.Error, IOError, DBusError, GLib.Error { + Tracker.get_debug_flags (); + +diff --git a/src/libtracker-sparql/tracker-remote-module.c b/src/libtracker-sparql/tracker-remote-module.c +new file mode 100644 +index 000000000..2ca0fd181 +--- /dev/null ++++ b/src/libtracker-sparql/tracker-remote-module.c +@@ -0,0 +1,115 @@ ++/* Yuck */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#define LIBSOUP_2_SONAME "libsoup-2.4.so.1" ++ ++static gboolean initialized = FALSE; ++ ++GType (* remote_endpoint_get_type) (void) = NULL; ++ ++TrackerEndpoint * (* remote_endpoint_new) (TrackerSparqlConnection *sparql_connection, ++ guint port, ++ GTlsCertificate *certificate, ++ GCancellable *cancellable, ++ GError **error) = NULL; ++TrackerSparqlConnection * (* remote_connection_new) (const gchar *url_base) = NULL; ++ ++static void ++tracker_init_remote (void) ++{ ++ const char *modules[3] = { 0 }; ++ gpointer handle = NULL; ++ gint i = 0; ++ ++ if (initialized) ++ return; ++ ++ g_assert (g_module_supported ()); ++ ++#ifdef HAVE_RTLD_NOLOAD ++ if ((handle = dlopen (LIBSOUP_2_SONAME, RTLD_NOW | RTLD_NOLOAD))) { ++ /* Force load of soup2 module */ ++ modules[0] = "libtracker-remote-soup2.so"; ++ } else ++#endif ++ { ++ modules[0] = "libtracker-remote-soup3.so"; ++ modules[1] = "libtracker-remote-soup2.so"; ++ } ++ ++ g_clear_pointer (&handle, dlclose); ++ ++ for (i = 0; modules[i]; i++) { ++ GModule *remote_module; ++ gchar *module_path; ++ ++ if (g_strcmp0 (g_get_current_dir (), BUILDROOT) == 0) { ++ /* Detect in-build runtime of this code, this may happen ++ * building introspection information or running tests. ++ * We want the in-tree modules to be loaded then. ++ */ ++ module_path = g_strdup_printf (BUILD_LIBDIR "/%s", modules[i]); ++ } else { ++ module_path = g_strdup_printf (PRIVATE_LIBDIR "/%s", modules[i]); ++ } ++ ++ remote_module = g_module_open (module_path, ++ G_MODULE_BIND_LAZY | ++ G_MODULE_BIND_LOCAL); ++ g_free (module_path); ++ ++ if (!remote_module) ++ continue; ++ ++ if (!g_module_symbol (remote_module, "tracker_endpoint_http_get_type", (gpointer *) &remote_endpoint_get_type) || ++ !g_module_symbol (remote_module, "tracker_endpoint_http_new", (gpointer *) &remote_endpoint_new) || ++ !g_module_symbol (remote_module, "tracker_remote_connection_new", (gpointer *) &remote_connection_new)) { ++ g_clear_pointer (&remote_module, g_module_close); ++ continue; ++ } ++ ++ g_module_make_resident (remote_module); ++ g_module_close (remote_module); ++ initialized = TRUE; ++ return; ++ } ++ ++ g_assert_not_reached (); ++} ++ ++GType ++tracker_endpoint_http_get_type (void) ++{ ++ tracker_init_remote (); ++ ++ return remote_endpoint_get_type (); ++} ++ ++TrackerEndpointHttp * ++tracker_endpoint_http_new (TrackerSparqlConnection *sparql_connection, ++ guint port, ++ GTlsCertificate *certificate, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ tracker_init_remote (); ++ ++ return (TrackerEndpointHttp *) remote_endpoint_new (sparql_connection, ++ port, ++ certificate, ++ cancellable, ++ error); ++} ++ ++TrackerSparqlConnection * ++tracker_sparql_connection_remote_new (const gchar *url_base) ++{ ++ tracker_init_remote (); ++ ++ return remote_connection_new (url_base); ++} +-- +2.38.1 + + +From 52324bd629a5446594717a2e782a85913821b447 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Wed, 10 Nov 2021 23:16:37 +0200 +Subject: [PATCH 03/11] libtracker-sparql: Escape query in remote connection + +This is embedded in the URI, so should get proper escaping to avoid +misinterpretation with certain characters. +--- + src/libtracker-sparql/remote/tracker-remote.vala | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/libtracker-sparql/remote/tracker-remote.vala b/src/libtracker-sparql/remote/tracker-remote.vala +index 50dc612f3..252d70a40 100644 +--- a/src/libtracker-sparql/remote/tracker-remote.vala ++++ b/src/libtracker-sparql/remote/tracker-remote.vala +@@ -37,7 +37,7 @@ public class Tracker.Remote.Connection : Tracker.Sparql.Connection { + } + + private Soup.Message create_request (string sparql) { +- var uri = _base_uri + "?query=" + sparql; ++ var uri = _base_uri + "?query=" + GLib.Uri.escape_string (sparql, null, false); + var message = new Soup.Message ("GET", uri); + #if SOUP2 + var headers = message.request_headers; +-- +2.38.1 + + +From 68592b8a44305937134d94dfc3dfdf8444485f7b Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Wed, 10 Nov 2021 23:28:11 +0200 +Subject: [PATCH 04/11] libtracker-data: Refactor some soup2/3 code handling + +Remove the need for one ifdef, and make a consistent handling of empty +responses. +--- + src/libtracker-sparql/remote/tracker-remote.vala | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/src/libtracker-sparql/remote/tracker-remote.vala b/src/libtracker-sparql/remote/tracker-remote.vala +index 252d70a40..f29a989ad 100644 +--- a/src/libtracker-sparql/remote/tracker-remote.vala ++++ b/src/libtracker-sparql/remote/tracker-remote.vala +@@ -83,18 +83,19 @@ public class Tracker.Remote.Connection : Tracker.Sparql.Connection { + + #if SOUP2 + _session.send_message (message); ++ var data = (string) message.response_body.flatten ().data; + #else +- var body = _session.send_and_read (message); ++ var body = _session.send_and_read (message); ++ var data = (string) body.get_data(); + #endif + ++ if (data == null || data == "") ++ throw new Sparql.Error.UNSUPPORTED ("Empty response"); ++ + if (cancellable != null && cancellable.is_cancelled ()) + throw new IOError.CANCELLED ("Operation was cancelled"); + +-#if SOUP2 +- return create_cursor (message, (string) message.response_body.flatten ().data); +-#else +- return create_cursor (message, (string) body.get_data()); +-#endif ++ return create_cursor (message, (string) data); + } + + public async override Sparql.Cursor query_async (string sparql, Cancellable? cancellable) throws GLib.Error, Sparql.Error, IOError { +-- +2.38.1 + + +From 6f3b7bc85b715d1071cfb39ff3f10ecc96395ee8 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Wed, 10 Nov 2021 23:43:50 +0200 +Subject: [PATCH 05/11] libtracker-data: Add remote TrackerSparqlStatement + implementation + +Implement this object for remote connections to increase API consistency. +Underneath, our very own SPARQL parser is used to do raw string replacements +on the actual query being sent. + +Besides API consistence, this also has the benefit to make ~var bindings +work with other SPARQL endpoints that are not Tracker's. +--- + src/libtracker-sparql/meson.build | 8 +- + .../remote/tracker-remote-statement.c | 401 ++++++++++++++++++ + .../remote/tracker-remote-statement.h | 32 ++ + .../remote/tracker-remote.vala | 4 + + .../remote/tracker-remote.vapi | 6 + + 5 files changed, 450 insertions(+), 1 deletion(-) + create mode 100644 src/libtracker-sparql/remote/tracker-remote-statement.c + create mode 100644 src/libtracker-sparql/remote/tracker-remote-statement.h + create mode 100644 src/libtracker-sparql/remote/tracker-remote.vapi + +diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build +index 87b8167cf..21b9b5a28 100644 +--- a/src/libtracker-sparql/meson.build ++++ b/src/libtracker-sparql/meson.build +@@ -90,8 +90,14 @@ subdir('direct') + + tracker_remote_dependencies = [json_glib, libxml2] + +-remote_sources = [ ++libtracker_sparql_remote_c_sources = files ( + 'tracker-endpoint-http.c', ++ 'remote/tracker-remote-statement.c', ++) ++ ++remote_sources = [ ++ libtracker_sparql_remote_c_sources, ++ 'remote/tracker-remote.vapi', + 'remote/tracker-json-cursor.vala', + 'remote/tracker-xml-cursor.vala', + 'remote/tracker-remote.vala', +diff --git a/src/libtracker-sparql/remote/tracker-remote-statement.c b/src/libtracker-sparql/remote/tracker-remote-statement.c +new file mode 100644 +index 000000000..6ca9f5246 +--- /dev/null ++++ b/src/libtracker-sparql/remote/tracker-remote-statement.c +@@ -0,0 +1,401 @@ ++/* ++ * Copyright (C) 2021, Red Hat Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ * Author: Carlos Garnacho ++ */ ++ ++#include "config.h" ++ ++#include "tracker-remote-statement.h" ++ ++#include ++#include ++#include ++#include ++ ++struct _TrackerRemoteStatement ++{ ++ TrackerSparqlStatement parent_instance; ++ TrackerNodeTree *parser_tree; ++ GHashTable *bindings; ++}; ++ ++G_DEFINE_TYPE (TrackerRemoteStatement, ++ tracker_remote_statement, ++ TRACKER_TYPE_SPARQL_STATEMENT) ++ ++static void ++tracker_remote_statement_finalize (GObject *object) ++{ ++ TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (object); ++ ++ if (remote_stmt->parser_tree) ++ tracker_node_tree_free (remote_stmt->parser_tree); ++ g_hash_table_unref (remote_stmt->bindings); ++ ++ G_OBJECT_CLASS (tracker_remote_statement_parent_class)->finalize (object); ++} ++ ++static void ++tracker_remote_statement_bind_int (TrackerSparqlStatement *stmt, ++ const gchar *name, ++ gint64 value) ++{ ++ TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); ++ GValue *val; ++ ++ val = g_new0 (GValue, 1); ++ g_value_init (val, G_TYPE_INT64); ++ g_value_set_int64 (val, value); ++ ++ g_hash_table_insert (remote_stmt->bindings, ++ g_strdup (name), ++ val); ++} ++ ++static void ++tracker_remote_statement_bind_boolean (TrackerSparqlStatement *stmt, ++ const gchar *name, ++ gboolean value) ++{ ++ TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); ++ GValue *val; ++ ++ val = g_new0 (GValue, 1); ++ g_value_init (val, G_TYPE_BOOLEAN); ++ g_value_set_boolean (val, value); ++ ++ g_hash_table_insert (remote_stmt->bindings, ++ g_strdup (name), ++ val); ++} ++ ++static void ++tracker_remote_statement_bind_string (TrackerSparqlStatement *stmt, ++ const gchar *name, ++ const gchar *value) ++{ ++ TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); ++ GValue *val; ++ ++ val = g_new0 (GValue, 1); ++ g_value_init (val, G_TYPE_STRING); ++ g_value_set_string (val, value); ++ ++ g_hash_table_insert (remote_stmt->bindings, ++ g_strdup (name), ++ val); ++} ++ ++static void ++tracker_remote_statement_bind_double (TrackerSparqlStatement *stmt, ++ const gchar *name, ++ gdouble value) ++{ ++ TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); ++ GValue *val; ++ ++ val = g_new0 (GValue, 1); ++ g_value_init (val, G_TYPE_DOUBLE); ++ g_value_set_double (val, value); ++ ++ g_hash_table_insert (remote_stmt->bindings, ++ g_strdup (name), ++ val); ++} ++ ++static void ++append_gvalue (GString *str, ++ const GValue *value) ++{ ++ if (G_VALUE_HOLDS_BOOLEAN (value)) { ++ g_string_append_printf (str, "%s", ++ g_value_get_boolean (value) ? ++ "true" : "false"); ++ } else if (G_VALUE_HOLDS_INT64 (value)) { ++ g_string_append_printf (str, "%" G_GINT64_FORMAT, ++ g_value_get_int64 (value)); ++ } else if (G_VALUE_HOLDS_DOUBLE (value)) { ++ gchar buf[G_ASCII_DTOSTR_BUF_SIZE + 1]; ++ ++ g_ascii_dtostr (buf, sizeof (buf), g_value_get_double (value)); ++ g_string_append (str, buf); ++ } else if (G_VALUE_TYPE (value) == G_TYPE_DATE_TIME) { ++ GDateTime *datetime; ++ gchar *datetime_str; ++ ++ datetime = g_value_get_boxed (value); ++ datetime_str = tracker_date_format_iso8601 (datetime); ++ g_string_append_printf (str, "\"%s\"", datetime_str); ++ g_free (datetime_str); ++ } else if (G_VALUE_HOLDS_STRING (value)) { ++ const gchar *val = g_value_get_string (value); ++ int len = strlen (val); ++ gchar *end; ++ gboolean is_number = FALSE; ++ ++ /* Try to detect numbers anyway, since we use to allow ++ * loose typing in other connection types. ++ */ ++ g_ascii_strtoll (val, &end, 10); ++ is_number = (end == &val[len]); ++ ++ if (!is_number) { ++ g_ascii_strtod (val, &end); ++ is_number = (end == &val[len]); ++ } ++ ++ if (is_number) ++ g_string_append (str, val); ++ else ++ g_string_append_printf (str, "\"%s\"", val); ++ } ++} ++ ++static gchar * ++apply_bindings (TrackerSparqlStatement *stmt, ++ GHashTable *bindings, ++ GError **error) ++{ ++ TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); ++ const gchar *query = tracker_sparql_statement_get_sparql (stmt); ++ GString *str = g_string_new (NULL); ++ TrackerParserNode *node = tracker_node_tree_get_root (remote_stmt->parser_tree); ++ ++ for (node = tracker_sparql_parser_tree_find_first (node, TRUE); ++ node; ++ node = tracker_sparql_parser_tree_find_next (node, TRUE)) { ++ const TrackerGrammarRule *rule; ++ gssize start, end; ++ ++ if (!tracker_parser_node_get_extents (node, &start, &end)) { ++ /* Skip over 0-len nodes */ ++ continue; ++ } ++ ++ rule = tracker_parser_node_get_rule (node); ++ ++ if (tracker_grammar_rule_is_a (rule, RULE_TYPE_TERMINAL, ++ TERMINAL_TYPE_PARAMETERIZED_VAR)) { ++ gchar *param_name; ++ const GValue *value; ++ ++ param_name = g_strndup (&query[start], end - start); ++ value = g_hash_table_lookup (bindings, ¶m_name[1]); ++ if (!value) { ++ g_set_error (error, ++ TRACKER_SPARQL_ERROR, ++ TRACKER_SPARQL_ERROR_PARSE, ++ "No binding found for variable %s", ++ param_name); ++ g_string_free (str, TRUE); ++ g_free (param_name); ++ return NULL; ++ } ++ ++ append_gvalue (str, value); ++ g_free (param_name); ++ } else { ++ g_string_append_len (str, ++ &query[start], ++ end - start); ++ } ++ ++ g_string_append_c (str, ' '); ++ } ++ ++ return g_string_free (str, FALSE); ++} ++ ++static TrackerSparqlCursor * ++execute_statement (TrackerSparqlStatement *stmt, ++ GHashTable *bindings, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ TrackerSparqlCursor *cursor; ++ gchar *rewritten_query = NULL; ++ ++ if (g_hash_table_size (bindings) > 0) { ++ rewritten_query = apply_bindings (stmt, bindings, error); ++ if (!rewritten_query) ++ return NULL; ++ } ++ ++ cursor = tracker_sparql_connection_query (tracker_sparql_statement_get_connection (stmt), ++ rewritten_query ? rewritten_query : ++ tracker_sparql_statement_get_sparql (stmt), ++ cancellable, ++ error); ++ g_free (rewritten_query); ++ ++ return cursor; ++} ++ ++TrackerSparqlCursor * ++tracker_remote_statement_execute (TrackerSparqlStatement *stmt, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); ++ ++ return execute_statement (stmt, remote_stmt->bindings, cancellable, error); ++} ++ ++static void ++execute_in_thread (GTask *task, ++ gpointer object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ TrackerSparqlCursor *cursor; ++ GHashTable *bindings = task_data; ++ GError *error = NULL; ++ ++ if (g_task_return_error_if_cancelled (task)) ++ return; ++ ++ cursor = execute_statement (object, ++ bindings, ++ g_task_get_cancellable (task), ++ &error); ++ if (error) ++ g_task_return_error (task, error); ++ else ++ g_task_return_pointer (task, cursor, g_object_unref); ++ ++ g_object_unref (task); ++} ++ ++static void ++free_gvalue (gpointer data) ++{ ++ g_value_unset (data); ++ g_free (data); ++} ++ ++static GHashTable * ++create_bindings_ht (void) ++{ ++ return g_hash_table_new_full (g_str_hash, g_str_equal, ++ g_free, free_gvalue); ++} ++ ++static GHashTable * ++copy_values_deep (GHashTable *values) ++{ ++ GHashTable *copy; ++ GHashTableIter iter; ++ gpointer key, val; ++ ++ copy = create_bindings_ht (); ++ g_hash_table_iter_init (&iter, values); ++ ++ while (g_hash_table_iter_next (&iter, &key, &val)) { ++ GValue *copy_value; ++ ++ copy_value = g_new0 (GValue, 1); ++ g_value_init (copy_value, G_VALUE_TYPE (val)); ++ g_value_copy (val, copy_value); ++ ++ g_hash_table_insert (copy, g_strdup (key), copy_value); ++ } ++ ++ return copy; ++} ++ ++void ++tracker_remote_statement_execute_async (TrackerSparqlStatement *stmt, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); ++ GHashTable *bindings; ++ GTask *task; ++ ++ bindings = copy_values_deep (remote_stmt->bindings); ++ ++ task = g_task_new (stmt, cancellable, callback, user_data); ++ g_task_set_task_data (task, bindings, (GDestroyNotify) g_hash_table_unref); ++ g_task_run_in_thread (task, execute_in_thread); ++} ++ ++TrackerSparqlCursor * ++tracker_remote_statement_execute_finish (TrackerSparqlStatement *stmt, ++ GAsyncResult *res, ++ GError **error) ++{ ++ return g_task_propagate_pointer (G_TASK (res), error); ++} ++ ++void ++tracker_remote_statement_clear_bindings (TrackerSparqlStatement *stmt) ++{ ++ TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); ++ ++ g_hash_table_remove_all (remote_stmt->bindings); ++} ++ ++static void ++tracker_remote_statement_class_init (TrackerRemoteStatementClass *klass) ++{ ++ TrackerSparqlStatementClass *stmt_class = TRACKER_SPARQL_STATEMENT_CLASS (klass); ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->finalize = tracker_remote_statement_finalize; ++ ++ stmt_class->bind_int = tracker_remote_statement_bind_int; ++ stmt_class->bind_boolean = tracker_remote_statement_bind_boolean; ++ stmt_class->bind_string = tracker_remote_statement_bind_string; ++ stmt_class->bind_double = tracker_remote_statement_bind_double; ++ stmt_class->execute = tracker_remote_statement_execute; ++ stmt_class->execute_async = tracker_remote_statement_execute_async; ++ stmt_class->execute_finish = tracker_remote_statement_execute_finish; ++ stmt_class->clear_bindings = tracker_remote_statement_clear_bindings; ++} ++ ++static void ++tracker_remote_statement_init (TrackerRemoteStatement *stmt) ++{ ++ stmt->bindings = create_bindings_ht (); ++} ++ ++TrackerSparqlStatement * ++tracker_remote_statement_new (TrackerSparqlConnection *conn, ++ const gchar *query, ++ GError **error) ++{ ++ TrackerRemoteStatement *remote_stmt; ++ TrackerSparqlStatement *stmt; ++ ++ stmt = g_object_new (TRACKER_TYPE_REMOTE_STATEMENT, ++ "connection", conn, ++ "sparql", query, ++ NULL); ++ remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); ++ remote_stmt->parser_tree = ++ tracker_sparql_parse_query (tracker_sparql_statement_get_sparql (stmt), ++ -1, NULL, error); ++ if (!remote_stmt->parser_tree) { ++ g_object_unref (stmt); ++ return NULL; ++ } ++ ++ return stmt; ++} +diff --git a/src/libtracker-sparql/remote/tracker-remote-statement.h b/src/libtracker-sparql/remote/tracker-remote-statement.h +new file mode 100644 +index 000000000..1cedf3811 +--- /dev/null ++++ b/src/libtracker-sparql/remote/tracker-remote-statement.h +@@ -0,0 +1,32 @@ ++/* ++ * Copyright (C) 2021, Red Hat Ltd. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ * Author: Carlos Garnacho ++ */ ++ ++#include ++ ++#define TRACKER_TYPE_REMOTE_STATEMENT (tracker_remote_statement_get_type ()) ++G_DECLARE_FINAL_TYPE (TrackerRemoteStatement, ++ tracker_remote_statement, ++ TRACKER, REMOTE_STATEMENT, ++ TrackerSparqlStatement) ++ ++TrackerSparqlStatement * tracker_remote_statement_new (TrackerSparqlConnection *conn, ++ const gchar *query, ++ GError **error); +diff --git a/src/libtracker-sparql/remote/tracker-remote.vala b/src/libtracker-sparql/remote/tracker-remote.vala +index f29a989ad..f3be3147a 100644 +--- a/src/libtracker-sparql/remote/tracker-remote.vala ++++ b/src/libtracker-sparql/remote/tracker-remote.vala +@@ -110,6 +110,10 @@ public class Tracker.Remote.Connection : Tracker.Sparql.Connection { + #endif + } + ++ public override Sparql.Statement? query_statement (string sparql, GLib.Cancellable? cancellable = null) throws Sparql.Error { ++ return new Remote.Statement (this, sparql); ++ } ++ + public override void close () { + } + +diff --git a/src/libtracker-sparql/remote/tracker-remote.vapi b/src/libtracker-sparql/remote/tracker-remote.vapi +new file mode 100644 +index 000000000..caf018b41 +--- /dev/null ++++ b/src/libtracker-sparql/remote/tracker-remote.vapi +@@ -0,0 +1,6 @@ ++namespace Tracker { ++ [CCode (cheader_filename = "libtracker-sparql/remote/tracker-remote-statement.h")] ++ class Remote.Statement : Sparql.Statement { ++ public Statement (Sparql.Connection conn, string query) throws Sparql.Error; ++ } ++} +-- +2.38.1 + + +From 04f2b77b5c1958db2c2de6d54d716ef20c0b59dc Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Sat, 27 Nov 2021 14:54:51 +0100 +Subject: [PATCH 06/11] libtracker-sparql: Add missing "static" in functions + +Luckily, these symbols won't be leaked, still not great. +--- + src/libtracker-sparql/remote/tracker-remote-statement.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/libtracker-sparql/remote/tracker-remote-statement.c b/src/libtracker-sparql/remote/tracker-remote-statement.c +index 6ca9f5246..ad80a589b 100644 +--- a/src/libtracker-sparql/remote/tracker-remote-statement.c ++++ b/src/libtracker-sparql/remote/tracker-remote-statement.c +@@ -319,7 +319,7 @@ copy_values_deep (GHashTable *values) + return copy; + } + +-void ++static void + tracker_remote_statement_execute_async (TrackerSparqlStatement *stmt, + GCancellable *cancellable, + GAsyncReadyCallback callback, +@@ -336,7 +336,7 @@ tracker_remote_statement_execute_async (TrackerSparqlStatement *stmt, + g_task_run_in_thread (task, execute_in_thread); + } + +-TrackerSparqlCursor * ++static TrackerSparqlCursor * + tracker_remote_statement_execute_finish (TrackerSparqlStatement *stmt, + GAsyncResult *res, + GError **error) +@@ -344,7 +344,7 @@ tracker_remote_statement_execute_finish (TrackerSparqlStatement *stmt, + return g_task_propagate_pointer (G_TASK (res), error); + } + +-void ++static void + tracker_remote_statement_clear_bindings (TrackerSparqlStatement *stmt) + { + TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); +-- +2.38.1 + + +From 663e1784cfaab1d00eaf1c30e6549faa52e2f96f Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Sat, 23 Apr 2022 13:22:54 +0200 +Subject: [PATCH 07/11] libtracker-sparql: Move TrackerSerializerFormat enum to + distinct header + +We want this enum to be used from places that do not necessarily pull +TrackerSerializer directly, or other internals. Move it to a separate +header so that the enum can be used without further dependencies. +--- + src/libtracker-sparql/tracker-enums-private.h | 32 +++++++++++++++++++ + src/libtracker-sparql/tracker-private.h | 1 + + src/libtracker-sparql/tracker-serializer.h | 7 +--- + 3 files changed, 34 insertions(+), 6 deletions(-) + create mode 100644 src/libtracker-sparql/tracker-enums-private.h + +diff --git a/src/libtracker-sparql/tracker-enums-private.h b/src/libtracker-sparql/tracker-enums-private.h +new file mode 100644 +index 000000000..1d193b277 +--- /dev/null ++++ b/src/libtracker-sparql/tracker-enums-private.h +@@ -0,0 +1,32 @@ ++/* ++ * Copyright (C) 2022, Red Hat, Inc ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ * Author: Carlos Garnacho ++ */ ++ ++#ifndef TRACKER_ENUMS_PRIVATE_H ++#define TRACKER_ENUMS_PRIVATE_H ++ ++typedef enum ++{ ++ TRACKER_SERIALIZER_FORMAT_JSON, /* application/sparql-results+json */ ++ TRACKER_SERIALIZER_FORMAT_XML, /* application/sparql-results+xml */ ++ TRACKER_N_SERIALIZER_FORMATS ++} TrackerSerializerFormat; ++ ++#endif /* TRACKER_ENUMS_PRIVATE_H */ +diff --git a/src/libtracker-sparql/tracker-private.h b/src/libtracker-sparql/tracker-private.h +index b8c6381ec..c95568b45 100644 +--- a/src/libtracker-sparql/tracker-private.h ++++ b/src/libtracker-sparql/tracker-private.h +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + typedef struct _TrackerSparqlConnectionClass TrackerSparqlConnectionClass; + +diff --git a/src/libtracker-sparql/tracker-serializer.h b/src/libtracker-sparql/tracker-serializer.h +index f948858bb..704fea06a 100644 +--- a/src/libtracker-sparql/tracker-serializer.h ++++ b/src/libtracker-sparql/tracker-serializer.h +@@ -23,6 +23,7 @@ + #define TRACKER_SERIALIZER_H + + #include ++#include + + #define TRACKER_TYPE_SERIALIZER (tracker_serializer_get_type()) + +@@ -31,12 +32,6 @@ G_DECLARE_DERIVABLE_TYPE (TrackerSerializer, + TRACKER, SERIALIZER, + GInputStream); + +-typedef enum +-{ +- TRACKER_SERIALIZER_FORMAT_JSON, /* application/sparql-results+json */ +- TRACKER_SERIALIZER_FORMAT_XML, /* application/sparql-results+xml */ +-} TrackerSerializerFormat; +- + GInputStream * tracker_serializer_new (TrackerSparqlCursor *cursor, + TrackerSerializerFormat format); + +-- +2.38.1 + + +From 477bc7f3bcabf213d3887e5d6312ef02e1025b01 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Sat, 23 Apr 2022 13:41:06 +0200 +Subject: [PATCH 08/11] libtracker-sparql: Add base http helper object + definitions + +This is a small internal API consisting of a TrackerHttpClient and +a TrackerHttpServer abstract objects. These are meant to wrap the +interaction with libsoup in a way that is most isolated from internal +library workings. + +These objects will have final implementations living in modules, but +also will be interacted through the base API from the library code, +this so far adds the latter. +--- + src/libtracker-sparql/meson.build | 3 + + src/libtracker-sparql/remote/meson.build | 3 + + src/libtracker-sparql/remote/tracker-http.c | 334 ++++++++++++++++++++ + src/libtracker-sparql/remote/tracker-http.h | 122 +++++++ + 4 files changed, 462 insertions(+) + create mode 100644 src/libtracker-sparql/remote/meson.build + create mode 100644 src/libtracker-sparql/remote/tracker-http.c + create mode 100644 src/libtracker-sparql/remote/tracker-http.h + +diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build +index 21b9b5a28..c1ba14a83 100644 +--- a/src/libtracker-sparql/meson.build ++++ b/src/libtracker-sparql/meson.build +@@ -1,3 +1,5 @@ ++subdir('remote') ++ + version_header = configure_file( + input: 'tracker-version-generated.h.meson.in', + output: 'tracker-version-generated.h', +@@ -148,6 +150,7 @@ libtracker_sparql = library('tracker-sparql-' + tracker_api_version, + 'direct/tracker-direct.vapi', + 'tracker-backend.vala', + 'tracker-remote-module.c', ++ remote_files, + tracker_gresources, + + gnu_symbol_visibility: 'hidden', +diff --git a/src/libtracker-sparql/remote/meson.build b/src/libtracker-sparql/remote/meson.build +new file mode 100644 +index 000000000..5cc639795 +--- /dev/null ++++ b/src/libtracker-sparql/remote/meson.build +@@ -0,0 +1,3 @@ ++remote_files = files( ++ 'tracker-http.c', ++) +diff --git a/src/libtracker-sparql/remote/tracker-http.c b/src/libtracker-sparql/remote/tracker-http.c +new file mode 100644 +index 000000000..345f8412d +--- /dev/null ++++ b/src/libtracker-sparql/remote/tracker-http.c +@@ -0,0 +1,334 @@ ++/* ++ * Copyright (C) 2022, Red Hat Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ * Author: Carlos Garnacho ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include "tracker-http.h" ++ ++static GType client_type = G_TYPE_NONE; ++static GType server_type = G_TYPE_NONE; ++ ++/* Module loading */ ++#define LIBSOUP_2_SONAME "libsoup-2.4.so.1" ++ ++static void ++ensure_types (void) ++{ ++ const char *modules[3] = { 0 }; ++ gpointer handle = NULL; ++ gint i = 0; ++ ++ if (client_type != G_TYPE_NONE) ++ return; ++ ++ g_assert (g_module_supported ()); ++ ++#ifdef HAVE_RTLD_NOLOAD ++ if ((handle = dlopen (LIBSOUP_2_SONAME, RTLD_NOW | RTLD_NOLOAD))) { ++ /* Force load of soup2 module */ ++ modules[0] = "libtracker-http-soup2.so"; ++ } else ++#endif ++ { ++ modules[0] = "libtracker-http-soup3.so"; ++ modules[1] = "libtracker-http-soup2.so"; ++ } ++ ++ g_clear_pointer (&handle, dlclose); ++ ++ for (i = 0; modules[i]; i++) { ++ GModule *remote_module; ++ gchar *module_path; ++ void (* init_func) (GType *client, GType *server); ++ ++ if (g_strcmp0 (g_get_current_dir (), BUILDROOT) == 0) { ++ /* Detect in-build runtime of this code, this may happen ++ * building introspection information or running tests. ++ * We want the in-tree modules to be loaded then. ++ */ ++ module_path = g_strdup_printf (BUILD_LIBDIR "/remote/%s", modules[i]); ++ } else { ++ module_path = g_strdup_printf (PRIVATE_LIBDIR "/%s", modules[i]); ++ } ++ ++ if (!g_file_test (module_path, G_FILE_TEST_EXISTS)) { ++ g_free (module_path); ++ continue; ++ } ++ ++ remote_module = g_module_open (module_path, ++ G_MODULE_BIND_LAZY | ++ G_MODULE_BIND_LOCAL); ++ g_free (module_path); ++ ++ if (!remote_module) { ++ g_printerr ("Could not load '%s': %s\n", ++ modules[i], g_module_error ()); ++ continue; ++ } ++ ++ if (!g_module_symbol (remote_module, "initialize_types", (gpointer *) &init_func)) { ++ g_printerr ("Could find init function: %s\n", ++ g_module_error ()); ++ g_clear_pointer (&remote_module, g_module_close); ++ continue; ++ } ++ ++ g_type_ensure (TRACKER_TYPE_HTTP_CLIENT); ++ g_type_ensure (TRACKER_TYPE_HTTP_SERVER); ++ ++ init_func (&client_type, &server_type); ++ ++ g_module_make_resident (remote_module); ++ g_module_close (remote_module); ++ ++ g_assert (client_type != G_TYPE_NONE); ++ g_assert (server_type != G_TYPE_NONE); ++ return; ++ } ++ ++ g_assert_not_reached (); ++} ++ ++/* HTTP server */ ++enum { ++ PROP_0, ++ PROP_HTTP_PORT, ++ PROP_HTTP_CERTIFICATE, ++ N_SERVER_PROPS ++}; ++ ++enum { ++ REQUEST, ++ N_SERVER_SIGNALS, ++}; ++ ++typedef struct ++{ ++ guint port; ++ GTlsCertificate *certificate; ++} TrackerHttpServerPrivate; ++ ++static GParamSpec *server_props[N_SERVER_PROPS] = { 0 }; ++static guint server_signals[N_SERVER_SIGNALS] = { 0 }; ++ ++G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (TrackerHttpServer, ++ tracker_http_server, ++ G_TYPE_OBJECT) ++ ++static void ++tracker_http_server_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ TrackerHttpServer *server = TRACKER_HTTP_SERVER (object); ++ TrackerHttpServerPrivate *priv = ++ tracker_http_server_get_instance_private (server); ++ ++ switch (prop_id) { ++ case PROP_HTTP_PORT: ++ priv->port = g_value_get_uint (value); ++ break; ++ case PROP_HTTP_CERTIFICATE: ++ priv->certificate = g_value_dup_object (value); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static void ++tracker_http_server_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ TrackerHttpServer *server = TRACKER_HTTP_SERVER (object); ++ TrackerHttpServerPrivate *priv = ++ tracker_http_server_get_instance_private (server); ++ ++ switch (prop_id) { ++ case PROP_HTTP_PORT: ++ g_value_set_uint (value, priv->port); ++ break; ++ case PROP_HTTP_CERTIFICATE: ++ g_value_set_object (value, priv->certificate); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static void ++tracker_http_server_class_init (TrackerHttpServerClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->set_property = tracker_http_server_set_property; ++ object_class->get_property = tracker_http_server_get_property; ++ ++ server_signals[REQUEST] = ++ g_signal_new ("request", ++ TRACKER_TYPE_HTTP_SERVER, 0, 0, ++ NULL, NULL, NULL, ++ G_TYPE_NONE, 5, ++ G_TYPE_SOCKET_ADDRESS, ++ G_TYPE_STRING, ++ G_TYPE_HASH_TABLE, ++ G_TYPE_UINT, ++ G_TYPE_POINTER); ++ ++ server_props[PROP_HTTP_PORT] = ++ g_param_spec_uint ("http-port", ++ "HTTP Port", ++ "HTTP Port", ++ 0, G_MAXUINT, ++ 8080, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); ++ server_props[PROP_HTTP_CERTIFICATE] = ++ g_param_spec_object ("http-certificate", ++ "HTTP certificate", ++ "HTTP certificate", ++ G_TYPE_TLS_CERTIFICATE, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); ++ ++ g_object_class_install_properties (object_class, ++ N_SERVER_PROPS, ++ server_props); ++} ++ ++static void ++tracker_http_server_init (TrackerHttpServer *server) ++{ ++} ++ ++TrackerHttpServer * ++tracker_http_server_new (guint port, ++ GTlsCertificate *certificate, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ ensure_types (); ++ ++ return g_initable_new (server_type, ++ cancellable, error, ++ "http-port", port, ++ "http-certificate", certificate, ++ NULL); ++} ++ ++void ++tracker_http_server_response (TrackerHttpServer *server, ++ TrackerHttpRequest *request, ++ TrackerSerializerFormat format, ++ GInputStream *content) ++{ ++ TRACKER_HTTP_SERVER_GET_CLASS (server)->response (server, ++ request, ++ format, ++ content); ++} ++ ++void ++tracker_http_server_error (TrackerHttpServer *server, ++ TrackerHttpRequest *request, ++ gint code, ++ const gchar *message) ++{ ++ TRACKER_HTTP_SERVER_GET_CLASS (server)->error (server, ++ request, ++ code, ++ message); ++} ++ ++/* HTTP client */ ++G_DEFINE_ABSTRACT_TYPE (TrackerHttpClient, tracker_http_client, G_TYPE_OBJECT) ++ ++static void ++tracker_http_client_class_init (TrackerHttpClientClass *klass) ++{ ++} ++ ++static void ++tracker_http_client_init (TrackerHttpClient *server) ++{ ++} ++ ++TrackerHttpClient * ++tracker_http_client_new (void) ++{ ++ ensure_types (); ++ ++ return g_object_new (client_type, NULL); ++} ++ ++void ++tracker_http_client_send_message_async (TrackerHttpClient *client, ++ const gchar *uri, ++ const gchar *query, ++ guint formats, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ TRACKER_HTTP_CLIENT_GET_CLASS (client)->send_message_async (client, ++ uri, ++ query, ++ formats, ++ cancellable, ++ callback, ++ user_data); ++} ++ ++GInputStream * ++tracker_http_client_send_message_finish (TrackerHttpClient *client, ++ GAsyncResult *res, ++ TrackerSerializerFormat *format, ++ GError **error) ++{ ++ return TRACKER_HTTP_CLIENT_GET_CLASS (client)->send_message_finish (client, ++ res, ++ format, ++ error); ++} ++ ++GInputStream * ++tracker_http_client_send_message (TrackerHttpClient *client, ++ const gchar *uri, ++ const gchar *query, ++ guint formats, ++ GCancellable *cancellable, ++ TrackerSerializerFormat *format, ++ GError **error) ++{ ++ return TRACKER_HTTP_CLIENT_GET_CLASS (client)->send_message (client, ++ uri, ++ query, ++ formats, ++ cancellable, ++ format, ++ error); ++} +diff --git a/src/libtracker-sparql/remote/tracker-http.h b/src/libtracker-sparql/remote/tracker-http.h +new file mode 100644 +index 000000000..15e3498c0 +--- /dev/null ++++ b/src/libtracker-sparql/remote/tracker-http.h +@@ -0,0 +1,122 @@ ++/* ++ * Copyright (C) 2022, Red Hat Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ * Author: Carlos Garnacho ++ */ ++ ++#ifndef TRACKER_HTTP_H ++#define TRACKER_HTTP_H ++ ++#include ++ ++#ifndef MODULE ++#include ++#endif ++ ++typedef struct _TrackerHttpRequest TrackerHttpRequest; ++ ++#define TRACKER_TYPE_HTTP_SERVER (tracker_http_server_get_type ()) ++G_DECLARE_DERIVABLE_TYPE (TrackerHttpServer, ++ tracker_http_server, ++ TRACKER, HTTP_SERVER, ++ GObject) ++ ++struct _TrackerHttpServerClass { ++ GObjectClass parent_class; ++ ++ void (* response) (TrackerHttpServer *server, ++ TrackerHttpRequest *request, ++ TrackerSerializerFormat format, ++ GInputStream *content); ++ void (* error) (TrackerHttpServer *server, ++ TrackerHttpRequest *request, ++ gint code, ++ const gchar *message); ++}; ++ ++TrackerHttpServer * tracker_http_server_new (guint port, ++ GTlsCertificate *certificate, ++ GCancellable *cancellable, ++ GError **error); ++ ++void tracker_http_server_response (TrackerHttpServer *server, ++ TrackerHttpRequest *request, ++ TrackerSerializerFormat format, ++ GInputStream *content); ++ ++void tracker_http_server_error (TrackerHttpServer *server, ++ TrackerHttpRequest *request, ++ gint code, ++ const gchar *message); ++ ++#define TRACKER_TYPE_HTTP_CLIENT (tracker_http_client_get_type ()) ++G_DECLARE_DERIVABLE_TYPE (TrackerHttpClient, ++ tracker_http_client, ++ TRACKER, HTTP_CLIENT, ++ GObject) ++ ++struct _TrackerHttpClientClass { ++ GObjectClass parent_class; ++ ++ void (* send_message_async) (TrackerHttpClient *client, ++ const gchar *uri, ++ const gchar *query, ++ guint formats, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++ GInputStream * (* send_message_finish) (TrackerHttpClient *client, ++ GAsyncResult *res, ++ TrackerSerializerFormat *format, ++ GError **error); ++ GInputStream * (* send_message) (TrackerHttpClient *client, ++ const gchar *uri, ++ const gchar *query, ++ guint formats, ++ GCancellable *cancellable, ++ TrackerSerializerFormat *format, ++ GError **error); ++}; ++ ++TrackerHttpClient * tracker_http_client_new (void); ++ ++GInputStream * ++tracker_http_client_send_message (TrackerHttpClient *client, ++ const gchar *uri, ++ const gchar *query, ++ guint formats, ++ GCancellable *cancellable, ++ TrackerSerializerFormat *format, ++ GError **error); ++ ++void ++tracker_http_client_send_message_async (TrackerHttpClient *client, ++ const gchar *uri, ++ const gchar *query, ++ guint formats, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++ ++GInputStream * ++tracker_http_client_send_message_finish (TrackerHttpClient *client, ++ GAsyncResult *res, ++ TrackerSerializerFormat *format, ++ GError **error); ++ ++#endif /* TRACKER_HTTP_H */ +-- +2.38.1 + + +From eacc6ab2ca3177f593983133e0664d6b11a16978 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Sat, 23 Apr 2022 13:49:44 +0200 +Subject: [PATCH 09/11] libtracker-sparql: Add HTTP module soup2/soup3 + implementation + +These modules implement TrackerHttpClient and TrackerHttpServer using +the 2 relevant major libsoup versions. These modules are built and +installed, but dormant and unused. +--- + src/libtracker-sparql/meson.build | 2 + + src/libtracker-sparql/remote/meson.build | 34 ++ + .../remote/tracker-http-module.c | 564 ++++++++++++++++++ + .../remote/tracker-http-module.h | 39 ++ + 4 files changed, 639 insertions(+) + create mode 100644 src/libtracker-sparql/remote/tracker-http-module.c + create mode 100644 src/libtracker-sparql/remote/tracker-http-module.h + +diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build +index c1ba14a83..2cc0eebab 100644 +--- a/src/libtracker-sparql/meson.build ++++ b/src/libtracker-sparql/meson.build +@@ -1,3 +1,5 @@ ++libtracker_sparql_modules = [] ++ + subdir('remote') + + version_header = configure_file( +diff --git a/src/libtracker-sparql/remote/meson.build b/src/libtracker-sparql/remote/meson.build +index 5cc639795..ca137b85e 100644 +--- a/src/libtracker-sparql/remote/meson.build ++++ b/src/libtracker-sparql/remote/meson.build +@@ -1,3 +1,37 @@ + remote_files = files( + 'tracker-http.c', + ) ++ ++module_sources = files('tracker-http-module.c') ++ ++if libsoup2.found() ++ libtracker_http_soup2 = shared_module('tracker-http-soup2', ++ module_sources, ++ dependencies: [libsoup2], ++ c_args: tracker_c_args + [ ++ '-include', 'config.h', ++ '-include', '../tracker-enums-private.h', ++ '-DMODULE', ++ ], ++ install: true, ++ install_dir: tracker_internal_libs_dir, ++ name_suffix: 'so', ++ ) ++ libtracker_sparql_modules += libtracker_http_soup2 ++endif ++ ++if libsoup3.found() ++ libtracker_http_soup3 = shared_module('tracker-http-soup3', ++ module_sources, ++ dependencies: [libsoup3], ++ c_args: tracker_c_args + [ ++ '-include', 'config.h', ++ '-include', '../tracker-enums-private.h', ++ '-DMODULE', ++ ], ++ install: true, ++ install_dir: tracker_internal_libs_dir, ++ name_suffix: 'so', ++ ) ++ libtracker_sparql_modules += libtracker_http_soup3 ++endif +diff --git a/src/libtracker-sparql/remote/tracker-http-module.c b/src/libtracker-sparql/remote/tracker-http-module.c +new file mode 100644 +index 000000000..421005001 +--- /dev/null ++++ b/src/libtracker-sparql/remote/tracker-http-module.c +@@ -0,0 +1,564 @@ ++/* ++ * Copyright (C) 2022, Red Hat Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ * Author: Carlos Garnacho ++ */ ++ ++#include ++ ++#include "tracker-http-module.h" ++ ++GType ++tracker_http_client_get_type (void) ++{ ++ return g_type_from_name ("TrackerHttpClient"); ++} ++ ++GType ++tracker_http_server_get_type (void) ++{ ++ return g_type_from_name ("TrackerHttpServer"); ++} ++ ++static const gchar *mimetypes[] = { ++ "application/sparql-results+json", ++ "application/sparql-results+xml", ++ "text/turtle", ++ "application/trig", ++}; ++ ++G_STATIC_ASSERT (G_N_ELEMENTS (mimetypes) == TRACKER_N_SERIALIZER_FORMATS); ++ ++#define USER_AGENT "Tracker " PACKAGE_VERSION " (https://gitlab.gnome.org/GNOME/tracker/issues/)" ++ ++/* Server */ ++struct _TrackerHttpRequest ++{ ++ TrackerHttpServer *server; ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ SoupServerMessage *message; ++#else ++ SoupMessage *message; ++#endif ++ GTask *task; ++ GInputStream *istream; ++}; ++ ++struct _TrackerHttpServerSoup ++{ ++ TrackerHttpServer parent_instance; ++ SoupServer *server; ++ GCancellable *cancellable; ++}; ++ ++static void tracker_http_server_soup_initable_iface_init (GInitableIface *iface); ++ ++G_DEFINE_TYPE_WITH_CODE (TrackerHttpServerSoup, ++ tracker_http_server_soup, ++ TRACKER_TYPE_HTTP_SERVER, ++ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, ++ tracker_http_server_soup_initable_iface_init)) ++ ++ ++static guint ++get_supported_formats (TrackerHttpRequest *request) ++{ ++ SoupMessageHeaders *request_headers; ++ TrackerSerializerFormat i; ++ guint formats = 0; ++ ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ request_headers = soup_server_message_get_request_headers (request->message); ++#else ++ request_headers = request->message->request_headers; ++#endif ++ ++ for (i = 0; i < TRACKER_N_SERIALIZER_FORMATS; i++) { ++ if (soup_message_headers_header_contains (request_headers, "Accept", ++ mimetypes[i])) ++ formats |= 1 << i; ++ } ++ ++ return formats; ++} ++ ++static void ++set_message_format (TrackerHttpRequest *request, ++ TrackerSerializerFormat format) ++{ ++ SoupMessageHeaders *response_headers; ++ ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ response_headers = soup_server_message_get_response_headers (request->message); ++#else ++ response_headers = request->message->response_headers; ++#endif ++ soup_message_headers_set_content_type (response_headers, mimetypes[format], NULL); ++} ++ ++#if SOUP_CHECK_VERSION (2, 99, 2) ++static void ++server_callback (SoupServer *server, ++ SoupServerMessage *message, ++ const char *path, ++ GHashTable *query, ++ gpointer user_data) ++#else ++static void ++server_callback (SoupServer *server, ++ SoupMessage *message, ++ const char *path, ++ GHashTable *query, ++ SoupClientContext *client, ++ gpointer user_data) ++#endif ++{ ++ TrackerHttpServer *http_server = user_data; ++ GSocketAddress *remote_address; ++ TrackerHttpRequest *request; ++ ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ remote_address = soup_server_message_get_remote_address (message); ++#else ++ remote_address = soup_client_context_get_remote_address (client); ++#endif ++ ++ request = g_new0 (TrackerHttpRequest, 1); ++ request->server = http_server; ++ request->message = message; ++ soup_server_pause_message (server, message); ++ ++ g_signal_emit_by_name (http_server, "request", ++ remote_address, ++ path, ++ query, ++ get_supported_formats (request), ++ request); ++} ++ ++static gboolean ++tracker_http_server_initable_init (GInitable *initable, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ TrackerHttpServerSoup *server = TRACKER_HTTP_SERVER_SOUP (initable); ++ GTlsCertificate *certificate; ++ guint port; ++ ++ g_object_get (initable, ++ "http-certificate", &certificate, ++ "http-port", &port, ++ NULL); ++ ++ server->server = ++ soup_server_new ("tls-certificate", certificate, ++ "server-header", USER_AGENT, ++ NULL); ++ soup_server_add_handler (server->server, ++ "/sparql", ++ server_callback, ++ initable, ++ NULL); ++ g_clear_object (&certificate); ++ ++ return soup_server_listen_all (server->server, ++ port, ++ 0, error); ++} ++ ++static void ++tracker_http_server_soup_initable_iface_init (GInitableIface *iface) ++{ ++ iface->init = tracker_http_server_initable_init; ++} ++ ++static void ++tracker_http_server_soup_error (TrackerHttpServer *server, ++ TrackerHttpRequest *request, ++ gint code, ++ const gchar *message) ++{ ++ TrackerHttpServerSoup *server_soup = ++ TRACKER_HTTP_SERVER_SOUP (server); ++ ++ g_assert (request->server == server); ++ ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ soup_server_message_set_status (request->message, code, message); ++#else ++ soup_message_set_status_full (request->message, code, message); ++#endif ++ soup_server_unpause_message (server_soup->server, request->message); ++ g_free (request); ++} ++ ++static void ++handle_write_in_thread (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ TrackerHttpRequest *request = task_data; ++ gchar buffer[1000]; ++ SoupMessageBody *message_body; ++ GError *error = NULL; ++ gssize count; ++ ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ message_body = soup_server_message_get_response_body (request->message); ++#else ++ message_body = request->message->response_body; ++#endif ++ ++ for (;;) { ++ count = g_input_stream_read (request->istream, ++ buffer, sizeof (buffer), ++ cancellable, &error); ++ if (count < 0) { ++ g_task_return_error (task, error); ++ g_object_unref (task); ++ break; ++ } ++ ++ soup_message_body_append (message_body, ++ SOUP_MEMORY_COPY, ++ buffer, count); ++ ++ if ((gsize) count < sizeof (buffer)) { ++ break; ++ } ++ } ++ ++ g_input_stream_close (request->istream, cancellable, NULL); ++ soup_message_body_complete (message_body); ++ g_task_return_boolean (task, TRUE); ++ g_object_unref (task); ++} ++ ++static void ++write_finished_cb (GObject *object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ TrackerHttpRequest *request = user_data; ++ TrackerHttpServerSoup *server = ++ TRACKER_HTTP_SERVER_SOUP (request->server); ++ GError *error = NULL; ++ ++ if (!g_task_propagate_boolean (G_TASK (result), &error)) { ++ tracker_http_server_soup_error (request->server, ++ request, ++ 500, ++ error->message); ++ g_clear_error (&error); ++ } else { ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ soup_server_message_set_status (request->message, 200, NULL); ++#else ++ soup_message_set_status (request->message, 200); ++#endif ++ soup_server_unpause_message (server->server, request->message); ++ g_free (request); ++ } ++} ++ ++static void ++tracker_http_server_soup_response (TrackerHttpServer *server, ++ TrackerHttpRequest *request, ++ TrackerSerializerFormat format, ++ GInputStream *content) ++{ ++ TrackerHttpServerSoup *server_soup = ++ TRACKER_HTTP_SERVER_SOUP (server); ++ ++ g_assert (request->server == server); ++ ++ set_message_format (request, format); ++ ++ request->istream = content; ++ request->task = g_task_new (server, server_soup->cancellable, ++ write_finished_cb, request); ++ ++ g_task_set_task_data (request->task, request, NULL); ++ g_task_run_in_thread (request->task, handle_write_in_thread); ++} ++ ++static void ++tracker_http_server_soup_finalize (GObject *object) ++{ ++ TrackerHttpServerSoup *server = ++ TRACKER_HTTP_SERVER_SOUP (object); ++ ++ g_cancellable_cancel (server->cancellable); ++ g_object_unref (server->cancellable); ++ ++ g_clear_object (&server->server); ++ ++ G_OBJECT_CLASS (tracker_http_server_soup_parent_class)->finalize (object); ++} ++ ++static void ++tracker_http_server_soup_class_init (TrackerHttpServerSoupClass *klass) ++{ ++ TrackerHttpServerClass *server_class = TRACKER_HTTP_SERVER_CLASS (klass); ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->finalize = tracker_http_server_soup_finalize; ++ ++ server_class->response = tracker_http_server_soup_response; ++ server_class->error = tracker_http_server_soup_error; ++} ++ ++static void ++tracker_http_server_soup_init (TrackerHttpServerSoup *server) ++{ ++ server->cancellable = g_cancellable_new (); ++} ++ ++/* Client */ ++struct _TrackerHttpClientSoup ++{ ++ TrackerHttpClient parent_instance; ++ SoupSession *session; ++}; ++ ++G_DEFINE_TYPE (TrackerHttpClientSoup, tracker_http_client_soup, ++ TRACKER_TYPE_HTTP_CLIENT) ++ ++static gboolean ++get_content_type_format (SoupMessage *message, ++ TrackerSerializerFormat *format, ++ GError **error) ++{ ++ SoupMessageHeaders *response_headers; ++ gint status_code; ++ const gchar *content_type; ++ TrackerSerializerFormat i; ++ ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ status_code = soup_message_get_status (message); ++ response_headers = soup_message_get_response_headers (message); ++#else ++ status_code = message->status_code; ++ response_headers = message->response_headers; ++#endif ++ ++ if (status_code != SOUP_STATUS_OK) { ++ g_set_error (error, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "Unhandled status code %d", ++ status_code); ++ return FALSE; ++ } ++ ++ content_type = soup_message_headers_get_content_type (response_headers, NULL); ++ ++ for (i = 0; i < TRACKER_N_SERIALIZER_FORMATS; i++) { ++ if (g_strcmp0 (content_type, mimetypes[i]) == 0) { ++ *format = i; ++ return TRUE; ++ } ++ } ++ ++ g_set_error (error, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "Unhandled content type '%s'", ++ soup_message_headers_get_content_type (response_headers, NULL)); ++ return FALSE; ++} ++ ++static void ++send_message_cb (GObject *source, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ GTask *task = user_data; ++ GInputStream *stream; ++ GError *error = NULL; ++ SoupMessage *message; ++ ++ stream = soup_session_send_finish (SOUP_SESSION (source), res, &error); ++ message = g_task_get_task_data (task); ++ ++ if (stream) { ++ TrackerSerializerFormat format; ++ ++ if (!get_content_type_format (message, &format, &error)) { ++ g_task_return_error (task, error); ++ } else { ++ g_task_set_task_data (task, GUINT_TO_POINTER (format), NULL); ++ g_task_return_pointer (task, stream, g_object_unref); ++ } ++ } else { ++ g_task_return_error (task, error); ++ } ++ ++ g_object_unref (task); ++} ++ ++static void ++add_accepted_formats (SoupMessageHeaders *headers, ++ guint formats) ++{ ++ TrackerSerializerFormat i; ++ ++ for (i = 0; i < TRACKER_N_SERIALIZER_FORMATS; i++) { ++ if ((formats & (1 << i)) == 0) ++ continue; ++ ++ soup_message_headers_append (headers, "Accept", mimetypes[i]); ++ } ++} ++ ++static SoupMessage * ++create_message (const gchar *uri, ++ const gchar *query, ++ guint formats) ++{ ++ SoupMessage *message; ++ SoupMessageHeaders *headers; ++ gchar *full_uri, *query_escaped; ++ ++ query_escaped = g_uri_escape_string (query, NULL, FALSE); ++ full_uri = g_strconcat (uri, "?query=", query_escaped, NULL); ++ g_free (query_escaped); ++ ++ message = soup_message_new ("GET", full_uri); ++ g_free (full_uri); ++ ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ headers = soup_message_get_request_headers (message); ++#else ++ headers = message->request_headers; ++#endif ++ ++ soup_message_headers_append (headers, "User-Agent", USER_AGENT); ++ add_accepted_formats (headers, formats); ++ ++ return message; ++} ++ ++static void ++tracker_http_client_soup_send_message_async (TrackerHttpClient *client, ++ const gchar *uri, ++ const gchar *query, ++ guint formats, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ TrackerHttpClientSoup *client_soup = TRACKER_HTTP_CLIENT_SOUP (client); ++ SoupMessage *message; ++ GTask *task; ++ ++ task = g_task_new (client, cancellable, callback, user_data); ++ ++ message = create_message (uri, query, formats); ++ g_task_set_task_data (task, message, g_object_unref); ++ ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ soup_session_send_async (client_soup->session, ++ message, ++ G_PRIORITY_DEFAULT, ++ cancellable, ++ send_message_cb, ++ task); ++#else ++ soup_session_send_async (client_soup->session, ++ message, ++ cancellable, ++ send_message_cb, ++ task); ++#endif ++} ++ ++static GInputStream * ++tracker_http_client_soup_send_message_finish (TrackerHttpClient *client, ++ GAsyncResult *res, ++ TrackerSerializerFormat *format, ++ GError **error) ++{ ++ if (format) ++ *format = GPOINTER_TO_UINT (g_task_get_task_data (G_TASK (res))); ++ ++ return g_task_propagate_pointer (G_TASK (res), error); ++} ++ ++static GInputStream * ++tracker_http_client_soup_send_message (TrackerHttpClient *client, ++ const gchar *uri, ++ const gchar *query, ++ guint formats, ++ GCancellable *cancellable, ++ TrackerSerializerFormat *format, ++ GError **error) ++{ ++ TrackerHttpClientSoup *client_soup = TRACKER_HTTP_CLIENT_SOUP (client); ++ SoupMessage *message; ++ GInputStream *stream; ++ ++ message = create_message (uri, query, formats); ++ ++#if SOUP_CHECK_VERSION (2, 99, 2) ++ stream = soup_session_send (client_soup->session, ++ message, ++ cancellable, ++ error); ++#else ++ stream = soup_session_send (client_soup->session, ++ message, ++ cancellable, ++ error); ++#endif ++ if (!stream) ++ return NULL; ++ ++ if (!get_content_type_format (message, format, error)) { ++ g_clear_object (&stream); ++ return NULL; ++ } ++ ++ return stream; ++} ++ ++static void ++tracker_http_client_soup_class_init (TrackerHttpClientSoupClass *klass) ++{ ++ TrackerHttpClientClass *client_class = ++ TRACKER_HTTP_CLIENT_CLASS (klass); ++ ++ client_class->send_message_async = tracker_http_client_soup_send_message_async; ++ client_class->send_message_finish = tracker_http_client_soup_send_message_finish; ++ client_class->send_message = tracker_http_client_soup_send_message; ++} ++ ++static void ++tracker_http_client_soup_init (TrackerHttpClientSoup *client) ++{ ++ client->session = soup_session_new (); ++} ++ ++void ++initialize_types (GType *client, ++ GType *server) ++{ ++ *client = TRACKER_TYPE_HTTP_CLIENT_SOUP; ++ *server = TRACKER_TYPE_HTTP_SERVER_SOUP; ++} +diff --git a/src/libtracker-sparql/remote/tracker-http-module.h b/src/libtracker-sparql/remote/tracker-http-module.h +new file mode 100644 +index 000000000..c013a6f35 +--- /dev/null ++++ b/src/libtracker-sparql/remote/tracker-http-module.h +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (C) 2022, Red Hat Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ * Author: Carlos Garnacho ++ */ ++ ++#ifndef TRACKER_HTTP_MODULE_H ++#define TRACKER_HTTP_MODULE_H ++ ++#include "tracker-http.h" ++ ++#define TRACKER_TYPE_HTTP_SERVER_SOUP (tracker_http_server_soup_get_type ()) ++G_DECLARE_FINAL_TYPE (TrackerHttpServerSoup, ++ tracker_http_server_soup, ++ TRACKER, HTTP_SERVER_SOUP, ++ TrackerHttpServer) ++ ++#define TRACKER_TYPE_HTTP_CLIENT_SOUP (tracker_http_client_soup_get_type ()) ++G_DECLARE_FINAL_TYPE (TrackerHttpClientSoup, ++ tracker_http_client_soup, ++ TRACKER, HTTP_CLIENT_SOUP, ++ TrackerHttpClient) ++ ++#endif /* TRACKER_HTTP_MODULE_H */ +-- +2.38.1 + + +From f1687b4f7ab2be4866424b05debac47bfcc8f9c7 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Sat, 23 Apr 2022 14:18:26 +0200 +Subject: [PATCH 10/11] libtracker-sparql: Port remote TrackerSparqlConnection + to new http helpers + +Remove the direct soup dependency by using TrackerHttpClient, that deals +with soup itself. This also means the remote connection objects can move +to the main library code, instead of being built as standalone modules. + +In consequence, the older remote modules are no longer built, and their +loading infrastructure removed. +--- + src/libtracker-sparql/meson.build | 58 +-------- + src/libtracker-sparql/remote/meson.build | 5 + + .../remote/tracker-http-module.c | 2 - + .../remote/tracker-remote.vala | 94 ++++---------- + .../remote/tracker-remote.vapi | 15 +++ + src/libtracker-sparql/tracker-backend.vala | 5 + + src/libtracker-sparql/tracker-remote-module.c | 115 ------------------ + 7 files changed, 50 insertions(+), 244 deletions(-) + delete mode 100644 src/libtracker-sparql/tracker-remote-module.c + +diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build +index 2cc0eebab..74a88e224 100644 +--- a/src/libtracker-sparql/meson.build ++++ b/src/libtracker-sparql/meson.build +@@ -24,6 +24,7 @@ libtracker_sparql_c_sources = files( + 'tracker-cursor.c', + 'tracker-endpoint.c', + 'tracker-endpoint-dbus.c', ++ 'tracker-endpoint-http.c', + 'tracker-error.c', + 'tracker-namespace-manager.c', + 'tracker-notifier.c', +@@ -92,66 +93,11 @@ install_data( + subdir('bus') + subdir('direct') + +-tracker_remote_dependencies = [json_glib, libxml2] +- +-libtracker_sparql_remote_c_sources = files ( +- 'tracker-endpoint-http.c', +- 'remote/tracker-remote-statement.c', +-) +- +-remote_sources = [ +- libtracker_sparql_remote_c_sources, +- 'remote/tracker-remote.vapi', +- 'remote/tracker-json-cursor.vala', +- 'remote/tracker-xml-cursor.vala', +- 'remote/tracker-remote.vala', +-] +- +-if libsoup2.found() +- libtracker_remote_soup2 = shared_module('tracker-remote-soup2', remote_sources, +- dependencies: tracker_remote_dependencies + [tracker_common_dep, tracker_sparql_intermediate_dep, libsoup2], +- c_args: tracker_c_args + [ +- '-include', 'config.h', +- '-include', 'libtracker-sparql/tracker-private.h', +- ], +- vala_args: [ +- '--debug', +- '--pkg', 'posix', +- # FIXME: Meson has code to add --target-glib automatically, but it +- # doesn't seem to work here. +- '--target-glib', glib_required, +- '--define=SOUP2', +- ], +- install: true, +- install_dir: tracker_internal_libs_dir, +- ) +-endif +- +-if libsoup3.found() +- libtracker_remote_soup3 = shared_module('tracker-remote-soup3', remote_sources, +- dependencies: tracker_remote_dependencies + [tracker_common_dep, tracker_sparql_intermediate_dep, libsoup3], +- c_args: tracker_c_args + [ +- '-include', 'config.h', +- '-include', 'libtracker-sparql/tracker-private.h', +- ], +- vala_args: [ +- '--debug', +- '--pkg', 'posix', +- # FIXME: Meson has code to add --target-glib automatically, but it +- # doesn't seem to work here. +- '--target-glib', glib_required, +- ], +- install: true, +- install_dir: tracker_internal_libs_dir, +- ) +-endif +- + libtracker_sparql = library('tracker-sparql-' + tracker_api_version, + '../libtracker-common/libtracker-common.vapi', + '../libtracker-data/libtracker-data.vapi', + 'direct/tracker-direct.vapi', + 'tracker-backend.vala', +- 'tracker-remote-module.c', + remote_files, + tracker_gresources, + +@@ -172,7 +118,7 @@ libtracker_sparql = library('tracker-sparql-' + tracker_api_version, + + link_whole: [libtracker_sparql_intermediate], + +- dependencies: [tracker_common_dep, tracker_sparql_bus_dep, tracker_sparql_direct_dep, tracker_sparql_vapi_dep, gmodule, libdl], ++ dependencies: [tracker_common_dep, tracker_sparql_bus_dep, tracker_sparql_direct_dep, tracker_sparql_vapi_dep, gmodule, libdl, json_glib, libxml2], + ) + + tracker_sparql_dep = declare_dependency( +diff --git a/src/libtracker-sparql/remote/meson.build b/src/libtracker-sparql/remote/meson.build +index ca137b85e..3d9e0c653 100644 +--- a/src/libtracker-sparql/remote/meson.build ++++ b/src/libtracker-sparql/remote/meson.build +@@ -1,5 +1,10 @@ + remote_files = files( + 'tracker-http.c', ++ 'tracker-remote-statement.c', ++ 'tracker-remote.vapi', ++ 'tracker-json-cursor.vala', ++ 'tracker-xml-cursor.vala', ++ 'tracker-remote.vala', + ) + + module_sources = files('tracker-http-module.c') +diff --git a/src/libtracker-sparql/remote/tracker-http-module.c b/src/libtracker-sparql/remote/tracker-http-module.c +index 421005001..4fa240654 100644 +--- a/src/libtracker-sparql/remote/tracker-http-module.c ++++ b/src/libtracker-sparql/remote/tracker-http-module.c +@@ -38,8 +38,6 @@ tracker_http_server_get_type (void) + static const gchar *mimetypes[] = { + "application/sparql-results+json", + "application/sparql-results+xml", +- "text/turtle", +- "application/trig", + }; + + G_STATIC_ASSERT (G_N_ELEMENTS (mimetypes) == TRACKER_N_SERIALIZER_FORMATS); +diff --git a/src/libtracker-sparql/remote/tracker-remote.vala b/src/libtracker-sparql/remote/tracker-remote.vala +index f3be3147a..9c659c2ef 100644 +--- a/src/libtracker-sparql/remote/tracker-remote.vala ++++ b/src/libtracker-sparql/remote/tracker-remote.vala +@@ -18,96 +18,48 @@ + * + * Author: Carlos Garnacho + */ +-[CCode (cname = "PACKAGE_VERSION")] +-extern const string PACKAGE_VERSION; +- + public class Tracker.Remote.Connection : Tracker.Sparql.Connection { +- +- internal Soup.Session _session; ++ internal HttpClient _client; + internal string _base_uri; + +- const string XML_TYPE = "application/sparql-results+xml"; +- const string JSON_TYPE = "application/sparql-results+json"; +- const string USER_AGENT = "Tracker/" + PACKAGE_VERSION + " (https://gitlab.gnome.org/GNOME/tracker/issues/; tracker-list@lists.gnome.org) Tracker/" + PACKAGE_VERSION; +- + public Connection (string base_uri) { + Object (); + _base_uri = base_uri; +- _session = new Soup.Session (); +- } +- +- private Soup.Message create_request (string sparql) { +- var uri = _base_uri + "?query=" + GLib.Uri.escape_string (sparql, null, false); +- var message = new Soup.Message ("GET", uri); +-#if SOUP2 +- var headers = message.request_headers; +-#else +- var headers = message.get_request_headers(); +-#endif +- +- headers.append ("User-Agent", USER_AGENT); +- headers.append ("Accept", JSON_TYPE); +- headers.append ("Accept", XML_TYPE); +- +- return message; ++ _client = new HttpClient(); + } + +- private Sparql.Cursor create_cursor (Soup.Message message, string document) throws GLib.Error, Sparql.Error { +-#if SOUP2 +- var status_code = message.status_code; +- var headers = message.response_headers; +-#else +- var status_code = message.get_status(); +- var headers = message.get_response_headers(); +-#endif ++ private Sparql.Cursor create_cursor (GLib.InputStream stream, SerializerFormat format) throws GLib.Error, Sparql.Error { ++ var buffer = new uchar[20 * 1024 * 1024]; ++ size_t len; ++ stream.read_all (buffer, out len, null); + +- if (status_code != Soup.Status.OK) { +- throw new Sparql.Error.UNSUPPORTED ("Unhandled status code %u, document is: %s", +- status_code, document); +- } +- +- var content_type = headers.get_content_type (null); +- long length = document.length; +- +- if (content_type == JSON_TYPE) { +- return new Tracker.Remote.JsonCursor (document, length); +- } else if (content_type == XML_TYPE) { +- return new Tracker.Remote.XmlCursor (document, length); ++ if (format == SerializerFormat.JSON) { ++ return new Tracker.Remote.JsonCursor ((string) buffer, (long) len); ++ } else if (format == SerializerFormat.XML) { ++ return new Tracker.Remote.XmlCursor ((string) buffer, (long) len); + } else { +- throw new Sparql.Error.UNSUPPORTED ("Unknown content type '%s', document is: %s", content_type, document); ++ throw new Sparql.Error.UNSUPPORTED ("Unparseable content type, document is: %s", (string) buffer); + } + } + + public override Sparql.Cursor query (string sparql, Cancellable? cancellable) throws GLib.Error, Sparql.Error, IOError { +- var message = create_request (sparql); +- +-#if SOUP2 +- _session.send_message (message); +- var data = (string) message.response_body.flatten ().data; +-#else +- var body = _session.send_and_read (message); +- var data = (string) body.get_data(); +-#endif +- +- if (data == null || data == "") +- throw new Sparql.Error.UNSUPPORTED ("Empty response"); +- +- if (cancellable != null && cancellable.is_cancelled ()) +- throw new IOError.CANCELLED ("Operation was cancelled"); ++ uint flags = ++ (1 << SerializerFormat.JSON) | ++ (1 << SerializerFormat.XML); ++ SerializerFormat format; ++ var istream = _client.send_message (_base_uri, sparql, flags, cancellable, out format); + +- return create_cursor (message, (string) data); ++ return create_cursor (istream, format); + } + + public async override Sparql.Cursor query_async (string sparql, Cancellable? cancellable) throws GLib.Error, Sparql.Error, IOError { +- var message = create_request (sparql); ++ uint flags = ++ (1 << SerializerFormat.JSON) | ++ (1 << SerializerFormat.XML); ++ SerializerFormat format; ++ var istream = yield _client.send_message_async (_base_uri, sparql, flags, cancellable, out format); + +-#if SOUP2 +- yield _session.send_async (message, cancellable); +- return create_cursor (message, (string) message.response_body.flatten ().data); +-#else +- var body = yield _session.send_and_read_async (message, GLib.Priority.DEFAULT, cancellable); +- return create_cursor (message, (string) body.get_data()); +-#endif ++ return create_cursor (istream, format); + } + + public override Sparql.Statement? query_statement (string sparql, GLib.Cancellable? cancellable = null) throws Sparql.Error { +diff --git a/src/libtracker-sparql/remote/tracker-remote.vapi b/src/libtracker-sparql/remote/tracker-remote.vapi +index caf018b41..d266470e1 100644 +--- a/src/libtracker-sparql/remote/tracker-remote.vapi ++++ b/src/libtracker-sparql/remote/tracker-remote.vapi +@@ -3,4 +3,19 @@ namespace Tracker { + class Remote.Statement : Sparql.Statement { + public Statement (Sparql.Connection conn, string query) throws Sparql.Error; + } ++ ++ [CCode (cheader_filename = "libtracker-sparql/remote/tracker-http.h")] ++ class HttpClient : GLib.Object { ++ public HttpClient (); ++ public async GLib.InputStream send_message_async (string uri, string query, uint formats, GLib.Cancellable? cancellable, out SerializerFormat format) throws GLib.Error; ++ public GLib.InputStream send_message (string uri, string query, uint formats, GLib.Cancellable? cancellable, out SerializerFormat format) throws GLib.Error; ++ } ++ ++ [CCode (cheader_filename = "libtracker-sparql/tracker-enums-private.h")] ++ enum SerializerFormat { ++ JSON, ++ XML, ++ TTL, ++ TRIG, ++ } + } +diff --git a/src/libtracker-sparql/tracker-backend.vala b/src/libtracker-sparql/tracker-backend.vala +index af1102d5a..f2d497f6a 100644 +--- a/src/libtracker-sparql/tracker-backend.vala ++++ b/src/libtracker-sparql/tracker-backend.vala +@@ -82,3 +82,8 @@ public static async Tracker.Sparql.Connection tracker_sparql_connection_new_asyn + yield conn.init_async (Priority.DEFAULT, cancellable); + return conn; + } ++ ++public static Tracker.Sparql.Connection tracker_sparql_connection_remote_new (string uri_base) { ++ Tracker.get_debug_flags (); ++ return new Tracker.Remote.Connection (uri_base); ++} +diff --git a/src/libtracker-sparql/tracker-remote-module.c b/src/libtracker-sparql/tracker-remote-module.c +deleted file mode 100644 +index 2ca0fd181..000000000 +--- a/src/libtracker-sparql/tracker-remote-module.c ++++ /dev/null +@@ -1,115 +0,0 @@ +-/* Yuck */ +- +-#include "config.h" +- +-#include +-#include +-#include +- +-#define LIBSOUP_2_SONAME "libsoup-2.4.so.1" +- +-static gboolean initialized = FALSE; +- +-GType (* remote_endpoint_get_type) (void) = NULL; +- +-TrackerEndpoint * (* remote_endpoint_new) (TrackerSparqlConnection *sparql_connection, +- guint port, +- GTlsCertificate *certificate, +- GCancellable *cancellable, +- GError **error) = NULL; +-TrackerSparqlConnection * (* remote_connection_new) (const gchar *url_base) = NULL; +- +-static void +-tracker_init_remote (void) +-{ +- const char *modules[3] = { 0 }; +- gpointer handle = NULL; +- gint i = 0; +- +- if (initialized) +- return; +- +- g_assert (g_module_supported ()); +- +-#ifdef HAVE_RTLD_NOLOAD +- if ((handle = dlopen (LIBSOUP_2_SONAME, RTLD_NOW | RTLD_NOLOAD))) { +- /* Force load of soup2 module */ +- modules[0] = "libtracker-remote-soup2.so"; +- } else +-#endif +- { +- modules[0] = "libtracker-remote-soup3.so"; +- modules[1] = "libtracker-remote-soup2.so"; +- } +- +- g_clear_pointer (&handle, dlclose); +- +- for (i = 0; modules[i]; i++) { +- GModule *remote_module; +- gchar *module_path; +- +- if (g_strcmp0 (g_get_current_dir (), BUILDROOT) == 0) { +- /* Detect in-build runtime of this code, this may happen +- * building introspection information or running tests. +- * We want the in-tree modules to be loaded then. +- */ +- module_path = g_strdup_printf (BUILD_LIBDIR "/%s", modules[i]); +- } else { +- module_path = g_strdup_printf (PRIVATE_LIBDIR "/%s", modules[i]); +- } +- +- remote_module = g_module_open (module_path, +- G_MODULE_BIND_LAZY | +- G_MODULE_BIND_LOCAL); +- g_free (module_path); +- +- if (!remote_module) +- continue; +- +- if (!g_module_symbol (remote_module, "tracker_endpoint_http_get_type", (gpointer *) &remote_endpoint_get_type) || +- !g_module_symbol (remote_module, "tracker_endpoint_http_new", (gpointer *) &remote_endpoint_new) || +- !g_module_symbol (remote_module, "tracker_remote_connection_new", (gpointer *) &remote_connection_new)) { +- g_clear_pointer (&remote_module, g_module_close); +- continue; +- } +- +- g_module_make_resident (remote_module); +- g_module_close (remote_module); +- initialized = TRUE; +- return; +- } +- +- g_assert_not_reached (); +-} +- +-GType +-tracker_endpoint_http_get_type (void) +-{ +- tracker_init_remote (); +- +- return remote_endpoint_get_type (); +-} +- +-TrackerEndpointHttp * +-tracker_endpoint_http_new (TrackerSparqlConnection *sparql_connection, +- guint port, +- GTlsCertificate *certificate, +- GCancellable *cancellable, +- GError **error) +-{ +- tracker_init_remote (); +- +- return (TrackerEndpointHttp *) remote_endpoint_new (sparql_connection, +- port, +- certificate, +- cancellable, +- error); +-} +- +-TrackerSparqlConnection * +-tracker_sparql_connection_remote_new (const gchar *url_base) +-{ +- tracker_init_remote (); +- +- return remote_connection_new (url_base); +-} +-- +2.38.1 + + +From 4d6d43495ce831d9f3b52f2f0142a475edad35fe Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Sat, 23 Apr 2022 14:01:12 +0200 +Subject: [PATCH 11/11] libtracker-sparql: Port TrackerEndpointHttp to new HTTP + module + +Use TrackerHttpServer to implement HTTP endpoints, and remove all +soup specific code. This also means the TrackerEndpointHttp object +and type can move out of the elder soup modules, and into the main +library code. All soup 2/3 handling will stay underneath. +--- + src/libtracker-sparql/tracker-endpoint-http.c | 238 ++++-------------- + 1 file changed, 55 insertions(+), 183 deletions(-) + +diff --git a/src/libtracker-sparql/tracker-endpoint-http.c b/src/libtracker-sparql/tracker-endpoint-http.c +index 5aa82b03d..2a45f1114 100644 +--- a/src/libtracker-sparql/tracker-endpoint-http.c ++++ b/src/libtracker-sparql/tracker-endpoint-http.c +@@ -25,15 +25,13 @@ + #include "tracker-serializer.h" + #include "tracker-private.h" + +-#include +- +-#define SERVER_HEADER "Tracker " PACKAGE_VERSION " (https://gitlab.gnome.org/GNOME/tracker/issues/)" ++#include "remote/tracker-http.h" + + typedef struct _TrackerEndpointHttp TrackerEndpointHttp; + + struct _TrackerEndpointHttp { + TrackerEndpoint parent_instance; +- SoupServer *server; ++ TrackerHttpServer *server; + GTlsCertificate *certificate; + guint port; + GCancellable *cancellable; +@@ -41,11 +39,7 @@ struct _TrackerEndpointHttp { + + typedef struct { + TrackerEndpoint *endpoint; +-#if SOUP_CHECK_VERSION (2, 99, 2) +- SoupServerMessage *message; +-#else +- SoupMessage *message; +-#endif ++ TrackerHttpRequest *request; + GInputStream *istream; + GTask *task; + TrackerSerializerFormat format; +@@ -63,9 +57,6 @@ enum { + N_PROPS + }; + +-#define XML_TYPE "application/sparql-results+xml" +-#define JSON_TYPE "application/sparql-results+json" +- + static GParamSpec *props[N_PROPS]; + static guint signals[N_SIGNALS]; + +@@ -81,80 +72,6 @@ request_free (Request *request) + g_free (request); + } + +-static void +-handle_request_in_thread (GTask *task, +- gpointer source_object, +- gpointer task_data, +- GCancellable *cancellable) +-{ +- Request *request = task_data; +- gchar *buffer[1000]; +- gboolean finished = FALSE; +- SoupMessageBody *message_body; +- GError *error = NULL; +- gssize count; +- +-#if SOUP_CHECK_VERSION (2, 99, 2) +- message_body = soup_server_message_get_response_body (request->message); +-#else +- message_body = request->message->response_body; +-#endif +- +- while (!finished) { +- count = g_input_stream_read (request->istream, +- buffer, sizeof (buffer), +- cancellable, &error); +- if (count == -1) { +- g_task_return_error (task, error); +- break; +- } else if (count < sizeof (buffer)) { +- finished = TRUE; +- } +- +- soup_message_body_append (message_body, +- SOUP_MEMORY_COPY, +- buffer, count); +- } +- +- g_input_stream_close (request->istream, cancellable, NULL); +- soup_message_body_complete (message_body); +- g_task_return_boolean (task, TRUE); +-} +- +-static void +-request_finished_cb (GObject *object, +- GAsyncResult *result, +- gpointer user_data) +-{ +- Request *request = user_data; +- TrackerEndpointHttp *endpoint_http; +- GError *error = NULL; +- +- endpoint_http = TRACKER_ENDPOINT_HTTP (request->endpoint); +- +- if (!g_task_propagate_boolean (G_TASK (result), &error)) { +-#if SOUP_CHECK_VERSION (2, 99, 2) +- soup_server_message_set_status (request->message, 500, +- error ? error->message : +- "No error message"); +-#else +- soup_message_set_status_full (request->message, 500, +- error ? error->message : +- "No error message"); +-#endif +- g_clear_error (&error); +- } else { +-#if SOUP_CHECK_VERSION (2, 99, 2) +- soup_server_message_set_status (request->message, 200, NULL); +-#else +- soup_message_set_status (request->message, 200); +-#endif +- } +- +- soup_server_unpause_message (endpoint_http->server, request->message); +- request_free (request); +-} +- + static void + query_async_cb (GObject *object, + GAsyncResult *result, +@@ -163,141 +80,98 @@ query_async_cb (GObject *object, + TrackerEndpointHttp *endpoint_http; + TrackerSparqlCursor *cursor; + Request *request = user_data; ++ GInputStream *stream; + GError *error = NULL; + + endpoint_http = TRACKER_ENDPOINT_HTTP (request->endpoint); + cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (object), + result, &error); + if (error) { +-#if SOUP_CHECK_VERSION (2, 99, 2) +- soup_server_message_set_status (request->message, 500, error->message); +-#else +- soup_message_set_status_full (request->message, 500, error->message); +-#endif +- soup_server_unpause_message (endpoint_http->server, request->message); ++ tracker_http_server_error (endpoint_http->server, ++ request->request, ++ 500, ++ error->message); + request_free (request); ++ g_error_free (error); + return; + } + +- request->istream = tracker_serializer_new (cursor, request->format); +- request->task = g_task_new (endpoint_http, endpoint_http->cancellable, +- request_finished_cb, request); +- g_task_set_task_data (request->task, request, NULL); +- +- g_task_run_in_thread (request->task, handle_request_in_thread); ++ stream = tracker_serializer_new (cursor, request->format); ++ /* Consumes the input stream */ ++ tracker_http_server_response (endpoint_http->server, ++ request->request, ++ request->format, ++ stream); ++ request_free (request); + } + +-#if SOUP_CHECK_VERSION (2, 99, 2) + static gboolean +-pick_format (SoupServerMessage *message, ++pick_format (guint formats, + TrackerSerializerFormat *format) +-#else +-static gboolean +-pick_format (SoupMessage *message, +- TrackerSerializerFormat *format) +-#endif + { +- SoupMessageHeaders *request_headers, *response_headers; +- +-#if SOUP_CHECK_VERSION (2, 99, 2) +- request_headers = soup_server_message_get_request_headers (message); +- response_headers = soup_server_message_get_response_headers (message); +-#else +- request_headers = message->request_headers; +- response_headers = message->response_headers; +-#endif +- +- if (soup_message_headers_header_contains (request_headers, "Accept", JSON_TYPE)) { +- soup_message_headers_set_content_type (response_headers, JSON_TYPE, NULL); +- *format = TRACKER_SERIALIZER_FORMAT_JSON; +- return TRUE; +- } else if (soup_message_headers_header_contains (request_headers, "Accept", XML_TYPE)) { +- soup_message_headers_set_content_type (response_headers, XML_TYPE, NULL); +- *format = TRACKER_SERIALIZER_FORMAT_XML; +- return TRUE; +- } else { +- return FALSE; ++ TrackerSerializerFormat i; ++ ++ for (i = 0; i < TRACKER_N_SERIALIZER_FORMATS; i++) { ++ if ((formats & (1 << i)) != 0) { ++ *format = i; ++ return TRUE; ++ } + } + + return FALSE; + } + +-#if SOUP_CHECK_VERSION (2, 99, 2) + static void +-server_callback (SoupServer *server, +- SoupServerMessage *message, +- const char *path, +- GHashTable *query, +- gpointer user_data) +-#else +-static void +-server_callback (SoupServer *server, +- SoupMessage *message, +- const char *path, +- GHashTable *query, +- SoupClientContext *client, +- gpointer user_data) +-#endif ++http_server_request_cb (TrackerHttpServer *server, ++ GSocketAddress *remote_address, ++ const gchar *path, ++ GHashTable *params, ++ guint formats, ++ TrackerHttpRequest *request, ++ gpointer user_data) + { + TrackerEndpoint *endpoint = user_data; + TrackerSparqlConnection *conn; + TrackerSerializerFormat format; +- GSocketAddress *remote_address; + gboolean block = FALSE; + const gchar *sparql; +- Request *request; ++ Request *data; + +-#if SOUP_CHECK_VERSION (2, 99, 2) +- remote_address = soup_server_message_get_remote_address (message); +-#else +- remote_address = soup_client_context_get_remote_address (client); +-#endif + if (remote_address) { + g_signal_emit (endpoint, signals[BLOCK_REMOTE_ADDRESS], 0, + remote_address, &block); + } + + if (block) { +-#if SOUP_CHECK_VERSION (2, 99, 2) +- soup_server_message_set_status (message, 500, "Remote address disallowed"); +-#else +- soup_message_set_status_full (message, 500, "Remote address disallowed"); +-#endif ++ tracker_http_server_error (server, request, 500, ++ "Remote address disallowed"); + return; + } + +- sparql = g_hash_table_lookup (query, "query"); ++ sparql = g_hash_table_lookup (params, "query"); + if (!sparql) { +-#if SOUP_CHECK_VERSION (2, 99, 2) +- soup_server_message_set_status (message, 500, "No query given"); +-#else +- soup_message_set_status_full (message, 500, "No query given"); +-#endif ++ tracker_http_server_error (server, request, 500, ++ "No query given"); + return; + } + +- if (!pick_format (message, &format)) { +-#if SOUP_CHECK_VERSION (2, 99, 2) +- soup_server_message_set_status (message, 500, "No recognized accepted formats"); +-#else +- soup_message_set_status_full (message, 500, "No recognized accepted formats"); +-#endif ++ if (!pick_format (formats, &format)) { ++ tracker_http_server_error (server, request, 500, ++ "No recognized accepted formats"); + return; + } + +- request = g_new0 (Request, 1); +- request->endpoint = endpoint; +- request->message = message; +- request->format = format; ++ data = g_new0 (Request, 1); ++ data->endpoint = endpoint; ++ data->request = request; ++ data->format = format; + + conn = tracker_endpoint_get_sparql_connection (endpoint); + tracker_sparql_connection_query_async (conn, + sparql, + NULL, + query_async_cb, +- request); +- +- soup_server_pause_message (server, message); ++ data); + } + + static gboolean +@@ -309,18 +183,16 @@ tracker_endpoint_http_initable_init (GInitable *initable, + TrackerEndpointHttp *endpoint_http = TRACKER_ENDPOINT_HTTP (endpoint); + + endpoint_http->server = +- soup_server_new ("tls-certificate", endpoint_http->certificate, +- "server-header", SERVER_HEADER, +- NULL); +- soup_server_add_handler (endpoint_http->server, +- "/sparql", +- server_callback, +- initable, +- NULL); +- +- return soup_server_listen_all (endpoint_http->server, +- endpoint_http->port, +- 0, error); ++ tracker_http_server_new (endpoint_http->port, ++ endpoint_http->certificate, ++ cancellable, ++ error); ++ if (!endpoint_http->server) ++ return FALSE; ++ ++ g_signal_connect (endpoint_http->server, "request", ++ G_CALLBACK (http_server_request_cb), initable); ++ return TRUE; + } + + static void +-- +2.38.1 + diff --git a/SPECS/tracker.spec b/SPECS/tracker.spec index e63fb46..718797e 100644 --- a/SPECS/tracker.spec +++ b/SPECS/tracker.spec @@ -4,13 +4,15 @@ Name: tracker Version: 3.1.2 -Release: 2%{?dist} +Release: 3%{?dist} Summary: Desktop-neutral metadata database and search tool License: GPLv2+ URL: https://gnome.pages.gitlab.gnome.org/tracker/ Source0: https://download.gnome.org/sources/tracker/3.1/tracker-%{tarball_version}.tar.xz +Patch1: modular-soup.diff + BuildRequires: asciidoc BuildRequires: gettext BuildRequires: gtk-doc @@ -146,6 +148,7 @@ The %{name}-devel package contains the documentation for %{name}. %dir %{_libdir}/girepository-1.0 %{_libdir}/girepository-1.0/Tracker-3.0.typelib %{_libdir}/libtracker-sparql-3.0.so.0* +%{_libdir}/tracker-3.0/libtracker-http-soup*.so %{_datadir}/tracker3/ %files devel @@ -169,6 +172,10 @@ The %{name}-devel package contains the documentation for %{name}. %changelog +* Tue Nov 22 2022 Carlos Garnacho - 3.1.2-3 +- Backport module-based libsoup support + Resolves: rhbz#2137366 + * Tue Nov 30 2021 Tomas Popela - 3.1.2-2 - Ship tracker-devel in CRB