diff options
18 files changed, 2830 insertions, 1016 deletions
diff --git a/src/libtracker-common/libtracker-common.vapi b/src/libtracker-common/libtracker-common.vapi deleted file mode 100644 index 9e4298897..000000000 --- a/src/libtracker-common/libtracker-common.vapi +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2008-2009, Nokia - * - * 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. - */ - -[CCode (cprefix = "Tracker", gir_namespace = "Tracker", - gir_version = "2.0", lower_case_cprefix = "tracker_")] -namespace Tracker { - [CCode (cheader_filename = "libtracker-common/tracker-date-time.h")] - public double string_to_date (string date_string, out int offset) throws DateError; - - [CCode (cheader_filename = "libtracker-common/tracker-utils.h")] - public string? util_build_dbus_uri (GLib.BusType bus_type, string service, string? path); - - [CCode (cheader_filename = "libtracker-common/tracker-date-time.h")] - public errordomain DateError { - OFFSET, - INVALID_ISO8601 - } - - [CCode (cheader_filename = "libtracker-common/tracker-common.h")] - public class ConfigFile : GLib.Object { - [CCode (has_construct_function = false)] - public ConfigFile (); - [NoAccessorMethod] - public string domain { get; construct; } - public bool save (); - public virtual signal void changed (); - public GLib.File file; - public GLib.FileMonitor monitor; - public bool file_exists; - public GLib.KeyFile key_file; - } - - [CCode (cheader_filename = "libtracker-common/tracker-common.h")] - public class KeyfileObject { - public static string blurb (void *object, string property); - public static bool default_boolean (void *object, string property); - public static int default_int (void *object, string property); - public static bool validate_int (void *object, string propery, int value); - public static void load_int (void *object, string property, GLib.KeyFile key_file, string group, string key); - public static void load_boolean (void *object, string property, GLib.KeyFile key_file, string group, string key); - public static void load_string (void *object, string property, GLib.KeyFile key_file, string group, string key); - public static void load_string_list (void *object, string property, GLib.KeyFile key_file, string group, string key, out GLib.SList return_instead); - public static void load_directory_list (void *object, string property, GLib.KeyFile key_file, string group, string key, bool is_recursive, out GLib.SList return_instead); - public static void save_int (void *object, string property, GLib.KeyFile key_file, string group, string key); - public static void save_boolean (void *object, string property, GLib.KeyFile key_file, string group, string key); - public static void save_string (void *object, string property, GLib.KeyFile key_file, string group, string key); - public static void save_string_list (void *object, string property, GLib.KeyFile key_file, string group, string key); - public static void save_directory_list (void *object, string property, GLib.KeyFile key_file, string group, string key); - } - - [CCode (cheader_filename = "libtracker-common/tracker-locale.h")] - namespace Locale { - public void sanity_check (); - } - - [Compact] - [CCode (cheader_filename = "libtracker-common/tracker-common.h")] - namespace IPC { - public GLib.BusType bus (); - } - - [Compact] - [CCode (ref_function = "", unref_function = "", cheader_filename = "libtracker-common/tracker-common.h")] - public class DBusRequest { - public static DBusRequest begin (string? sender, string format,...); - public void debug (string format,...); - public void end (GLib.Error? e = null); - [CCode (cname = "tracker_dbus_enable_client_lookup")] - public static void enable_client_lookup (bool enable); - } - - [CCode (ref_function = "tracker_domain_ontology_ref", unref_function = "tracker_domain_ontology_unref", cheader_filename = "libtracker-common/tracker-domain-ontology.h")] - public class DomainOntology { - public DomainOntology (string? name, GLib.Cancellable? cancellable) throws GLib.Error; - public GLib.File get_cache (); - public GLib.File? get_journal (); - public GLib.File get_ontology (); - public string get_domain (string? suffix = null); - } - - [CCode (cheader_filename = "libtracker-common/tracker-common.h")] - public void ioprio_init (); - - [CCode (cheader_filename = "libtracker-common/tracker-debug.h")] - public uint get_debug_flags (); - - [CCode (cname = "g_message", cheader_filename = "glib.h")] - [PrintfFormat] - public void message (string format, ...); - [CCode (cname = "g_warning", cheader_filename = "glib.h")] - [PrintfFormat] - public void warning (string format, ...); - [CCode (cname = "g_critical", cheader_filename = "glib.h")] - [PrintfFormat] - public void critical (string format, ...); - [CCode (cname = "g_error", cheader_filename = "glib.h")] - [PrintfFormat] - [NoReturn] - public void error (string format, ...); - [CCode (cname = "g_debug", cheader_filename = "glib.h")] - [PrintfFormat] - public void debug (string format, ...); -} - diff --git a/src/libtracker-sparql/bus/.gitignore b/src/libtracker-sparql/bus/.gitignore deleted file mode 100644 index c34fe4c56..000000000 --- a/src/libtracker-sparql/bus/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -tracker-namespace.c -tracker-bus.[ch] -tracker-bus*.vapi -tracker-array-cursor.c -tracker-bus-fd-cursor.c diff --git a/src/libtracker-sparql/bus/meson.build b/src/libtracker-sparql/bus/meson.build index 6852171d0..28534e006 100644 --- a/src/libtracker-sparql/bus/meson.build +++ b/src/libtracker-sparql/bus/meson.build @@ -1,7 +1,6 @@ bus_files = files( - 'tracker-bus.vala', - 'tracker-namespace.vala', - 'tracker-bus-batch.vala', - 'tracker-bus-fd-cursor.vala', - 'tracker-bus-statement.vala', + 'tracker-bus.c', + 'tracker-bus-batch.c', + 'tracker-bus-cursor.c', + 'tracker-bus-statement.c', ) diff --git a/src/libtracker-sparql/bus/tracker-bus-batch.c b/src/libtracker-sparql/bus/tracker-bus-batch.c new file mode 100644 index 000000000..b2f144ca3 --- /dev/null +++ b/src/libtracker-sparql/bus/tracker-bus-batch.c @@ -0,0 +1,192 @@ +/* + * 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 <carlosg@gnome.org> + */ + +#include "config.h" + +#include "tracker-bus-batch.h" + +struct _TrackerBusBatch +{ + TrackerBatch parent_instance; + GPtrArray *updates; +}; + +typedef struct { + GMainLoop *loop; + gboolean retval; + GError *error; +} ExecuteAsyncData; + +G_DEFINE_TYPE (TrackerBusBatch, tracker_bus_batch, TRACKER_TYPE_BATCH) + +static void +tracker_bus_batch_finalize (GObject *object) +{ + TrackerBusBatch *bus_batch = TRACKER_BUS_BATCH (object); + + g_ptr_array_unref (bus_batch->updates); + + G_OBJECT_CLASS (tracker_bus_batch_parent_class)->finalize (object); +} + +static void +tracker_bus_batch_add_sparql (TrackerBatch *batch, + const gchar *sparql) +{ + TrackerBusBatch *bus_batch = TRACKER_BUS_BATCH (batch); + + g_ptr_array_add (bus_batch->updates, g_strdup (sparql)); +} + +static void +tracker_bus_batch_add_resource (TrackerBatch *batch, + const gchar *graph, + TrackerResource *resource) +{ + TrackerBusBatch *bus_batch = TRACKER_BUS_BATCH (batch); + TrackerSparqlConnection *conn; + TrackerNamespaceManager *namespaces; + gchar *sparql; + + conn = tracker_batch_get_connection (batch); + namespaces = tracker_sparql_connection_get_namespace_manager (conn); + sparql = tracker_resource_print_sparql_update (resource, namespaces, graph); + g_ptr_array_add (bus_batch->updates, sparql); +} + +static void +execute_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + ExecuteAsyncData *data = user_data; + + data->retval = + tracker_batch_execute_finish (TRACKER_BATCH (source), + res, &data->error); + g_main_loop_quit (data->loop); +} + +static gboolean +tracker_bus_batch_execute (TrackerBatch *batch, + GCancellable *cancellable, + GError **error) +{ + GMainContext *context; + ExecuteAsyncData data = { 0, }; + + context = g_main_context_new (); + data.loop = g_main_loop_new (context, FALSE); + g_main_context_push_thread_default (context); + + tracker_batch_execute_async (batch, + cancellable, + execute_cb, + &data); + + g_main_loop_run (data.loop); + + g_main_context_pop_thread_default (context); + + g_main_loop_unref (data.loop); + + if (data.error) { + g_propagate_error (error, data.error); + return FALSE; + } + + return data.retval; +} + +static void +update_array_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = user_data; + GError *error = NULL; + + if (!tracker_sparql_connection_update_array_finish (TRACKER_SPARQL_CONNECTION (source), + res, &error)) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + + g_object_unref (task); +} + +static void +tracker_bus_batch_execute_async (TrackerBatch *batch, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TrackerBusBatch *bus_batch = TRACKER_BUS_BATCH (batch); + TrackerSparqlConnection *conn; + GTask *task; + + task = g_task_new (batch, cancellable, callback, user_data); + conn = tracker_batch_get_connection (batch); + tracker_sparql_connection_update_array_async (conn, + (gchar **) bus_batch->updates->pdata, + bus_batch->updates->len, + cancellable, + update_array_cb, + task); +} + +static gboolean +tracker_bus_batch_execute_finish (TrackerBatch *batch, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +tracker_bus_batch_class_init (TrackerBusBatchClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + TrackerBatchClass *batch_class = TRACKER_BATCH_CLASS (klass); + + object_class->finalize = tracker_bus_batch_finalize; + + batch_class->add_sparql = tracker_bus_batch_add_sparql; + batch_class->add_resource = tracker_bus_batch_add_resource; + batch_class->execute = tracker_bus_batch_execute; + batch_class->execute_async = tracker_bus_batch_execute_async; + batch_class->execute_finish = tracker_bus_batch_execute_finish; +} + +static void +tracker_bus_batch_init (TrackerBusBatch *batch) +{ + batch->updates = g_ptr_array_new (); + g_ptr_array_set_free_func (batch->updates, g_free); +} + +TrackerBatch * +tracker_bus_batch_new (TrackerBusConnection *connection) +{ + return g_object_new (TRACKER_TYPE_BUS_BATCH, + "connection", connection, + NULL); +} diff --git a/src/libtracker-sparql/bus/tracker-bus-batch.h b/src/libtracker-sparql/bus/tracker-bus-batch.h new file mode 100644 index 000000000..97be326c9 --- /dev/null +++ b/src/libtracker-sparql/bus/tracker-bus-batch.h @@ -0,0 +1,37 @@ +/* + * 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 <carlosg@gnome.org> + */ + +#ifndef __TRACKER_BUS_BATCH_H__ +#define __TRACKER_BUS_BATCH_H__ + +#include "tracker-bus.h" + +#include <libtracker-sparql/tracker-private.h> + +#define TRACKER_TYPE_BUS_BATCH (tracker_bus_batch_get_type ()) +G_DECLARE_FINAL_TYPE (TrackerBusBatch, + tracker_bus_batch, + TRACKER, BUS_BATCH, + TrackerBatch) + +TrackerBatch * tracker_bus_batch_new (TrackerBusConnection *connection); + +#endif /* __TRACKER_BUS_BATCH_H__ */ diff --git a/src/libtracker-sparql/bus/tracker-bus-batch.vala b/src/libtracker-sparql/bus/tracker-bus-batch.vala deleted file mode 100644 index 1993775b3..000000000 --- a/src/libtracker-sparql/bus/tracker-bus-batch.vala +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 <carlosg@gnome.org> - */ - -public class Tracker.Bus.Batch : Tracker.Batch { - private DBusConnection bus; - private string dbus_name; - private string object_path; - private string[] updates; - - public Batch (DBusConnection bus, string dbus_name, string object_path) { - Object (); - this.bus = bus; - this.dbus_name = dbus_name; - this.object_path = object_path; - } - - public override void add_sparql (string sparql) { - updates += sparql; - } - - public override void add_resource (string? graph, Resource resource) { - var namespaces = this.connection.get_namespace_manager (); - var sparql = resource.print_sparql_update (namespaces, graph); - updates += sparql; - } - - public override bool execute (GLib.Cancellable? cancellable) throws Sparql.Error, GLib.Error, GLib.IOError, GLib.DBusError { - // use separate main context for sync operation - var context = new MainContext (); - var loop = new MainLoop (context, false); - context.push_thread_default (); - AsyncResult async_res = null; - execute_async.begin (cancellable, (o, res) => { - async_res = res; - loop.quit (); - }); - loop.run (); - context.pop_thread_default (); - return execute_async.end (async_res); - } - - public async override bool execute_async (GLib.Cancellable? cancellable) throws Sparql.Error, GLib.Error, GLib.IOError, GLib.DBusError { - return yield Tracker.Bus.Connection.perform_update_array (bus, dbus_name, object_path, updates, cancellable); - } -} diff --git a/src/libtracker-sparql/bus/tracker-bus-cursor.c b/src/libtracker-sparql/bus/tracker-bus-cursor.c new file mode 100644 index 000000000..0a006683e --- /dev/null +++ b/src/libtracker-sparql/bus/tracker-bus-cursor.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2010, Nokia <ivan.frade@nokia.com> + * + * 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. + */ + +#include "config.h" + +#include "tracker-bus-cursor.h" + +struct _TrackerBusCursor +{ + TrackerDeserializer parent_instance; + GDataInputStream *data_stream; + GVariant *variables; + const gchar **variable_names; + gint n_columns; + TrackerSparqlValueType *types; + gchar *row_data; + const gchar **values; + gboolean finished; +}; + +enum { + PROP_0, + PROP_VARIABLES, + N_PROPS +}; + +static GParamSpec *props[N_PROPS] = { 0, }; + +G_DEFINE_TYPE (TrackerBusCursor, + tracker_bus_cursor, + TRACKER_TYPE_DESERIALIZER) + +static void +tracker_bus_cursor_finalize (GObject *object) +{ + TrackerBusCursor *bus_cursor = TRACKER_BUS_CURSOR (object); + + g_clear_object (&bus_cursor->data_stream); + g_clear_pointer (&bus_cursor->variables, g_variant_unref); + g_clear_pointer (&bus_cursor->types, g_free); + g_clear_pointer (&bus_cursor->row_data, g_free); + g_clear_pointer (&bus_cursor->values, g_free); + g_clear_pointer (&bus_cursor->variable_names, g_free); + + G_OBJECT_CLASS (tracker_bus_cursor_parent_class)->finalize (object); +} + +static void +tracker_bus_cursor_constructed (GObject *object) +{ + TrackerDeserializer *deserializer = TRACKER_DESERIALIZER (object); + TrackerBusCursor *cursor = TRACKER_BUS_CURSOR (object); + + G_OBJECT_CLASS (tracker_bus_cursor_parent_class)->constructed (object); + + cursor->variable_names = g_variant_get_strv (cursor->variables, NULL); + cursor->n_columns = g_strv_length ((gchar **) cursor->variable_names); + cursor->data_stream = + g_data_input_stream_new (tracker_deserializer_get_stream (deserializer)); + g_data_input_stream_set_byte_order (cursor->data_stream, + G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN); +} + +static void +tracker_bus_cursor_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TrackerBusCursor *cursor = TRACKER_BUS_CURSOR (object); + + switch (prop_id) { + case PROP_VARIABLES: + cursor->variables = g_value_dup_variant (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +tracker_bus_cursor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TrackerBusCursor *cursor = TRACKER_BUS_CURSOR (object); + + switch (prop_id) { + case PROP_VARIABLES: + g_value_set_variant (value, cursor->variables); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gint +tracker_bus_cursor_get_n_columns (TrackerSparqlCursor *cursor) +{ + TrackerBusCursor *bus_cursor = TRACKER_BUS_CURSOR (cursor); + + return bus_cursor->n_columns; +} + +static TrackerSparqlValueType +tracker_bus_cursor_get_value_type (TrackerSparqlCursor *cursor, + gint column) +{ + TrackerBusCursor *bus_cursor = TRACKER_BUS_CURSOR (cursor); + + if (bus_cursor->finished) + return TRACKER_SPARQL_VALUE_TYPE_UNBOUND; + if (column < 0 || column >= bus_cursor->n_columns) + return TRACKER_SPARQL_VALUE_TYPE_UNBOUND; + if (!bus_cursor->types) + return TRACKER_SPARQL_VALUE_TYPE_UNBOUND; + + return bus_cursor->types[column]; +} + +static const gchar * +tracker_bus_cursor_get_variable_name (TrackerSparqlCursor *cursor, + gint column) +{ + TrackerBusCursor *bus_cursor = TRACKER_BUS_CURSOR (cursor); + + if (bus_cursor->finished) + return NULL; + if (column < 0 || column >= bus_cursor->n_columns) + return NULL; + + return bus_cursor->variable_names[column]; +} + +static const gchar * +tracker_bus_cursor_get_string (TrackerSparqlCursor *cursor, + gint column, + glong *len) +{ + TrackerBusCursor *bus_cursor = TRACKER_BUS_CURSOR (cursor); + const gchar *str; + + if (bus_cursor->finished) + return NULL; + if (column < 0 || column >= bus_cursor->n_columns) + return NULL; + if (!bus_cursor->types) + return NULL; + + /* Return null instead of empty string for unbound values */ + if (bus_cursor->types[column] == TRACKER_SPARQL_VALUE_TYPE_UNBOUND) + return NULL; + + str = bus_cursor->values[column]; + + if (len) + *len = strlen (str); + + return str; +} + +static gboolean +tracker_bus_cursor_next (TrackerSparqlCursor *cursor, + GCancellable *cancellable, + GError **error) +{ + TrackerBusCursor *bus_cursor = TRACKER_BUS_CURSOR (cursor); + gint32 *offsets, n_columns, i; + + if (bus_cursor->finished) + return FALSE; + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + /* So, the make up on each cursor segment is: + * + * iteration = [4 bytes for number of columns, + * columns x 4 bytes for types + * columns x 4 bytes for offsets] + */ + n_columns = g_data_input_stream_read_int32 (bus_cursor->data_stream, + NULL, NULL); + + if (n_columns == 0) { + bus_cursor->finished = TRUE; + return FALSE; + } + + g_clear_pointer (&bus_cursor->types, g_free); + bus_cursor->types = g_new0 (TrackerSparqlValueType, n_columns); + g_input_stream_read_all (G_INPUT_STREAM (bus_cursor->data_stream), + bus_cursor->types, + n_columns * sizeof (gint32), + NULL, NULL, NULL); + + offsets = g_new0 (gint32, n_columns); + g_input_stream_read_all (G_INPUT_STREAM (bus_cursor->data_stream), + offsets, + n_columns * sizeof (gint32), + NULL, NULL, NULL); + + /* The last offset says how long we have to go to read + * the whole row data. + */ + g_clear_pointer (&bus_cursor->row_data, g_free); + bus_cursor->row_data = + g_new0 (gchar, offsets[n_columns - 1] + 1); + g_input_stream_read_all (G_INPUT_STREAM (bus_cursor->data_stream), + bus_cursor->row_data, + offsets[n_columns - 1] + 1, + NULL, NULL, NULL); + + g_clear_pointer (&bus_cursor->values, g_free); + bus_cursor->values = g_new0 (const gchar *, n_columns); + + for (i = 0; i < n_columns; i++) { + if (i == 0) + bus_cursor->values[i] = bus_cursor->row_data; + else + bus_cursor->values[i] = &bus_cursor->row_data[offsets[i - 1]] + 1; + } + + g_free (offsets); + + return TRUE; +} + +static void +next_in_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GError *error = NULL; + gboolean retval; + + retval = tracker_sparql_cursor_next (source_object, cancellable, &error); + + if (error) + g_task_return_error (task, error); + else + g_task_return_boolean (task, retval); +} + +static void +tracker_bus_cursor_next_async (TrackerSparqlCursor *cursor, + GCancellable *cancellable, + GAsyncReadyCallback cb, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (cursor, cancellable, cb, user_data); + g_task_run_in_thread (task, next_in_thread); +} + +static gboolean +tracker_bus_cursor_next_finish (TrackerSparqlCursor *cursor, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +tracker_bus_cursor_rewind (TrackerSparqlCursor *cursor) +{ + TrackerBusCursor *bus_cursor = TRACKER_BUS_CURSOR (cursor); + + g_clear_pointer (&bus_cursor->types, g_free); + g_clear_pointer (&bus_cursor->row_data, g_free); + + if (g_seekable_can_seek (G_SEEKABLE (bus_cursor->data_stream))) { + g_seekable_seek (G_SEEKABLE (bus_cursor->data_stream), + 0, G_SEEK_SET, NULL, NULL); + } + + bus_cursor->finished = FALSE; +} + +static void +tracker_bus_cursor_close (TrackerSparqlCursor *cursor) +{ + TrackerBusCursor *bus_cursor = TRACKER_BUS_CURSOR (cursor); + + g_input_stream_close (G_INPUT_STREAM (bus_cursor->data_stream), + NULL, NULL); + + TRACKER_SPARQL_CURSOR_CLASS (tracker_bus_cursor_parent_class)->close (cursor); +} + +static void +tracker_bus_cursor_class_init (TrackerBusCursorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + TrackerSparqlCursorClass *cursor_class = + TRACKER_SPARQL_CURSOR_CLASS (klass); + + object_class->finalize = tracker_bus_cursor_finalize; + object_class->constructed = tracker_bus_cursor_constructed; + object_class->set_property = tracker_bus_cursor_set_property; + object_class->get_property = tracker_bus_cursor_get_property; + + cursor_class->get_n_columns = tracker_bus_cursor_get_n_columns; + cursor_class->get_value_type = tracker_bus_cursor_get_value_type; + cursor_class->get_variable_name = tracker_bus_cursor_get_variable_name; + cursor_class->get_string = tracker_bus_cursor_get_string; + cursor_class->next = tracker_bus_cursor_next; + cursor_class->next_async = tracker_bus_cursor_next_async; + cursor_class->next_finish = tracker_bus_cursor_next_finish; + cursor_class->rewind = tracker_bus_cursor_rewind; + cursor_class->close = tracker_bus_cursor_close; + + props[PROP_VARIABLES] = + g_param_spec_variant ("variables", + "Variables", + "Variables", + G_VARIANT_TYPE ("as"), + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (object_class, N_PROPS, props); + +} + +static void +tracker_bus_cursor_init (TrackerBusCursor *cursor) +{ +} + +TrackerSparqlCursor * +tracker_bus_cursor_new (GInputStream *stream, + GVariant *variables) + +{ + return g_object_new (TRACKER_TYPE_BUS_CURSOR, + "stream", stream, + "variables", variables, + NULL); +} diff --git a/src/libtracker-sparql/bus/tracker-namespace.vala b/src/libtracker-sparql/bus/tracker-bus-cursor.h index 9e0a404de..d5b6532cc 100644 --- a/src/libtracker-sparql/bus/tracker-namespace.vala +++ b/src/libtracker-sparql/bus/tracker-bus-cursor.h @@ -1,5 +1,5 @@ /* - * Copyright © 2015 Collabora Ltd. + * Copyright (C) 2010, Nokia <ivan.frade@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,14 +16,18 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ +#ifndef __TRACKER_BUS_CURSOR_H__ +#define __TRACKER_BUS_CURSOR_H__ -/* - * This file serves as the representation for the Tracker namespace, mostly - * so that we can set its namespace and version attributes for GIR. - */ +#include <libtracker-sparql/tracker-sparql.h> + +#define TRACKER_TYPE_BUS_CURSOR (tracker_bus_cursor_get_type ()) +G_DECLARE_FINAL_TYPE (TrackerBusCursor, + tracker_bus_cursor, + TRACKER, BUS_CURSOR, + TrackerDeserializer); + +TrackerSparqlCursor *tracker_bus_cursor_new (GInputStream *stream, + GVariant *variables); -[CCode (cprefix = "TrackerBus", gir_namespace = "TrackerBus", - gir_version = "2.0", lower_case_cprefix = "tracker_bus_")] -namespace Tracker -{ -} +#endif /* __TRACKER_BUS_CURSOR_H__ */ diff --git a/src/libtracker-sparql/bus/tracker-bus-fd-cursor.vala b/src/libtracker-sparql/bus/tracker-bus-fd-cursor.vala deleted file mode 100644 index 954ad1ec7..000000000 --- a/src/libtracker-sparql/bus/tracker-bus-fd-cursor.vala +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2010, Nokia <ivan.frade@nokia.com> - * - * 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. - */ - -class Tracker.Bus.FDCursor : Tracker.Sparql.Cursor { - internal char* buffer; - internal ulong buffer_index; - internal ulong buffer_size; - - internal int _n_columns; - internal int* offsets; - internal int* types; - internal char* data; - internal string[] variable_names; - internal bool cursor_finished; - - public FDCursor (char* buffer, ulong buffer_size, string[] variable_names) { - Object (); - this.buffer = buffer; - this.buffer_size = buffer_size; - this.variable_names = variable_names; - this.cursor_finished = true; - _n_columns = variable_names.length; - } - - ~FDCursor () { - free (buffer); - } - - inline int buffer_read_int () { - int v = *((int*) (buffer + buffer_index)); - - buffer_index += 4; - - return v; - } - - public override int n_columns { - get { return _n_columns; } - } - - public override Sparql.ValueType get_value_type (int column) - requires (types != null) { - if (column >= n_columns) { - return Sparql.ValueType.UNBOUND; - } - - /* Cast from int to enum */ - return (Sparql.ValueType) types[column]; - } - - public override unowned string? get_variable_name (int column) - requires (variable_names != null) { - return variable_names[column]; - } - - public override unowned string? get_string (int column, out long length = null) - requires (cursor_finished == false) { - unowned string str = null; - - if (column >= n_columns) { - length = 0; - return null; - } - - // return null instead of empty string for unbound values - if (types[column] == Sparql.ValueType.UNBOUND) { - length = 0; - return null; - } - - if (column == 0) { - str = (string) data; - } else { - str = (string) (data + offsets[column - 1] + 1); - } - - length = str.length; - - return str; - } - - public override bool next (Cancellable? cancellable = null) throws GLib.Error { - int last_offset; - - if (cancellable != null && cancellable.is_cancelled ()) { - throw new IOError.CANCELLED ("Operation was cancelled"); - } - - if (buffer_index >= buffer_size) { - cursor_finished = true; - data = null; - return false; - } - - /* So, the make up on each cursor segment is: - * - * iteration = [4 bytes for number of columns, - * columns x 4 bytes for types - * columns x 4 bytes for offsets] - */ - - _n_columns = buffer_read_int (); - - /* Storage of ints that will be cast to TrackerSparqlValueType enums, - * also see get_value_type */ - types = (int*) (buffer + buffer_index); - buffer_index += sizeof (int) * n_columns; - - offsets = (int*) (buffer + buffer_index); - buffer_index += sizeof (int) * (n_columns - 1); - last_offset = buffer_read_int (); - - data = buffer + buffer_index; - cursor_finished = false; - - buffer_index += last_offset + 1; - - return true; - } - - public override async bool next_async (Cancellable? cancellable = null) throws GLib.Error { - // next never blocks - return next (cancellable); - } - - public override void rewind () { - buffer_index = 0; - data = buffer; - cursor_finished = false; - } - - public override void close () { - } -} diff --git a/src/libtracker-sparql/bus/tracker-bus-statement.c b/src/libtracker-sparql/bus/tracker-bus-statement.c new file mode 100644 index 000000000..5ec6a7c2c --- /dev/null +++ b/src/libtracker-sparql/bus/tracker-bus-statement.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2020, 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 <carlosg@gnome.org> + */ + +#include "config.h" + +#include <libtracker-common/tracker-common.h> + +#include "tracker-bus-statement.h" + +struct _TrackerBusStatement +{ + TrackerSparqlStatement parent_instance; + GHashTable *arguments; +}; + +G_DEFINE_TYPE (TrackerBusStatement, + tracker_bus_statement, + TRACKER_TYPE_SPARQL_STATEMENT) + +static void +tracker_bus_statement_finalize (GObject *object) +{ + TrackerBusStatement *bus_stmt = TRACKER_BUS_STATEMENT (object); + + g_hash_table_unref (bus_stmt->arguments); + + G_OBJECT_CLASS (tracker_bus_statement_parent_class)->finalize (object); +} + +static void +tracker_bus_statement_bind_string (TrackerSparqlStatement *stmt, + const gchar *name, + const gchar *value) +{ + TrackerBusStatement *bus_stmt = TRACKER_BUS_STATEMENT (stmt); + + g_hash_table_insert (bus_stmt->arguments, + g_strdup (name), + g_variant_new_string (value)); +} + +static void +tracker_bus_statement_bind_boolean (TrackerSparqlStatement *stmt, + const gchar *name, + gboolean value) +{ + TrackerBusStatement *bus_stmt = TRACKER_BUS_STATEMENT (stmt); + + g_hash_table_insert (bus_stmt->arguments, + g_strdup (name), + g_variant_new_boolean (value)); +} + +static void +tracker_bus_statement_bind_double (TrackerSparqlStatement *stmt, + const gchar *name, + gdouble value) +{ + TrackerBusStatement *bus_stmt = TRACKER_BUS_STATEMENT (stmt); + + g_hash_table_insert (bus_stmt->arguments, + g_strdup (name), + g_variant_new_double (value)); +} + +static void +tracker_bus_statement_bind_int (TrackerSparqlStatement *stmt, + const gchar *name, + gint64 value) +{ + TrackerBusStatement *bus_stmt = TRACKER_BUS_STATEMENT (stmt); + + g_hash_table_insert (bus_stmt->arguments, + g_strdup (name), + g_variant_new_int64 (value)); +} + +static void +tracker_bus_statement_bind_datetime (TrackerSparqlStatement *stmt, + const gchar *name, + GDateTime *value) +{ + TrackerBusStatement *bus_stmt = TRACKER_BUS_STATEMENT (stmt); + gchar *date_str; + + date_str = tracker_date_format_iso8601 (value); + g_hash_table_insert (bus_stmt->arguments, + g_strdup (name), + g_variant_new_string (date_str)); + g_free (date_str); +} + +static void +tracker_bus_statement_clear_bindings (TrackerSparqlStatement *stmt) +{ + TrackerBusStatement *bus_stmt = TRACKER_BUS_STATEMENT (stmt); + + g_hash_table_remove_all (bus_stmt->arguments); +} + +static GVariant * +get_arguments (TrackerBusStatement *bus_stmt) +{ + GVariantBuilder builder; + GHashTableIter iter; + gpointer key, value; + + if (g_hash_table_size (bus_stmt->arguments) == 0) + return NULL; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + g_hash_table_iter_init (&iter, bus_stmt->arguments); + + while (g_hash_table_iter_next (&iter, &key, &value)) + g_variant_builder_add (&builder, "{sv}", key, g_variant_ref_sink (value)); + + return g_variant_builder_end (&builder); +} + +static TrackerSparqlCursor * +tracker_bus_statement_execute (TrackerSparqlStatement *stmt, + GCancellable *cancellable, + GError **error) +{ + TrackerBusStatement *bus_stmt = TRACKER_BUS_STATEMENT (stmt); + TrackerSparqlConnection *conn; + + conn = tracker_sparql_statement_get_connection (stmt); + + return tracker_bus_connection_perform_query (TRACKER_BUS_CONNECTION (conn), + tracker_sparql_statement_get_sparql (stmt), + get_arguments (bus_stmt), + cancellable, error); +} + +static void +execute_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + TrackerSparqlCursor *cursor; + GError *error = NULL; + GTask *task = user_data; + + cursor = tracker_bus_connection_perform_query_finish (TRACKER_BUS_CONNECTION (source), + res, &error); + if (cursor) + g_task_return_pointer (task, cursor, g_object_unref); + else + g_task_return_error (task, error); + + g_object_unref (task); +} + +static void +tracker_bus_statement_execute_async (TrackerSparqlStatement *stmt, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TrackerBusStatement *bus_stmt = TRACKER_BUS_STATEMENT (stmt); + TrackerSparqlConnection *conn; + GTask *task; + + task = g_task_new (stmt, cancellable, callback, user_data); + conn = tracker_sparql_statement_get_connection (stmt); + tracker_bus_connection_perform_query_async (TRACKER_BUS_CONNECTION (conn), + tracker_sparql_statement_get_sparql (stmt), + get_arguments (bus_stmt), + cancellable, + execute_cb, + task); +} + +static TrackerSparqlCursor * +tracker_bus_statement_execute_finish (TrackerSparqlStatement *stmt, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +serialize_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GInputStream *istream; + GError *error = NULL; + GTask *task = user_data; + + istream = tracker_bus_connection_perform_serialize_finish (TRACKER_BUS_CONNECTION (source), + res, &error); + if (istream) + g_task_return_pointer (task, istream, g_object_unref); + else + g_task_return_error (task, error); + + g_object_unref (task); +} + +static void +tracker_bus_statement_serialize_async (TrackerSparqlStatement *stmt, + TrackerSerializeFlags flags, + TrackerRdfFormat format, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TrackerBusStatement *bus_stmt = TRACKER_BUS_STATEMENT (stmt); + TrackerSparqlConnection *conn; + GTask *task; + + task = g_task_new (stmt, cancellable, callback, user_data); + conn = tracker_sparql_statement_get_connection (stmt); + tracker_bus_connection_perform_serialize_async (TRACKER_BUS_CONNECTION (conn), + flags, format, + tracker_sparql_statement_get_sparql (stmt), + get_arguments (bus_stmt), + cancellable, + serialize_cb, + task); +} + +static GInputStream * +tracker_bus_statement_serialize_finish (TrackerSparqlStatement *stmt, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +tracker_bus_statement_class_init (TrackerBusStatementClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + TrackerSparqlStatementClass *stmt_class = + TRACKER_SPARQL_STATEMENT_CLASS (klass); + + object_class->finalize = tracker_bus_statement_finalize; + + stmt_class->bind_boolean = tracker_bus_statement_bind_boolean; + stmt_class->bind_double = tracker_bus_statement_bind_double; + stmt_class->bind_int = tracker_bus_statement_bind_int; + stmt_class->bind_string = tracker_bus_statement_bind_string; + stmt_class->bind_datetime = tracker_bus_statement_bind_datetime; + stmt_class->clear_bindings = tracker_bus_statement_clear_bindings; + stmt_class->execute = tracker_bus_statement_execute; + stmt_class->execute_async = tracker_bus_statement_execute_async; + stmt_class->execute_finish = tracker_bus_statement_execute_finish; + stmt_class->serialize_async = tracker_bus_statement_serialize_async; + stmt_class->serialize_finish = tracker_bus_statement_serialize_finish; +} + +static void +tracker_bus_statement_init (TrackerBusStatement *stmt) +{ + stmt->arguments = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + (GDestroyNotify) g_variant_unref); +} + +TrackerSparqlStatement * +tracker_bus_statement_new (TrackerBusConnection *conn, + const gchar *query) +{ + return g_object_new (TRACKER_TYPE_BUS_STATEMENT, + "connection", conn, + "sparql", query, + NULL); +} diff --git a/src/libtracker-sparql/bus/tracker-bus-statement.h b/src/libtracker-sparql/bus/tracker-bus-statement.h new file mode 100644 index 000000000..31c7ea95d --- /dev/null +++ b/src/libtracker-sparql/bus/tracker-bus-statement.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020, 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 <carlosg@gnome.org> + */ + +#ifndef __TRACKER_BUS_STATEMENT_H__ +#define __TRACKER_BUS_STATEMENT_H__ + +#include "tracker-bus.h" + +#include <libtracker-sparql/tracker-private.h> + +#define TRACKER_TYPE_BUS_STATEMENT (tracker_bus_statement_get_type ()) +G_DECLARE_FINAL_TYPE (TrackerBusStatement, + tracker_bus_statement, + TRACKER, BUS_STATEMENT, + TrackerSparqlStatement) + +TrackerSparqlStatement * tracker_bus_statement_new (TrackerBusConnection *conn, + const gchar *sparql); + +#endif /* __TRACKER_BUS_STATEMENT_H__ */ diff --git a/src/libtracker-sparql/bus/tracker-bus-statement.vala b/src/libtracker-sparql/bus/tracker-bus-statement.vala deleted file mode 100644 index 7a2472135..000000000 --- a/src/libtracker-sparql/bus/tracker-bus-statement.vala +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2020, 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 <carlosg@gnome.org> - */ - -public class Tracker.Bus.Statement : Tracker.Sparql.Statement { - private DBusConnection bus; - private string query; - private string dbus_name; - private string object_path; - private HashTable<string,GLib.Variant> arguments; - - public Statement (Sparql.Connection connection, DBusConnection bus, string dbus_name, string object_path, string query) { - Object (connection: connection); - this.bus = bus; - this.dbus_name = dbus_name; - this.object_path = object_path; - this.query = query; - this.arguments = new HashTable<string, GLib.Variant> (str_hash, str_equal); - } - - public override void bind_boolean (string name, bool value) { - this.arguments.insert (name, new GLib.Variant.boolean (value)); - } - - public override void bind_double (string name, double value) { - this.arguments.insert (name, new GLib.Variant.double (value)); - } - - public override void bind_int (string name, int64 value) { - this.arguments.insert (name, new GLib.Variant.int64 (value)); - } - - public override void bind_string (string name, string value) { - this.arguments.insert (name, new GLib.Variant.string (value)); - } - - public override void bind_datetime (string name, DateTime value) { - string date_time = value.to_string (); - this.arguments.insert (name, new GLib.Variant.string (date_time)); - } - - public override void clear_bindings () { - this.arguments.remove_all (); - } - - private VariantBuilder? get_arguments () { - if (this.arguments.size () == 0) - return null; - - VariantBuilder builder = new VariantBuilder (new VariantType ("a{sv}")); - HashTableIter<string, Variant> iter = HashTableIter<string, Variant> (this.arguments); - unowned string arg; - unowned GLib.Variant value; - - while (iter.next (out arg, out value)) - builder.add ("{sv}", arg, value); - - return builder; - } - - public override Sparql.Cursor execute (GLib.Cancellable? cancellable) throws Sparql.Error, GLib.Error, GLib.IOError, GLib.DBusError { - // use separate main context for sync operation - var context = new MainContext (); - var loop = new MainLoop (context, false); - context.push_thread_default (); - AsyncResult async_res = null; - execute_async.begin (cancellable, (o, res) => { - async_res = res; - loop.quit (); - }); - loop.run (); - context.pop_thread_default (); - return execute_async.end (async_res); - } - - public async override Sparql.Cursor execute_async (GLib.Cancellable? cancellable) throws Sparql.Error, GLib.Error, GLib.IOError, GLib.DBusError { - return yield Tracker.Bus.Connection.perform_query_call (bus, dbus_name, object_path, query, get_arguments (), cancellable); - } - - public async override GLib.InputStream serialize_async (SerializeFlags flags, RdfFormat format, GLib.Cancellable? cancellable) throws Sparql.Error, GLib.Error, GLib.IOError, GLib.DBusError { - return yield Tracker.Bus.Connection.perform_serialize (bus, dbus_name, object_path, flags, format, query, get_arguments (), cancellable); - } -} diff --git a/src/libtracker-sparql/bus/tracker-bus.c b/src/libtracker-sparql/bus/tracker-bus.c new file mode 100644 index 000000000..7dcf645c6 --- /dev/null +++ b/src/libtracker-sparql/bus/tracker-bus.c @@ -0,0 +1,1703 @@ +/* + * Copyright (C) 2010, Nokia <ivan.frade@nokia.com> + * + * 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. + */ + +#include "config.h" + +#include "tracker-bus.h" + +#include <gio/gunixfdlist.h> +#include <gio/gunixinputstream.h> +#include <gio/gunixoutputstream.h> +#include <libtracker-common/tracker-common.h> + +#include "tracker-bus-batch.h" +#include "tracker-bus-cursor.h" +#include "tracker-bus-statement.h" + +#define DBUS_PEER_IFACE "org.freedesktop.DBus.Peer" + +#define PORTAL_NAME "org.freedesktop.portal.Tracker" +#define PORTAL_PATH "/org/freedesktop/portal/Tracker" +#define PORTAL_IFACE "org.freedesktop.portal.Tracker" + +#define ENDPOINT_IFACE "org.freedesktop.Tracker3.Endpoint" + +#define DBUS_TIMEOUT 30000 + +struct _TrackerBusConnection { + TrackerSparqlConnection parent_instance; + + GDBusConnection *dbus_conn; + TrackerNamespaceManager *namespaces; + gchar *dbus_name; + gchar *object_path; + gboolean sandboxed; +}; + +enum { + PROP_0, + PROP_BUS_NAME, + PROP_BUS_OBJECT_PATH, + PROP_BUS_CONNECTION, + N_PROPS +}; + +static GParamSpec *props[N_PROPS] = { 0, }; + +typedef struct { + GMainLoop *loop; + gpointer retval; + GError *error; +} AsyncData; + +typedef struct { + struct { + GError *error; + gboolean finished; + } dbus, splice; +} DeserializeTaskData; + +static void tracker_bus_connection_async_initable_iface_init (GAsyncInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (TrackerBusConnection, tracker_bus_connection, + TRACKER_TYPE_SPARQL_CONNECTION, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, + tracker_bus_connection_async_initable_iface_init)) + +static GDBusMessage * +create_portal_create_session_message (TrackerBusConnection *conn) +{ + GDBusMessage *message; + gchar *dbus_uri; + const gchar *object_path; + + object_path = conn->object_path; + if (g_strcmp0 (object_path, "/org/freedesktop/Tracker3/Endpoint") == 0) + object_path = NULL; + + dbus_uri = tracker_util_build_dbus_uri (G_BUS_TYPE_SESSION, + conn->dbus_name, + object_path); + message = g_dbus_message_new_method_call (PORTAL_NAME, + PORTAL_PATH, + PORTAL_IFACE, + "CreateSession"); + g_dbus_message_set_body (message, g_variant_new ("(s)", dbus_uri)); + g_free (dbus_uri); + + return message; +} + +static GDBusMessage * +create_portal_close_session_message (TrackerBusConnection *bus) +{ + GDBusMessage *message; + + message = g_dbus_message_new_method_call (PORTAL_NAME, + PORTAL_PATH, + PORTAL_IFACE, + "CloseSession"); + g_dbus_message_set_body (message, + g_variant_new ("(o)", bus->object_path)); + + return message; +} + +static GDBusMessage * +create_query_message (TrackerBusConnection *conn, + const gchar *sparql, + GVariant *arguments, + GUnixFDList *fd_list, + int fd_idx) +{ + GDBusMessage *message; + GVariant *body; + + if (!arguments) + arguments = g_variant_new ("a{sv}", NULL); + + message = g_dbus_message_new_method_call (conn->dbus_name, + conn->object_path, + ENDPOINT_IFACE, + "Query"); + body = g_variant_new ("(sh@a{sv})", sparql, fd_idx, arguments); + g_dbus_message_set_body (message, body); + g_dbus_message_set_unix_fd_list (message, fd_list); + + return message; +} + +static GDBusMessage * +create_serialize_message (TrackerBusConnection *conn, + const gchar *sparql, + TrackerSerializeFlags flags, + TrackerRdfFormat format, + GVariant *arguments, + GUnixFDList *fd_list, + int fd_idx) +{ + GDBusMessage *message; + GVariant *body; + + if (!arguments) + arguments = g_variant_new ("a{sv}", NULL); + + message = g_dbus_message_new_method_call (conn->dbus_name, + conn->object_path, + ENDPOINT_IFACE, + "Serialize"); + body = g_variant_new ("(shii@a{sv})", + sparql, fd_idx, + flags, format, arguments); + g_dbus_message_set_body (message, body); + g_dbus_message_set_unix_fd_list (message, fd_list); + + return message; +} + +static GDBusMessage * +create_update_message (TrackerBusConnection *conn, + const gchar *request, + GUnixFDList *fd_list, + int fd_idx) +{ + GDBusMessage *message; + GVariant *body; + + message = g_dbus_message_new_method_call (conn->dbus_name, + conn->object_path, + ENDPOINT_IFACE, + request); + body = g_variant_new ("(h)", fd_idx); + g_dbus_message_set_body (message, body); + g_dbus_message_set_unix_fd_list (message, fd_list); + + return message; +} + +static GDBusMessage * +create_deserialize_message (TrackerBusConnection *conn, + TrackerDeserializeFlags flags, + TrackerRdfFormat format, + const gchar *default_graph, + GUnixFDList *fd_list, + int fd_idx) +{ + GDBusMessage *message; + GVariant *body; + + message = g_dbus_message_new_method_call (conn->dbus_name, + conn->object_path, + ENDPOINT_IFACE, + "Deserialize"); + body = g_variant_new ("(hiisa{sv})", + fd_idx, flags, format, + default_graph ? default_graph : "", + NULL); + g_dbus_message_set_body (message, body); + g_dbus_message_set_unix_fd_list (message, fd_list); + + return message; +} + +static gboolean +write_sparql_query (GOutputStream *ostream, + const gchar *query, + GError **error) +{ + GDataOutputStream *data; + int len; + + len = strlen (query); + data = g_data_output_stream_new (ostream); + g_data_output_stream_set_byte_order (data, G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN); + if (!g_data_output_stream_put_int32 (data, len, NULL, error)) + goto error; + if (!g_data_output_stream_put_string (data, query, NULL, error)) + goto error; + + g_object_unref (data); + return TRUE; + error: + g_object_unref (data); + return FALSE; +} + +static gboolean +write_sparql_queries (GOutputStream *ostream, + const gchar **queries, + gint n_queries, + GError **error) +{ + GDataOutputStream *data; + int len, i; + + data = g_data_output_stream_new (ostream); + g_data_output_stream_set_byte_order (data, G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN); + + if (!g_data_output_stream_put_int32 (data, n_queries, NULL, error)) + goto error; + + for (i = 0; i < n_queries; i++) { + len = strlen (queries[i]); + if (!g_data_output_stream_put_int32 (data, len, NULL, error)) + goto error; + if (!g_data_output_stream_put_string (data, queries[i], + NULL, error)) + goto error; + } + + g_output_stream_close (G_OUTPUT_STREAM (data), NULL, NULL); + g_object_unref (data); + return TRUE; + error: + g_object_unref (data); + return FALSE; +} + +static void +query_namespaces_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + TrackerBusConnection *bus = TRACKER_BUS_CONNECTION (source); + TrackerSparqlCursor *cursor; + GError *error = NULL; + GTask *task = user_data; + + cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (source), + res, &error); + if (cursor) { + bus->namespaces = tracker_namespace_manager_new (); + + while (tracker_sparql_cursor_next (cursor, NULL, NULL)) { + const gchar *prefix, *uri; + + prefix = tracker_sparql_cursor_get_string (cursor, 0, NULL); + uri = tracker_sparql_cursor_get_string (cursor, 1, NULL); + + tracker_namespace_manager_add_prefix (bus->namespaces, + prefix, uri); + } + + tracker_namespace_manager_seal (bus->namespaces); + tracker_sparql_cursor_close (cursor); + g_task_return_boolean (task, TRUE); + g_object_unref (cursor); + } else { + g_task_return_error (task, error); + } + + g_object_unref (task); +} + +static void +init_namespaces (TrackerBusConnection *conn, + GTask *task) +{ + tracker_sparql_connection_query_async (TRACKER_SPARQL_CONNECTION (conn), + "SELECT ?prefix ?name { ?name nrl:prefix ?prefix }", + NULL, + query_namespaces_cb, + task); +} + +static void +create_portal_session_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + TrackerBusConnection *bus; + GTask *task = user_data; + GDBusMessage *reply; + GError *error = NULL; + GVariant *body; + + bus = g_task_get_source_object (task); + reply = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source), + res, &error); + + if (reply && !g_dbus_message_to_gerror (reply, &error)) { + body = g_dbus_message_get_body (reply); + + /* We have now access through the portal, replace bus name/path */ + bus->sandboxed = TRUE; + g_clear_pointer (&bus->object_path, g_free); + g_variant_get_child (body, 0, "o", &bus->object_path); + g_clear_pointer (&bus->dbus_name, g_free); + bus->dbus_name = g_strdup (PORTAL_NAME); + + init_namespaces (bus, task); + } else { + g_task_return_error (task, error); + g_object_unref (task); + } + + g_clear_object (&reply); +} + +static void +init_sandbox (TrackerBusConnection *conn, + GTask *task) +{ + GDBusMessage *message; + + /* We are in a flatpak sandbox, check going through the portal */ + message = create_portal_create_session_message (conn); + g_dbus_connection_send_message_with_reply (conn->dbus_conn, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + DBUS_TIMEOUT, + NULL, + g_task_get_cancellable (task), + create_portal_session_cb, + task); + g_object_unref (message); +} + +static void +ping_peer_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + TrackerBusConnection *bus; + GTask *task = user_data; + GError *error = NULL; + GDBusMessage *reply; + + bus = g_task_get_source_object (task); + reply = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source), + res, &error); + + if (!reply || g_dbus_message_to_gerror (reply, &error)) { + if (g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS)) { + /* We do not see the endpoint directly but this is a sandbox. + * Check for portal existence. + */ + g_clear_error (&error); + init_sandbox (bus, task); + } else { + /* We do not see the endpoint, and this is not a sandboxed situation */ + g_dbus_error_strip_remote_error (error); + g_task_return_error (task, error); + g_object_unref (task); + } + } else { + init_namespaces (bus, task); + } + + g_clear_object (&reply); +} + +static void +ping_peer (TrackerBusConnection *bus, + GTask *task) +{ + /* If this environment variable is present, we always go via the portal */ + if (g_getenv ("TRACKER_TEST_PORTAL_FLATPAK_INFO") == NULL) { + GDBusMessage *message; + + message = g_dbus_message_new_method_call (bus->dbus_name, + bus->object_path, + DBUS_PEER_IFACE, + "Ping"); + g_dbus_connection_send_message_with_reply (bus->dbus_conn, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + DBUS_TIMEOUT, + NULL, + g_task_get_cancellable (task), + ping_peer_cb, + task); + g_object_unref (message); + } else { + init_sandbox (bus, task); + } +} + +static void +get_bus_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + TrackerBusConnection *bus; + GTask *task = user_data; + GError *error = NULL; + + bus = g_task_get_source_object (task); + bus->dbus_conn = g_bus_get_finish (res, &error); + + if (bus->dbus_conn) { + ping_peer (bus, task); + } else { + g_task_return_error (task, error); + g_object_unref (task); + } +} + +static void +tracker_bus_connection_async_initable_init_async (GAsyncInitable *async_initable, + gint priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TrackerBusConnection *bus; + GTask *task; + + bus = TRACKER_BUS_CONNECTION (async_initable); + task = g_task_new (async_initable, cancellable, callback, user_data); + g_task_set_priority (task, priority); + + if (!bus->dbus_conn) + g_bus_get (G_BUS_TYPE_SESSION, cancellable, get_bus_cb, task); + else + ping_peer (bus, task); +} + +static gboolean +tracker_bus_connection_async_initable_init_finish (GAsyncInitable *async_initable, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +tracker_bus_connection_async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = tracker_bus_connection_async_initable_init_async; + iface->init_finish = tracker_bus_connection_async_initable_init_finish; +} + +static void +tracker_bus_connection_finalize (GObject *object) +{ + TrackerBusConnection *bus = TRACKER_BUS_CONNECTION (object); + + g_clear_object (&bus->dbus_conn); + g_clear_pointer (&bus->dbus_name, g_free); + g_clear_pointer (&bus->object_path, g_free); + g_clear_object (&bus->namespaces); + + G_OBJECT_CLASS (tracker_bus_connection_parent_class)->finalize (object); +} + +static void +tracker_bus_connection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TrackerBusConnection *bus = TRACKER_BUS_CONNECTION (object); + + switch (prop_id) { + case PROP_BUS_NAME: + bus->dbus_name = g_value_dup_string (value); + break; + case PROP_BUS_OBJECT_PATH: + bus->object_path = g_value_dup_string (value); + break; + case PROP_BUS_CONNECTION: + bus->dbus_conn = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +tracker_bus_connection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TrackerBusConnection *bus = TRACKER_BUS_CONNECTION (object); + + switch (prop_id) { + case PROP_BUS_NAME: + g_value_set_string (value, bus->dbus_name); + break; + case PROP_BUS_OBJECT_PATH: + g_value_set_string (value, bus->object_path); + break; + case PROP_BUS_CONNECTION: + g_value_set_object (value, bus->dbus_conn); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +create_pipe (int *input, + int *output, + GError **error) +{ + int fds[2]; + + if (pipe (fds) < 0) { + g_set_error (error, + G_IO_ERROR, + g_io_error_from_errno (errno), + "Pipe creation failed: %m"); + return FALSE; + } + + *input = fds[0]; + *output = fds[1]; + return TRUE; +} + +static gboolean +create_pipe_for_write (GOutputStream **ostream, + GUnixFDList **fd_list, + int *fd_idx, + GError **error) +{ + int input, output, idx; + GUnixFDList *list; + + if (!create_pipe (&input, &output, error)) + return FALSE; + + list = g_unix_fd_list_new (); + idx = g_unix_fd_list_append (list, input, error); + close (input); + + if (idx < 0) { + g_object_unref (list); + close (output); + return FALSE; + } + + *fd_list = list; + *fd_idx = idx; + *ostream = g_unix_output_stream_new (output, TRUE); + + return TRUE; +} + +static gboolean +create_pipe_for_read (GInputStream **istream, + GUnixFDList **fd_list, + int *fd_idx, + GError **error) +{ + int input, output, idx; + GUnixFDList *list; + + if (!create_pipe (&input, &output, error)) + return FALSE; + + list = g_unix_fd_list_new (); + idx = g_unix_fd_list_append (list, output, error); + close (output); + + if (idx < 0) { + g_object_unref (list); + close (input); + return FALSE; + } + + *fd_list = list; + *fd_idx = idx; + *istream = g_unix_input_stream_new (input, TRUE); + + return TRUE; +} + +static TrackerSparqlCursor * +tracker_bus_connection_query (TrackerSparqlConnection *self, + const gchar *sparql, + GCancellable *cancellable, + GError **error) +{ + return tracker_bus_connection_perform_query (TRACKER_BUS_CONNECTION (self), + sparql, NULL, + cancellable, error); +} + +static void +query_async_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + TrackerSparqlCursor *cursor; + GTask *task = user_data; + GError *error = NULL; + + cursor = tracker_bus_connection_perform_query_finish (TRACKER_BUS_CONNECTION (source), + res, &error); + + if (cursor) + g_task_return_pointer (task, cursor, g_object_unref); + else + g_task_return_error (task, error); + + g_object_unref (task); +} + +static void +tracker_bus_connection_query_async (TrackerSparqlConnection *self, + const gchar *sparql, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (self, cancellable, callback, user_data); + tracker_bus_connection_perform_query_async (TRACKER_BUS_CONNECTION (self), + sparql, + NULL, + cancellable, + query_async_cb, + task); +} + +static TrackerSparqlCursor * +tracker_bus_connection_query_finish (TrackerSparqlConnection *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static TrackerSparqlStatement * +tracker_bus_connection_query_statement (TrackerSparqlConnection *self, + const gchar *query, + GCancellable *cancellable, + GError **error) +{ + return tracker_bus_statement_new (TRACKER_BUS_CONNECTION (self), query); +} + +static void +update_dbus_call_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = user_data; + GDBusMessage *reply; + GError *error = NULL; + + reply = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source), + res, &error); + if (reply && !g_dbus_message_to_gerror (reply, &error)) { + GVariant *body; + + body = g_dbus_message_get_body (reply); + g_task_return_pointer (task, + body ? g_variant_ref (body) : NULL, + (GDestroyNotify) g_variant_unref); + } else { + g_dbus_error_strip_remote_error (error); + g_task_return_error (task, error); + } + + g_object_unref (task); + g_clear_object (&reply); +} + +static void +perform_update_async (TrackerBusConnection *bus, + const gchar *request, + GUnixFDList *fd_list, + int fd_idx, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusMessage *message; + GTask *task; + + task = g_task_new (bus, cancellable, callback, user_data); + message = create_update_message (bus, request, fd_list, fd_idx); + g_dbus_connection_send_message_with_reply (bus->dbus_conn, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + G_MAXINT, + NULL, + cancellable, + update_dbus_call_cb, + task); + g_object_unref (message); +} + +static GVariant * +perform_update_finish (TrackerBusConnection *conn, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +update_sync_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + AsyncData *data = user_data; + + tracker_sparql_connection_update_finish (TRACKER_SPARQL_CONNECTION (source), + res, &data->error); + g_main_loop_quit (data->loop); +} + +static void +tracker_bus_connection_update (TrackerSparqlConnection *self, + const gchar *sparql, + GCancellable *cancellable, + GError **error) +{ + GMainContext *context; + AsyncData data = { 0, }; + + context = g_main_context_new (); + data.loop = g_main_loop_new (context, FALSE); + g_main_context_push_thread_default (context); + + tracker_sparql_connection_update_async (self, + sparql, + cancellable, + update_sync_cb, + &data); + g_main_loop_run (data.loop); + + g_main_context_pop_thread_default (context); + + g_main_loop_unref (data.loop); + g_main_context_unref (context); + + if (data.error) + g_propagate_error (error, data.error); +} + +static void +update_cb (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = user_data; + GError *error = NULL; + GVariant *retval; + + retval = perform_update_finish (TRACKER_BUS_CONNECTION (object), + res, &error); + if (error) + g_task_return_error (task, error); + else + g_task_return_pointer (task, retval, (GDestroyNotify) g_variant_unref); + + g_object_unref (task); +} + +static void +tracker_bus_connection_update_async (TrackerSparqlConnection *self, + const gchar *sparql, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GUnixFDList *fd_list; + GOutputStream *ostream; + GError *error = NULL; + GTask *task; + int fd_idx; + + task = g_task_new (self, cancellable, callback, user_data); + + if (!create_pipe_for_write (&ostream, &fd_list, &fd_idx, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + perform_update_async (TRACKER_BUS_CONNECTION (self), + "Update", + fd_list, fd_idx, + cancellable, + update_cb, + task); + + write_sparql_query (ostream, sparql, NULL); + g_output_stream_close (ostream, NULL, NULL); + g_object_unref (ostream); + g_object_unref (fd_list); +} + +static void +tracker_bus_connection_update_finish (TrackerSparqlConnection *self, + GAsyncResult *res, + GError **error) +{ + GVariant *retval; + + retval = g_task_propagate_pointer (G_TASK (res), error); + g_clear_pointer (&retval, g_variant_unref); +} + +static void +tracker_bus_connection_update_array_async (TrackerSparqlConnection *self, + gchar **updates, + gint n_updates, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GUnixFDList *fd_list; + GOutputStream *ostream; + GError *error = NULL; + GTask *task; + int fd_idx; + + task = g_task_new (self, cancellable, callback, user_data); + + if (n_updates == 0) { + g_task_return_pointer (task, NULL, NULL); + g_object_unref (task); + return; + } + + if (!create_pipe_for_write (&ostream, &fd_list, &fd_idx, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + perform_update_async (TRACKER_BUS_CONNECTION (self), + "UpdateArray", + fd_list, fd_idx, + cancellable, + update_cb, + task); + + write_sparql_queries (ostream, (const gchar **) updates, n_updates, NULL); + g_output_stream_close (ostream, NULL, NULL); + g_object_unref (ostream); + g_object_unref (fd_list); +} + +static gboolean +tracker_bus_connection_update_array_finish (TrackerSparqlConnection *self, + GAsyncResult *res, + GError **error) +{ + GError *inner_error = NULL; + GVariant *retval; + + retval = g_task_propagate_pointer (G_TASK (res), &inner_error); + g_clear_pointer (&retval, g_variant_unref); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} + +static void +update_blank_sync_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + AsyncData *data = user_data; + + data->retval = + tracker_sparql_connection_update_blank_finish (TRACKER_SPARQL_CONNECTION (source), + res, &data->error); + g_main_loop_quit (data->loop); +} + +static GVariant * +tracker_bus_connection_update_blank (TrackerSparqlConnection *self, + const gchar *sparql, + GCancellable *cancellable, + GError **error) +{ + GMainContext *context; + AsyncData data = { 0, }; + + context = g_main_context_new (); + data.loop = g_main_loop_new (context, FALSE); + g_main_context_push_thread_default (context); + + tracker_sparql_connection_update_blank_async (self, + sparql, + cancellable, + update_blank_sync_cb, + &data); + g_main_loop_run (data.loop); + + g_main_context_pop_thread_default (context); + + g_main_loop_unref (data.loop); + g_main_context_unref (context); + + if (data.error) { + g_propagate_error (error, data.error); + return NULL; + } + + return data.retval; +} + +static void +tracker_bus_connection_update_blank_async (TrackerSparqlConnection *self, + const gchar *sparql, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GUnixFDList *fd_list; + GOutputStream *ostream; + GError *error = NULL; + GTask *task; + int fd_idx; + + task = g_task_new (self, cancellable, callback, user_data); + + if (!create_pipe_for_write (&ostream, &fd_list, &fd_idx, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + perform_update_async (TRACKER_BUS_CONNECTION (self), + "UpdateBlank", + fd_list, fd_idx, + cancellable, + update_cb, + task); + + write_sparql_query (ostream, sparql, NULL); + g_output_stream_close (ostream, NULL, NULL); + g_object_unref (ostream); + g_object_unref (fd_list); +} + +static GVariant * +tracker_bus_connection_update_blank_finish (TrackerSparqlConnection *self, + GAsyncResult *res, + GError **error) +{ + GVariant *retval; + + retval = g_task_propagate_pointer (G_TASK (res), error); + + if (retval) { + GVariant *child; + + child = g_variant_get_child_value (retval, 0); + g_variant_unref (retval); + return child; + } + + return NULL; +} + +static TrackerNamespaceManager * +tracker_bus_connection_get_namespace_manager (TrackerSparqlConnection *self) +{ + TrackerBusConnection *bus = TRACKER_BUS_CONNECTION (self); + + return bus->namespaces; +} + +static TrackerNotifier * +tracker_bus_connection_create_notifier (TrackerSparqlConnection *self) +{ + TrackerBusConnection *bus = TRACKER_BUS_CONNECTION (self); + TrackerNotifier *notifier; + + notifier = g_object_new (TRACKER_TYPE_NOTIFIER, + "connection", self, + NULL); + + tracker_notifier_signal_subscribe (notifier, + bus->dbus_conn, + bus->dbus_name, + bus->object_path, + NULL); + + return notifier; +} + +static void +tracker_bus_connection_close (TrackerSparqlConnection *self) +{ + TrackerBusConnection *bus = TRACKER_BUS_CONNECTION (self); + + if (bus->sandboxed) { + GDBusMessage *message; + + message = create_portal_close_session_message (bus); + g_dbus_connection_send_message (bus->dbus_conn, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + NULL, NULL); + g_object_unref (message); + } +} + +static void +tracker_bus_connection_close_async (TrackerSparqlConnection *connection, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TrackerBusConnection *bus = TRACKER_BUS_CONNECTION (connection); + GTask *task; + + task = g_task_new (connection, cancellable, callback, user_data); + + if (bus->sandboxed) { + GDBusMessage *message; + GError *error = NULL; + + message = create_portal_close_session_message (bus); + + if (!g_dbus_connection_send_message (bus->dbus_conn, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + NULL, &error)) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + + g_object_unref (message); + } else { + g_task_return_boolean (task, TRUE); + } + + g_object_unref (task); +} + +static gboolean +tracker_bus_connection_close_finish (TrackerSparqlConnection *connection, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static gboolean +tracker_bus_connection_update_resource (TrackerSparqlConnection *self, + const gchar *graph, + TrackerResource *resource, + GCancellable *cancellable, + GError **error) +{ + TrackerBatch *batch; + gboolean retval; + + batch = tracker_sparql_connection_create_batch (self); + tracker_batch_add_resource (batch, graph, resource); + retval = tracker_batch_execute (batch, cancellable, error); + g_object_unref (batch); + + return retval; +} + +static void +update_resource_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = user_data; + GError *error = NULL; + gboolean retval; + + retval = tracker_batch_execute_finish (TRACKER_BATCH (source), + res, &error); + + if (!retval) + g_task_return_error (task, error); + else + g_task_return_boolean (task, retval); + + g_object_unref (task); +} + +static void +tracker_bus_connection_update_resource_async (TrackerSparqlConnection *self, + const gchar *graph, + TrackerResource *resource, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TrackerBatch *batch; + GTask *task; + + task = g_task_new (self, cancellable, callback, user_data); + + batch = tracker_sparql_connection_create_batch (self); + tracker_batch_add_resource (batch, graph, resource); + tracker_batch_execute_async (batch, cancellable, update_resource_cb, task); + g_object_unref (batch); +} + +static gboolean +tracker_bus_connection_update_resource_finish (TrackerSparqlConnection *connection, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static TrackerBatch * +tracker_bus_connection_create_batch (TrackerSparqlConnection *connection) +{ + TrackerBusConnection *bus = TRACKER_BUS_CONNECTION (connection); + + return tracker_bus_batch_new (bus); +} + +static void +perform_serialize_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GInputStream *istream; + GError *error = NULL; + GTask *task = user_data; + + istream = tracker_bus_connection_perform_serialize_finish (TRACKER_BUS_CONNECTION (source), + res, &error); + if (istream) + g_task_return_pointer (task, istream, g_object_unref); + else + g_task_return_error (task, error); + + g_object_unref (task); +} + +static void +tracker_bus_connection_serialize_async (TrackerSparqlConnection *self, + TrackerSerializeFlags flags, + TrackerRdfFormat format, + const gchar *query, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (self, cancellable, callback, user_data); + tracker_bus_connection_perform_serialize_async (TRACKER_BUS_CONNECTION (self), + flags, format, query, NULL, + cancellable, + perform_serialize_cb, + task); +} + +static GInputStream * +tracker_bus_connection_serialize_finish (TrackerSparqlConnection *connection, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +check_finish_deserialize (GTask *task) +{ + DeserializeTaskData *data; + + data = g_task_get_task_data (task); + + if (!data->dbus.finished || !data->splice.finished) + return; + + if (data->dbus.error) { + g_dbus_error_strip_remote_error (data->dbus.error); + g_task_return_error (task, data->dbus.error); + } else if (data->splice.error) { + g_task_return_error (task, data->splice.error); + } else { + g_task_return_boolean (task, TRUE); + } +} + +static void +deserialize_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GDBusMessage *reply; + GTask *task = user_data; + DeserializeTaskData *data; + GError *error = NULL; + + data = g_task_get_task_data (task); + + reply = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source), + res, &error); + if (reply) + g_dbus_message_to_gerror (reply, &error); + + g_clear_object (&reply); + + data->dbus.finished = TRUE; + data->dbus.error = error; + check_finish_deserialize (task); +} + +static void +splice_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = user_data; + DeserializeTaskData *data; + GError *error = NULL; + + data = g_task_get_task_data (task); + + g_output_stream_splice_finish (G_OUTPUT_STREAM (source), + res, &error); + data->splice.finished = TRUE; + data->splice.error = error; + check_finish_deserialize (task); +} + +static void +deserialize_task_data_free (gpointer data) +{ + DeserializeTaskData *task_data = data; + + g_clear_error (&task_data->dbus.error); + g_clear_error (&task_data->splice.error); + g_free (task_data); +} + +static void +tracker_bus_connection_deserialize_async (TrackerSparqlConnection *self, + TrackerDeserializeFlags flags, + TrackerRdfFormat format, + const gchar *default_graph, + GInputStream *istream, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TrackerBusConnection *bus = TRACKER_BUS_CONNECTION (self); + GDBusMessage *message; + GUnixFDList *fd_list; + GOutputStream *ostream; + DeserializeTaskData *data; + GError *error = NULL; + GTask *task; + int fd_idx; + + task = g_task_new (self, cancellable, callback, user_data); + + data = g_new0 (DeserializeTaskData, 1); + g_task_set_task_data (task, data, deserialize_task_data_free); + + if (!create_pipe_for_write (&ostream, &fd_list, &fd_idx, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + message = create_deserialize_message (bus, flags, format, + default_graph, + fd_list, fd_idx); + + g_dbus_connection_send_message_with_reply (bus->dbus_conn, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + G_MAXINT, + NULL, + cancellable, + deserialize_cb, + task); + g_output_stream_splice_async (ostream, istream, + G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | + G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, + G_PRIORITY_DEFAULT, + cancellable, + splice_cb, + task); + + g_object_unref (message); + g_object_unref (fd_list); + g_object_unref (ostream); +} + +static gboolean +tracker_bus_connection_deserialize_finish (TrackerSparqlConnection *connection, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +tracker_bus_connection_class_init (TrackerBusConnectionClass *klass) +{ + TrackerSparqlConnectionClass *sparql_connection_class; + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + sparql_connection_class = TRACKER_SPARQL_CONNECTION_CLASS (klass); + + object_class->finalize = tracker_bus_connection_finalize; + object_class->set_property = tracker_bus_connection_set_property; + object_class->get_property = tracker_bus_connection_get_property; + + sparql_connection_class->query = tracker_bus_connection_query; + sparql_connection_class->query_async = tracker_bus_connection_query_async; + sparql_connection_class->query_finish = tracker_bus_connection_query_finish; + sparql_connection_class->query_statement = tracker_bus_connection_query_statement; + sparql_connection_class->update = tracker_bus_connection_update; + sparql_connection_class->update_async = tracker_bus_connection_update_async; + sparql_connection_class->update_finish = tracker_bus_connection_update_finish; + sparql_connection_class->update_array_async = tracker_bus_connection_update_array_async; + sparql_connection_class->update_array_finish = tracker_bus_connection_update_array_finish; + sparql_connection_class->update_blank = tracker_bus_connection_update_blank; + sparql_connection_class->update_blank_async = tracker_bus_connection_update_blank_async; + sparql_connection_class->update_blank_finish = tracker_bus_connection_update_blank_finish; + sparql_connection_class->get_namespace_manager = tracker_bus_connection_get_namespace_manager; + sparql_connection_class->create_notifier = tracker_bus_connection_create_notifier; + sparql_connection_class->close = tracker_bus_connection_close; + sparql_connection_class->close_async = tracker_bus_connection_close_async; + sparql_connection_class->close_finish = tracker_bus_connection_close_finish; + sparql_connection_class->update_resource = tracker_bus_connection_update_resource; + sparql_connection_class->update_resource_async = tracker_bus_connection_update_resource_async; + sparql_connection_class->update_resource_finish = tracker_bus_connection_update_resource_finish; + sparql_connection_class->create_batch = tracker_bus_connection_create_batch; + sparql_connection_class->serialize_async = tracker_bus_connection_serialize_async; + sparql_connection_class->serialize_finish = tracker_bus_connection_serialize_finish; + sparql_connection_class->deserialize_async = tracker_bus_connection_deserialize_async; + sparql_connection_class->deserialize_finish = tracker_bus_connection_deserialize_finish; + + props[PROP_BUS_NAME] = + g_param_spec_string ("bus-name", + "Bus name", + "Bus name", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + props[PROP_BUS_OBJECT_PATH] = + g_param_spec_string ("bus-object-path", + "Bus object path", + "Bus object path", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + props[PROP_BUS_CONNECTION] = + g_param_spec_object ("bus-connection", + "Bus connection", + "Bus connection", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (object_class, N_PROPS, props); +} + +static void +tracker_bus_connection_init (TrackerBusConnection *conn) +{ +} + +static void +bus_new_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + AsyncData *data = user_data; + + data->retval = tracker_bus_connection_new_finish (res, &data->error); + g_main_loop_quit (data->loop); +} + +TrackerSparqlConnection * +tracker_bus_connection_new (const gchar *service, + const gchar *object_path, + GDBusConnection *conn, + GError **error) +{ + GMainContext *context; + AsyncData data = { 0, }; + + context = g_main_context_new (); + data.loop = g_main_loop_new (context, FALSE); + g_main_context_push_thread_default (context); + + tracker_bus_connection_new_async (service, + object_path, + conn, + NULL, + bus_new_cb, + &data); + + g_main_loop_run (data.loop); + + g_main_context_pop_thread_default (context); + + g_main_loop_unref (data.loop); + g_main_context_unref (context); + + if (data.error) { + g_propagate_error (error, data.error); + return NULL; + } + + return TRACKER_SPARQL_CONNECTION (data.retval); +} + +void +tracker_bus_connection_new_async (const gchar *service, + const gchar *object_path, + GDBusConnection *conn, + GCancellable *cancellable, + GAsyncReadyCallback cb, + gpointer user_data) +{ + g_async_initable_new_async (TRACKER_TYPE_BUS_CONNECTION, + G_PRIORITY_DEFAULT, + cancellable, + cb, + user_data, + "bus-name", service, + "bus-object-path", object_path, + "bus-connection", conn, + NULL); +} + +TrackerSparqlConnection * +tracker_bus_connection_new_finish (GAsyncResult *res, + GError **error) +{ + GAsyncInitable *initable; + + initable = g_task_get_source_object (G_TASK (res)); + + return TRACKER_SPARQL_CONNECTION (g_async_initable_new_finish (initable, + res, + error)); +} + +static void +query_dbus_call_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = user_data; + GDBusMessage *reply; + GError *error = NULL; + + reply = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source), + res, &error); + if (reply && !g_dbus_message_to_gerror (reply, &error)) { + TrackerSparqlCursor *cursor; + GVariant *body; + GInputStream *istream; + + body = g_dbus_message_get_body (reply); + istream = g_task_get_task_data (task); + cursor = tracker_bus_cursor_new (istream, g_variant_get_child_value (body, 0)); + g_task_return_pointer (task, cursor, g_object_unref); + } else { + g_dbus_error_strip_remote_error (error); + g_task_return_error (task, error); + } + + g_object_unref (task); + g_clear_object (&reply); +} + +void +tracker_bus_connection_perform_query_async (TrackerBusConnection *bus, + const gchar *sparql, + GVariant *arguments, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusMessage *message; + GUnixFDList *fd_list; + GInputStream *istream; + GError *error = NULL; + GTask *task; + int fd_idx; + + task = g_task_new (bus, cancellable, callback, user_data); + + if (!create_pipe_for_read (&istream, &fd_list, &fd_idx, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_task_set_task_data (task, istream, g_object_unref); + + message = create_query_message (bus, sparql, arguments, + fd_list, fd_idx); + g_dbus_connection_send_message_with_reply (bus->dbus_conn, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + G_MAXINT, + NULL, + cancellable, + query_dbus_call_cb, + task); + g_object_unref (message); + g_object_unref (fd_list); +} + +TrackerSparqlCursor * +tracker_bus_connection_perform_query_finish (TrackerBusConnection *conn, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +perform_query_call_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + AsyncData *data = user_data; + + data->retval = tracker_bus_connection_perform_query_finish (TRACKER_BUS_CONNECTION (source), + res, &data->error); + g_main_loop_quit (data->loop); +} + +TrackerSparqlCursor * +tracker_bus_connection_perform_query (TrackerBusConnection *conn, + const gchar *sparql, + GVariant *arguments, + GCancellable *cancellable, + GError **error) +{ + GMainContext *context; + AsyncData data = { 0, }; + + context = g_main_context_new (); + data.loop = g_main_loop_new (context, FALSE); + g_main_context_push_thread_default (context); + + tracker_bus_connection_perform_query_async (conn, + sparql, + arguments, + cancellable, + perform_query_call_cb, + &data); + g_main_loop_run (data.loop); + + g_main_context_pop_thread_default (context); + + g_main_loop_unref (data.loop); + g_main_context_unref (context); + + if (data.error) { + g_propagate_error (error, data.error); + return NULL; + } + + return TRACKER_SPARQL_CURSOR (data.retval); +} + +static void +serialize_call_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = user_data; + GDBusMessage *reply; + GError *error = NULL; + + reply = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source), + res, &error); + if (reply && !g_dbus_message_to_gerror (reply, &error)) { + GInputStream *istream; + + istream = g_task_get_task_data (task); + g_task_return_pointer (task, g_object_ref (istream), g_object_unref); + g_object_unref (task); + } else { + g_dbus_error_strip_remote_error (error); + g_task_return_error (task, error); + g_object_unref (task); + } + + g_clear_object (&reply); +} + +void +tracker_bus_connection_perform_serialize_async (TrackerBusConnection *bus, + TrackerSerializeFlags flags, + TrackerRdfFormat format, + const gchar *query, + GVariant *arguments, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusMessage *message; + GUnixFDList *fd_list; + GInputStream *istream; + GError *error = NULL; + GTask *task; + int fd_idx; + + task = g_task_new (bus, cancellable, callback, user_data); + + if (!create_pipe_for_read (&istream, &fd_list, &fd_idx, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_task_set_task_data (task, istream, g_object_unref); + message = create_serialize_message (bus, query, flags, format, + arguments, fd_list, fd_idx); + g_dbus_connection_send_message_with_reply (bus->dbus_conn, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + G_MAXINT, + NULL, + cancellable, + serialize_call_cb, + task); + g_object_unref (message); + g_object_unref (fd_list); +} + +GInputStream * +tracker_bus_connection_perform_serialize_finish (TrackerBusConnection *conn, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} diff --git a/src/libtracker-sparql/bus/tracker-bus.h b/src/libtracker-sparql/bus/tracker-bus.h new file mode 100644 index 000000000..122f773c9 --- /dev/null +++ b/src/libtracker-sparql/bus/tracker-bus.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010, Nokia <ivan.frade@nokia.com> + * + * 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. + */ + +#ifndef __TRACKER_BUS_H__ +#define __TRACKER_BUS_H__ + +#include <libtracker-sparql/tracker-sparql.h> +#include <libtracker-sparql/tracker-private.h> + +#define TRACKER_TYPE_BUS_CONNECTION (tracker_bus_connection_get_type ()) +G_DECLARE_FINAL_TYPE (TrackerBusConnection, + tracker_bus_connection, + TRACKER, BUS_CONNECTION, + TrackerSparqlConnection) + +TrackerSparqlConnection * tracker_bus_connection_new (const gchar *service, + const gchar *object_path, + GDBusConnection *conn, + GError **error); + +void tracker_bus_connection_new_async (const gchar *service, + const gchar *object_path, + GDBusConnection *conn, + GCancellable *cancellable, + GAsyncReadyCallback cb, + gpointer user_data); + +TrackerSparqlConnection * tracker_bus_connection_new_finish (GAsyncResult *res, + GError **error); + +void tracker_bus_connection_perform_query_async (TrackerBusConnection *conn, + const gchar *sparql, + GVariant *arguments, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +TrackerSparqlCursor * tracker_bus_connection_perform_query_finish (TrackerBusConnection *conn, + GAsyncResult *res, + GError **error); + +TrackerSparqlCursor * tracker_bus_connection_perform_query (TrackerBusConnection *conn, + const gchar *sparql, + GVariant *arguments, + GCancellable *cancellable, + GError **error); + +void tracker_bus_connection_perform_serialize_async (TrackerBusConnection *conn, + TrackerSerializeFlags flags, + TrackerRdfFormat format, + const gchar *query, + GVariant *arguments, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GInputStream * tracker_bus_connection_perform_serialize_finish (TrackerBusConnection *conn, + GAsyncResult *res, + GError **error); + +#endif /* __TRACKER_BUS_H__ */ diff --git a/src/libtracker-sparql/bus/tracker-bus.vala b/src/libtracker-sparql/bus/tracker-bus.vala deleted file mode 100644 index 93f4b9f05..000000000 --- a/src/libtracker-sparql/bus/tracker-bus.vala +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (C) 2010, Nokia <ivan.frade@nokia.com> - * - * 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. - */ - -public class Tracker.Bus.Connection : Tracker.Sparql.Connection { - DBusConnection bus; - string dbus_name; - string object_path; - bool sandboxed; - NamespaceManager namespaces; - - private const string DBUS_PEER_IFACE = "org.freedesktop.DBus.Peer"; - - private const string PORTAL_NAME = "org.freedesktop.portal.Tracker"; - private const string PORTAL_PATH = "/org/freedesktop/portal/Tracker"; - private const string PORTAL_IFACE = "org.freedesktop.portal.Tracker"; - - private const string ENDPOINT_IFACE = "org.freedesktop.Tracker3.Endpoint"; - - private const int timeout = 30000; - - public string bus_name { - get { return dbus_name; } - } - - public string bus_object_path { - get { return object_path; } - } - - public async Connection (string dbus_name, string object_path, DBusConnection? dbus_connection, Cancellable? cancellable) throws Sparql.Error, IOError, DBusError, GLib.Error { - Object (); - this.sandboxed = false; - this.bus = dbus_connection; - - // ensure that error domain is registered with GDBus - new Sparql.Error.INTERNAL (""); - - // If this environment variable is present, we always go via the portal, - if (Environment.get_variable("TRACKER_TEST_PORTAL_FLATPAK_INFO") == null) { - var message = new DBusMessage.method_call (dbus_name, object_path, DBUS_PEER_IFACE, "Ping"); - - try { - var reply = yield this.bus.send_message_with_reply (message, 0, timeout, cancellable); - reply.to_gerror (); - this.dbus_name = dbus_name; - this.object_path = object_path; - yield this.init_namespaces (); - return; - } catch (GLib.Error e) { - if (!GLib.FileUtils.test ("/.flatpak-info", GLib.FileTest.EXISTS)) { - throw e; - } - } - } - - /* We are in a flatpak sandbox, check going through the portal */ - if (object_path == "/org/freedesktop/Tracker3/Endpoint") - object_path = null; - - string uri = Tracker.util_build_dbus_uri (GLib.BusType.SESSION, dbus_name, object_path); - var message = new DBusMessage.method_call (PORTAL_NAME, PORTAL_PATH, PORTAL_IFACE, "CreateSession"); - message.set_body (new Variant ("(s)", uri)); - - var reply = yield this.bus.send_message_with_reply (message, 0, timeout, cancellable); - reply.to_gerror (); - - var variant = reply.get_body (); - variant.get_child(0, "o", out object_path); - - this.dbus_name = PORTAL_NAME; - this.object_path = object_path; - this.sandboxed = true; - - yield this.init_namespaces (); - } - - private async void init_namespaces () throws Sparql.Error, IOError, DBusError, GLib.Error { - this.namespaces = new NamespaceManager (); - var cursor = yield this.query_async ("SELECT ?prefix ?name { ?name nrl:prefix ?prefix }", null); - while (cursor.next ()) - this.namespaces.add_prefix(cursor.get_string(0), cursor.get_string(1)); - cursor.close (); - } - - static void pipe (out UnixInputStream input, out UnixOutputStream output) throws IOError { - int pipefd[2]; - if (Posix.pipe (pipefd) < 0) { - throw new IOError.FAILED ("Pipe creation failed"); - } - input = new UnixInputStream (pipefd[0], true); - output = new UnixOutputStream (pipefd[1], true); - } - - static void handle_error_reply (DBusMessage message) throws Sparql.Error, IOError, DBusError { - try { - message.to_gerror (); - } catch (IOError e_io) { - GLib.DBusError.strip_remote_error (e_io); - throw e_io; - } catch (DBusError e_dbus) { - GLib.DBusError.strip_remote_error (e_dbus); - throw e_dbus; - } catch (Sparql.Error e_sparql) { - GLib.DBusError.strip_remote_error (e_sparql); - throw e_sparql; - } catch (Error e) { - GLib.DBusError.strip_remote_error (e); - throw new IOError.FAILED (e.message); - } - } - - static void send_query (DBusConnection bus, string dbus_name, string object_path, string sparql, VariantBuilder? arguments, UnixOutputStream output, Cancellable? cancellable, AsyncReadyCallback? callback) throws GLib.IOError, GLib.Error { - var message = new DBusMessage.method_call (dbus_name, object_path, ENDPOINT_IFACE, "Query"); - var fd_list = new UnixFDList (); - message.set_body (new Variant ("(sha{sv})", sparql, fd_list.append (output.fd), arguments)); - message.set_unix_fd_list (fd_list); - - bus.send_message_with_reply.begin (message, DBusSendMessageFlags.NONE, int.MAX, null, cancellable, callback); - } - - public static async Sparql.Cursor perform_query_call (DBusConnection bus, string dbus_name, string object_path, string sparql, VariantBuilder? arguments, Cancellable? cancellable) throws GLib.IOError, GLib.Error { - UnixInputStream input; - UnixOutputStream output; - pipe (out input, out output); - - // send D-Bus request - AsyncResult dbus_res = null; - bool received_result = false; - send_query (bus, dbus_name, object_path, sparql, arguments, output, cancellable, (o, res) => { - dbus_res = res; - if (received_result) { - perform_query_call.callback (); - } - }); - - output = null; - - // receive query results via FD - var mem_stream = new MemoryOutputStream (null, GLib.realloc, GLib.free); - - try { - yield mem_stream.splice_async (input, OutputStreamSpliceFlags.CLOSE_SOURCE | OutputStreamSpliceFlags.CLOSE_TARGET, Priority.DEFAULT, cancellable); - } finally { - // wait for D-Bus reply - received_result = true; - if (dbus_res == null) { - yield; - } - } - - var reply = bus.send_message_with_reply.end (dbus_res); - handle_error_reply (reply); - - string[] variable_names = (string[]) reply.get_body ().get_child_value (0); - mem_stream.close (); - return new FDCursor (mem_stream.steal_data (), mem_stream.data_size, variable_names); - } - - public override Sparql.Cursor query (string sparql, Cancellable? cancellable) throws Sparql.Error, GLib.Error, GLib.IOError, DBusError { - // use separate main context for sync operation - var context = new MainContext (); - var loop = new MainLoop (context, false); - context.push_thread_default (); - AsyncResult async_res = null; - query_async.begin (sparql, cancellable, (o, res) => { - async_res = res; - loop.quit (); - }); - loop.run (); - context.pop_thread_default (); - return query_async.end (async_res); - } - - public async override Sparql.Cursor query_async (string sparql, Cancellable? cancellable = null) throws Sparql.Error, GLib.Error, GLib.IOError, DBusError { - return yield perform_query_call (bus, dbus_name, object_path, sparql, null, cancellable); - } - - public override Sparql.Statement? query_statement (string sparql, GLib.Cancellable? cancellable = null) throws Sparql.Error { - return new Bus.Statement (this, bus, dbus_name, object_path, sparql); - } - - static void send_update (DBusConnection bus, string dbus_name, string object_path, string method, UnixInputStream input, Cancellable? cancellable, AsyncReadyCallback? callback) throws GLib.Error, GLib.IOError { - var message = new DBusMessage.method_call (dbus_name, object_path, ENDPOINT_IFACE, method); - var fd_list = new UnixFDList (); - message.set_body (new Variant ("(h)", fd_list.append (input.fd))); - message.set_unix_fd_list (fd_list); - - bus.send_message_with_reply.begin (message, DBusSendMessageFlags.NONE, int.MAX, null, cancellable, callback); - } - - public override void update (string sparql, Cancellable? cancellable = null) throws Sparql.Error, GLib.Error, GLib.IOError, DBusError { - // use separate main context for sync operation - var context = new MainContext (); - var loop = new MainLoop (context, false); - context.push_thread_default (); - AsyncResult async_res = null; - update_async.begin (sparql, cancellable, (o, res) => { - async_res = res; - loop.quit (); - }); - loop.run (); - context.pop_thread_default (); - update_async.end (async_res); - } - - public async override void update_async (string sparql, Cancellable? cancellable = null) throws Sparql.Error, GLib.Error, GLib.IOError, DBusError { - UnixInputStream input; - UnixOutputStream output; - pipe (out input, out output); - - // send D-Bus request - AsyncResult dbus_res = null; - bool sent_update = false; - send_update (bus, dbus_name, object_path, "Update", input, cancellable, (o, res) => { - dbus_res = res; - if (sent_update) { - update_async.callback (); - } - }); - - // send sparql string via fd - var data_stream = new DataOutputStream (output); - data_stream.set_byte_order (DataStreamByteOrder.HOST_ENDIAN); - data_stream.put_int32 ((int32) sparql.length); - data_stream.put_string (sparql); - data_stream = null; - - // wait for D-Bus reply - sent_update = true; - if (dbus_res == null) { - yield; - } - - var reply = bus.send_message_with_reply.end (dbus_res); - handle_error_reply (reply); - } - - public static async bool perform_update_array (DBusConnection bus, string dbus_name, string object_path, string[] sparql, Cancellable? cancellable) throws GLib.IOError, GLib.Error { - if (sparql.length == 0) - return true; - - UnixInputStream input; - UnixOutputStream output; - pipe (out input, out output); - - // send D-Bus request - AsyncResult dbus_res = null; - bool sent_update = false; - send_update (bus, dbus_name, object_path, "UpdateArray", input, cancellable, (o, res) => { - dbus_res = res; - if (sent_update) { - perform_update_array.callback (); - } - }); - - // send sparql strings via fd - var data_stream = new DataOutputStream (output); - data_stream.set_byte_order (DataStreamByteOrder.HOST_ENDIAN); - data_stream.put_int32 ((int32) sparql.length); - for (int i = 0; i < sparql.length; i++) { - data_stream.put_int32 ((int32) sparql[i].length); - data_stream.put_string (sparql[i]); - } - data_stream = null; - - // wait for D-Bus reply - sent_update = true; - if (dbus_res == null) { - yield; - } - - var reply = bus.send_message_with_reply.end (dbus_res); - handle_error_reply (reply); - - return true; - } - - public async override bool update_array_async (string[] sparql, Cancellable? cancellable = null) throws Sparql.Error, GLib.Error, GLib.IOError, DBusError { - return yield perform_update_array (bus, dbus_name, object_path, sparql, cancellable); - } - - public override GLib.Variant? update_blank (string sparql, Cancellable? cancellable = null) throws Sparql.Error, GLib.Error, GLib.IOError, DBusError { - // use separate main context for sync operation - var context = new MainContext (); - var loop = new MainLoop (context, false); - context.push_thread_default (); - AsyncResult async_res = null; - update_blank_async.begin (sparql, cancellable, (o, res) => { - async_res = res; - loop.quit (); - }); - loop.run (); - context.pop_thread_default (); - return update_blank_async.end (async_res); - } - - public async override GLib.Variant? update_blank_async (string sparql, Cancellable? cancellable = null) throws Sparql.Error, GLib.Error, GLib.IOError, DBusError { - UnixInputStream input; - UnixOutputStream output; - pipe (out input, out output); - - // send D-Bus request - AsyncResult dbus_res = null; - bool sent_update = false; - send_update (bus, dbus_name, object_path, "UpdateBlank", input, cancellable, (o, res) => { - dbus_res = res; - if (sent_update) { - update_blank_async.callback (); - } - }); - - // send sparql strings via fd - var data_stream = new DataOutputStream (output); - data_stream.set_byte_order (DataStreamByteOrder.HOST_ENDIAN); - data_stream.put_int32 ((int32) sparql.length); - data_stream.put_string (sparql); - data_stream = null; - - // wait for D-Bus reply - sent_update = true; - if (dbus_res == null) { - yield; - } - - var reply = bus.send_message_with_reply.end (dbus_res); - handle_error_reply (reply); - return reply.get_body ().get_child_value (0); - } - - public override bool update_resource (string? graph, Resource resource, GLib.Cancellable? cancellable = null) throws Sparql.Error, GLib.Error, GLib.IOError, GLib.DBusError { - var batch = this.create_batch (); - batch.add_resource (graph, resource); - return batch.execute (cancellable); - } - - public async override bool update_resource_async (string? graph, Resource resource, GLib.Cancellable? cancellable = null) throws Sparql.Error, GLib.Error, GLib.IOError, GLib.DBusError { - var batch = this.create_batch (); - batch.add_resource (graph, resource); - return yield batch.execute_async (cancellable); - } - - public override Tracker.Notifier? create_notifier () { - var notifier = (Tracker.Notifier) Object.new (typeof (Tracker.Notifier), - "connection", this, - null); - - notifier.signal_subscribe (this.bus, this.dbus_name, null, null); - - return notifier; - } - - public override void close () { - if (this.sandboxed) { - var message = new DBusMessage.method_call (PORTAL_NAME, PORTAL_PATH, PORTAL_IFACE, "CloseSession"); - message.set_body (new Variant ("(o)", this.object_path)); - - try { - this.bus.send_message (message, 0, null); - } catch (GLib.Error e) { - } - } - } - - public async override bool close_async () throws GLib.IOError { - this.close (); - return true; - } - - public override Tracker.NamespaceManager? get_namespace_manager () { - return namespaces; - } - - public override Tracker.Batch? create_batch () { - var batch = (Tracker.Batch) Object.new (typeof (Tracker.Bus.Batch), - "connection", this, - null); - return batch; - } - - public static async GLib.InputStream perform_serialize (DBusConnection bus, string dbus_name, string object_path, SerializeFlags flags, RdfFormat format, string sparql, VariantBuilder? arguments, Cancellable? cancellable) throws GLib.IOError, GLib.Error { - UnixInputStream input; - UnixOutputStream output; - pipe (out input, out output); - - var message = new DBusMessage.method_call (dbus_name, object_path, ENDPOINT_IFACE, "Serialize"); - var fd_list = new UnixFDList (); - message.set_body (new Variant ("(shiia{sv})", sparql, fd_list.append (output.fd), flags, format, arguments)); - message.set_unix_fd_list (fd_list); - - var reply = yield bus.send_message_with_reply (message, DBusSendMessageFlags.NONE, int.MAX, null, cancellable); - handle_error_reply (reply); - - return input; - } - - public async override GLib.InputStream serialize_async (SerializeFlags flags, RdfFormat format, string sparql, GLib.Cancellable? cancellable = null) throws Sparql.Error, GLib.Error, GLib.IOError, GLib.DBusError { - return yield perform_serialize (bus, dbus_name, object_path, flags, format, sparql, null, cancellable); - } - - public static async bool perform_deserialize (DBusConnection bus, string dbus_name, string object_path, DeserializeFlags flags, RdfFormat format, string? default_graph, GLib.InputStream istream, VariantBuilder? arguments, Cancellable? cancellable) throws GLib.IOError, GLib.Error { - UnixInputStream input; - UnixOutputStream output; - pipe (out input, out output); - - var message = new DBusMessage.method_call (dbus_name, object_path, ENDPOINT_IFACE, "Deserialize"); - var fd_list = new UnixFDList (); - DBusMessage? reply = null; - message.set_body (new Variant ("(hiisa{sv})", fd_list.append (input.fd), flags, format, default_graph != null ? default_graph : "", arguments)); - message.set_unix_fd_list (fd_list); - - bus.send_message_with_reply.begin (message, DBusSendMessageFlags.NONE, int.MAX, null, cancellable, (o, res) => { - reply = bus.send_message_with_reply.end(res); - perform_deserialize.callback(); - }); - - output.splice_async.begin (istream, OutputStreamSpliceFlags.CLOSE_SOURCE | OutputStreamSpliceFlags.CLOSE_TARGET, Priority.DEFAULT, cancellable); - - yield; - - if (reply != null) - handle_error_reply (reply); - - return true; - } - - public async override bool deserialize_async (DeserializeFlags flags, RdfFormat format, string? default_graph, GLib.InputStream istream, Cancellable? cancellable) throws GLib.IOError, GLib.Error { - return yield perform_deserialize (bus, dbus_name, object_path, flags, format, default_graph, istream, null, cancellable); - } -} diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build index ce3dedb0f..149dfd33d 100644 --- a/src/libtracker-sparql/meson.build +++ b/src/libtracker-sparql/meson.build @@ -105,8 +105,6 @@ libtracker_sparql_private = static_library('tracker-sparql-private', bus_files, direct_files, remote_files, - '../libtracker-common/libtracker-common.vapi', - 'tracker-backend.vala', tracker_gresources, c_args: [ diff --git a/src/libtracker-sparql/tracker-backend.vala b/src/libtracker-sparql/tracker-backend.vala deleted file mode 100644 index 1657ab25e..000000000 --- a/src/libtracker-sparql/tracker-backend.vala +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2010, Nokia <ivan.frade@nokia.com> - * - * 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. - */ - -/* All apps using libtracker-sparql will call one of these constructors, so - * we take the opportunity to call tracker_get_debug_flags(). This has the - * effect of printing the 'help' message if TRACKER_DEBUG=help is set. - */ - -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 (); - - var context = new GLib.MainContext (); - var loop = new GLib.MainLoop(context); - GLib.Error? error = null; - Tracker.Sparql.Connection? sparql_conn = null; - - context.push_thread_default (); - - tracker_sparql_connection_bus_new_async.begin(service, object_path, conn, null, (o, res) => { - try { - sparql_conn = tracker_sparql_connection_bus_new_async.end(res); - } catch (Error e) { - error = e; - } - loop.quit(); - }); - loop.run (); - - context.pop_thread_default (); - - if (error != null) - throw error; - - return sparql_conn; -} - -public static async Tracker.Sparql.Connection tracker_sparql_connection_bus_new_async (string service, string? object_path, DBusConnection? conn, Cancellable? cancellable) throws Tracker.Sparql.Error, IOError, DBusError, GLib.Error { - GLib.DBusConnection dbus_conn; - string path; - - Tracker.get_debug_flags (); - - if (conn != null) - dbus_conn = conn; - else - dbus_conn = yield GLib.Bus.get (GLib.BusType.SESSION, cancellable); - - if (object_path != null) - path = object_path; - else - path = "/org/freedesktop/Tracker3/Endpoint"; - - return yield new Tracker.Bus.Connection (service, path, dbus_conn, cancellable); -} diff --git a/src/libtracker-sparql/tracker-connection.c b/src/libtracker-sparql/tracker-connection.c index db9c02371..6f83d7abe 100644 --- a/src/libtracker-sparql/tracker-connection.c +++ b/src/libtracker-sparql/tracker-connection.c @@ -62,6 +62,7 @@ #include "tracker-private.h" #include "tracker-debug.h" +#include "bus/tracker-bus.h" #include "direct/tracker-direct.h" #include "remote/tracker-remote.h" @@ -115,53 +116,6 @@ tracker_sparql_connection_lookup_dbus_service (TrackerSparqlConnection *connect path); } -/* The constructor functions are defined in the libtracker-sparql-backend, but - * documented here. */ - -/** - * tracker_sparql_connection_bus_new: - * @service_name: The name of the D-Bus service to connect to. - * @object_path: (nullable): The path to the object, or %NULL to use the default. - * @dbus_connection: (nullable): The #GDBusConnection to use, or %NULL to use the session bus - * @error: pointer to a #GError - * - * Connects to a database owned by another process on the - * local machine. - * - * Returns: (transfer full): a new #TrackerSparqlConnection. Call g_object_unref() on the - * object when no longer used. - * - * Since: 3.0 - */ - -/** - * tracker_sparql_connection_bus_new_async: - * @service_name: The name of the D-Bus service to connect to. - * @object_path: (nullable): The path to the object, or %NULL to use the default. - * @dbus_connection: (nullable): The #GDBusConnection to use, or %NULL to use the session bus - * @cancellable: (nullable): a #GCancellable, or %NULL - * @callback: the #GAsyncReadyCallback called when the operation completes - * @user_data: data passed to @callback - * - * Connects to a database owned by another process on the - * local machine. This is an asynchronous operation. - * - * Since: 3.1 - */ - -/** - * tracker_sparql_connection_bus_new_finish: - * @result: the #GAsyncResult - * @error: pointer to a #GError - * - * Completion function for tracker_sparql_connection_bus_new_async(). - * - * Returns: (transfer full): a new #TrackerSparqlConnection. Call g_object_unref() on the - * object when no longer used. - * - * Since: 3.1 - */ - /** * tracker_sparql_connection_query: * @connection: a #TrackerSparqlConnection @@ -1143,3 +1097,117 @@ tracker_sparql_connection_new_finish (GAsyncResult *res, return g_task_propagate_pointer (G_TASK (res), error); } + +/** + * tracker_sparql_connection_bus_new: + * @service_name: The name of the D-Bus service to connect to. + * @object_path: (nullable): The path to the object, or %NULL to use the default. + * @dbus_connection: (nullable): The #GDBusConnection to use, or %NULL to use the session bus + * @error: pointer to a #GError + * + * Connects to a database owned by another process on the + * local machine. + * + * Returns: (transfer full): a new #TrackerSparqlConnection. Call g_object_unref() on the + * object when no longer used. + * + * Since: 3.0 + */ +TrackerSparqlConnection * +tracker_sparql_connection_bus_new (const gchar *service, + const gchar *object_path, + GDBusConnection *conn, + GError **error) +{ + g_return_val_if_fail (service != NULL, NULL); + g_return_val_if_fail (!conn || G_IS_DBUS_CONNECTION (conn), NULL); + g_return_val_if_fail (!error || !*error, NULL); + + if (!object_path) + object_path = "/org/freedesktop/Tracker3/Endpoint"; + + return tracker_bus_connection_new (service, object_path, conn, error); +} + +static void +bus_new_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + TrackerSparqlConnection *conn; + GTask *task = user_data; + GError *error = NULL; + + conn = tracker_bus_connection_new_finish (res, &error); + + if (conn) + g_task_return_pointer (task, conn, g_object_unref); + else + g_task_return_error (task, error); + + g_object_unref (task); +} + +/** + * tracker_sparql_connection_bus_new_async: + * @service_name: The name of the D-Bus service to connect to. + * @object_path: (nullable): The path to the object, or %NULL to use the default. + * @dbus_connection: (nullable): The #GDBusConnection to use, or %NULL to use the session bus + * @cancellable: (nullable): a #GCancellable, or %NULL + * @callback: the #GAsyncReadyCallback called when the operation completes + * @user_data: data passed to @callback + * + * Connects to a database owned by another process on the + * local machine. This is an asynchronous operation. + * + * Since: 3.1 + */ +void +tracker_sparql_connection_bus_new_async (const gchar *service, + const gchar *object_path, + GDBusConnection *conn, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + g_return_if_fail (service != NULL); + g_return_if_fail (!conn || G_IS_DBUS_CONNECTION (conn)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (NULL, cancellable, callback, user_data); + g_task_set_source_tag (task, tracker_sparql_connection_bus_new_async); + + if (!object_path) + object_path = "/org/freedesktop/Tracker3/Endpoint"; + + tracker_bus_connection_new_async (service, object_path, conn, + cancellable, bus_new_cb, + task); +} + +/** + * tracker_sparql_connection_bus_new_finish: + * @result: the #GAsyncResult + * @error: pointer to a #GError + * + * Completion function for tracker_sparql_connection_bus_new_async(). + * + * Returns: (transfer full): a new #TrackerSparqlConnection. Call g_object_unref() on the + * object when no longer used. + * + * Since: 3.1 + */ +TrackerSparqlConnection * +tracker_sparql_connection_bus_new_finish (GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (G_IS_TASK (result), NULL); + g_return_val_if_fail (!error || !*error, NULL); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == + tracker_sparql_connection_bus_new_async, + NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} |