diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:39:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:39:48 +0000 |
commit | 3ade071f273aaa973e44bf95d6b1d4913a18f03b (patch) | |
tree | e2f99d267ae18427645404f215b984afbe73098d /src/nautilus-tag-manager.c | |
parent | Initial commit. (diff) | |
download | nautilus-3ade071f273aaa973e44bf95d6b1d4913a18f03b.tar.xz nautilus-3ade071f273aaa973e44bf95d6b1d4913a18f03b.zip |
Adding upstream version 43.2.upstream/43.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/nautilus-tag-manager.c')
-rw-r--r-- | src/nautilus-tag-manager.c | 1019 |
1 files changed, 1019 insertions, 0 deletions
diff --git a/src/nautilus-tag-manager.c b/src/nautilus-tag-manager.c new file mode 100644 index 0000000..959eee8 --- /dev/null +++ b/src/nautilus-tag-manager.c @@ -0,0 +1,1019 @@ +/* nautilus-tag-manager.c + * + * Copyright (C) 2017 Alexandru Pandelea <alexandru.pandelea@gmail.com> + * Copyright (C) 2020 Sam Thursfield <sam@afuera.me.uk> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "nautilus-tag-manager.h" +#include "nautilus-file.h" +#include "nautilus-file-undo-operations.h" +#include "nautilus-file-undo-manager.h" +#include "nautilus-tracker-utilities.h" +#define DEBUG_FLAG NAUTILUS_DEBUG_TAG_MANAGER +#include "nautilus-debug.h" + +#include <gio/gunixinputstream.h> +#include <tracker-sparql.h> + +#include "config.h" + +struct _NautilusTagManager +{ + GObject object; + + gboolean database_ok; + TrackerSparqlConnection *db; + TrackerNotifier *notifier; + + TrackerSparqlStatement *query_starred_files; + TrackerSparqlStatement *query_file_is_starred; + + GHashTable *starred_file_uris; + GFile *home; + + GCancellable *cancellable; +}; + +G_DEFINE_TYPE (NautilusTagManager, nautilus_tag_manager, G_TYPE_OBJECT); + +static NautilusTagManager *tag_manager = NULL; + +/* See nautilus_tag_manager_new_dummy() documentation for details. */ +static gboolean make_dummy_instance = FALSE; + +typedef struct +{ + NautilusTagManager *tag_manager; + GTask *task; + GList *selection; + gboolean star; +} UpdateData; + +enum +{ + STARRED_CHANGED, + LAST_SIGNAL +}; + +#define QUERY_STARRED_FILES \ + "SELECT ?file " \ + "{ " \ + " ?file a nautilus:File ; " \ + " nautilus:starred true . " \ + "}" + +#define QUERY_FILE_IS_STARRED \ + "ASK " \ + "{ " \ + " ~file a nautilus:File ; " \ + " nautilus:starred true . " \ + "}" + +static guint signals[LAST_SIGNAL]; + +/* Limit to 10MB output from Tracker -- surely, nobody has over a million starred files. */ +#define TRACKER2_MAX_IMPORT_BYTES 10 * 1024 * 1024 + +static gchar * +tracker2_migration_stamp (void) +{ + return g_build_filename (g_get_user_data_dir (), "nautilus", "tracker2-migration-complete", NULL); +} + +static void +start_query_or_update (TrackerSparqlConnection *db, + GString *query, + GAsyncReadyCallback callback, + gpointer user_data, + gboolean is_query, + GCancellable *cancellable) +{ + g_autoptr (GError) error = NULL; + + if (!db) + { + g_message ("nautilus-tag-manager: No Tracker connection"); + return; + } + + if (is_query) + { + tracker_sparql_connection_query_async (db, + query->str, + cancellable, + callback, + user_data); + } + else + { + tracker_sparql_connection_update_async (db, + query->str, + cancellable, + callback, + user_data); + } +} + +static void +on_update_callback (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + TrackerSparqlConnection *db; + GError *error; + UpdateData *data; + + data = user_data; + + error = NULL; + + db = TRACKER_SPARQL_CONNECTION (object); + + tracker_sparql_connection_update_finish (db, result, &error); + + if (error == NULL) + { + /* FIXME: make sure data->tag_manager->starred_file_uris is up to date */ + + if (!nautilus_file_undo_manager_is_operating ()) + { + NautilusFileUndoInfo *undo_info; + + undo_info = nautilus_file_undo_info_starred_new (data->selection, data->star); + nautilus_file_undo_manager_set_action (undo_info); + + g_object_unref (undo_info); + } + + g_signal_emit_by_name (data->tag_manager, "starred-changed", nautilus_file_list_copy (data->selection)); + + g_task_return_boolean (data->task, TRUE); + g_object_unref (data->task); + } + else if (error && error->code == G_IO_ERROR_CANCELLED) + { + g_error_free (error); + } + else + { + g_warning ("error updating tags: %s", error->message); + g_task_return_error (data->task, error); + g_object_unref (data->task); + } + + nautilus_file_list_free (data->selection); + g_free (data); +} + +/** + * nautilus_tag_manager_get_starred_files: + * @self: The tag manager singleton + * + * Returns: (element-type gchar*) (transfer container): A list of the starred urls. + */ +GList * +nautilus_tag_manager_get_starred_files (NautilusTagManager *self) +{ + GHashTableIter starred_iter; + gchar *starred_uri; + GList *starred_file_uris = NULL; + + g_hash_table_iter_init (&starred_iter, self->starred_file_uris); + while (g_hash_table_iter_next (&starred_iter, (gpointer *) &starred_uri, NULL)) + { + g_autoptr (GFile) file = g_file_new_for_uri (starred_uri); + + /* Skip files ouside $HOME, because we don't support starring these yet. + * See comment on nautilus_tag_manager_can_star_contents() */ + if (g_file_has_prefix (file, self->home)) + { + starred_file_uris = g_list_prepend (starred_file_uris, starred_uri); + } + } + + return starred_file_uris; +} + +static void +on_get_starred_files_cursor_callback (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + TrackerSparqlCursor *cursor; + g_autoptr (GError) error = NULL; + const gchar *url; + gboolean success; + NautilusTagManager *self; + GList *changed_files; + NautilusFile *file; + + cursor = TRACKER_SPARQL_CURSOR (object); + + self = NAUTILUS_TAG_MANAGER (user_data); + + success = tracker_sparql_cursor_next_finish (cursor, result, &error); + + if (!success) + { + if (error != NULL) + { + g_warning ("Error on getting all tags cursor callback: %s", error->message); + } + + g_clear_object (&cursor); + return; + } + + url = tracker_sparql_cursor_get_string (cursor, 0, NULL); + + g_hash_table_add (self->starred_file_uris, g_strdup (url)); + + file = nautilus_file_get_by_uri (url); + + if (file) + { + changed_files = g_list_prepend (NULL, file); + + g_signal_emit_by_name (self, "starred-changed", changed_files); + + nautilus_file_list_free (changed_files); + } + else + { + DEBUG ("File %s is starred but not found", url); + } + + tracker_sparql_cursor_next_async (cursor, + self->cancellable, + on_get_starred_files_cursor_callback, + self); +} + +static void +on_get_starred_files_query_callback (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + TrackerSparqlCursor *cursor; + g_autoptr (GError) error = NULL; + TrackerSparqlStatement *statement; + NautilusTagManager *self; + + self = NAUTILUS_TAG_MANAGER (user_data); + statement = TRACKER_SPARQL_STATEMENT (object); + + cursor = tracker_sparql_statement_execute_finish (statement, + result, + &error); + + if (error != NULL) + { + if (error->code != G_IO_ERROR_CANCELLED) + { + g_warning ("Error on getting starred files: %s", error->message); + } + } + else + { + tracker_sparql_cursor_next_async (cursor, + self->cancellable, + on_get_starred_files_cursor_callback, + user_data); + } +} + +static void +nautilus_tag_manager_query_starred_files (NautilusTagManager *self, + GCancellable *cancellable) +{ + if (!self->database_ok) + { + g_message ("nautilus-tag-manager: No Tracker connection"); + return; + } + + tracker_sparql_statement_execute_async (self->query_starred_files, + cancellable, + on_get_starred_files_query_callback, + self); +} + +static GString * +nautilus_tag_manager_delete_tag (NautilusTagManager *self, + GList *selection) +{ + GString *query; + NautilusFile *file; + GList *l; + + query = g_string_new ("DELETE DATA {"); + + for (l = selection; l != NULL; l = l->next) + { + g_autofree gchar *uri = NULL; + + file = l->data; + + uri = nautilus_file_get_uri (file); + g_string_append_printf (query, + " <%s> a nautilus:File ; " + " nautilus:starred true . ", + uri); + } + + g_string_append (query, "}"); + + return query; +} + +static GString * +nautilus_tag_manager_insert_tag (NautilusTagManager *self, + GList *selection) +{ + GString *query; + NautilusFile *file; + GList *l; + + query = g_string_new ("INSERT DATA {"); + + for (l = selection; l != NULL; l = l->next) + { + g_autofree gchar *uri = NULL; + + file = l->data; + + uri = nautilus_file_get_uri (file); + g_string_append_printf (query, + " <%s> a nautilus:File ; " + " nautilus:starred true . ", + uri); + } + + g_string_append (query, "}"); + + return query; +} + +gboolean +nautilus_tag_manager_file_is_starred (NautilusTagManager *self, + const gchar *file_uri) +{ + return g_hash_table_contains (self->starred_file_uris, file_uri); +} + +void +nautilus_tag_manager_star_files (NautilusTagManager *self, + GObject *object, + GList *selection, + GAsyncReadyCallback callback, + GCancellable *cancellable) +{ + GString *query; + g_autoptr (GError) error = NULL; + GTask *task; + UpdateData *update_data; + + DEBUG ("Starring %i files", g_list_length (selection)); + + task = g_task_new (object, cancellable, callback, NULL); + + query = nautilus_tag_manager_insert_tag (self, selection); + + update_data = g_new0 (UpdateData, 1); + update_data->task = task; + update_data->tag_manager = self; + update_data->selection = nautilus_file_list_copy (selection); + update_data->star = TRUE; + + start_query_or_update (self->db, + query, + on_update_callback, + update_data, + FALSE, + cancellable); + + g_string_free (query, TRUE); +} + +void +nautilus_tag_manager_unstar_files (NautilusTagManager *self, + GObject *object, + GList *selection, + GAsyncReadyCallback callback, + GCancellable *cancellable) +{ + GString *query; + GTask *task; + UpdateData *update_data; + + DEBUG ("Unstarring %i files", g_list_length (selection)); + + task = g_task_new (object, cancellable, callback, NULL); + + query = nautilus_tag_manager_delete_tag (self, selection); + + update_data = g_new0 (UpdateData, 1); + update_data->task = task; + update_data->tag_manager = self; + update_data->selection = nautilus_file_list_copy (selection); + update_data->star = FALSE; + + start_query_or_update (self->db, + query, + on_update_callback, + update_data, + FALSE, + cancellable); + + g_string_free (query, TRUE); +} + +static void +on_tracker_notifier_events (TrackerNotifier *notifier, + gchar *service, + gchar *graph, + GPtrArray *events, + gpointer user_data) +{ + TrackerNotifierEvent *event; + NautilusTagManager *self; + int i; + const gchar *file_url; + GError *error = NULL; + TrackerSparqlCursor *cursor; + gboolean query_has_results = FALSE; + gboolean starred; + GList *changed_files; + NautilusFile *changed_file; + + self = NAUTILUS_TAG_MANAGER (user_data); + + for (i = 0; i < events->len; i++) + { + event = g_ptr_array_index (events, i); + + file_url = tracker_notifier_event_get_urn (event); + changed_file = NULL; + + DEBUG ("Got event for file %s", file_url); + + tracker_sparql_statement_bind_string (self->query_file_is_starred, "file", file_url); + cursor = tracker_sparql_statement_execute (self->query_file_is_starred, + NULL, + &error); + + if (cursor) + { + query_has_results = tracker_sparql_cursor_next (cursor, NULL, &error); + } + + if (error || !cursor || !query_has_results) + { + g_warning ("Couldn't query the starred files database: '%s'", error ? error->message : "(null error)"); + g_clear_error (&error); + return; + } + + starred = tracker_sparql_cursor_get_boolean (cursor, 0); + if (starred) + { + gboolean inserted = g_hash_table_add (self->starred_file_uris, g_strdup (file_url)); + + if (inserted) + { + DEBUG ("Added %s to starred files list", file_url); + changed_file = nautilus_file_get_by_uri (file_url); + } + } + else + { + gboolean removed = g_hash_table_remove (self->starred_file_uris, file_url); + + if (removed) + { + DEBUG ("Removed %s from starred files list", file_url); + changed_file = nautilus_file_get_by_uri (file_url); + } + } + + if (changed_file) + { + changed_files = g_list_prepend (NULL, changed_file); + + g_signal_emit_by_name (self, "starred-changed", changed_files); + + nautilus_file_list_free (changed_files); + } + + g_object_unref (cursor); + } +} + +static void +nautilus_tag_manager_finalize (GObject *object) +{ + NautilusTagManager *self; + + self = NAUTILUS_TAG_MANAGER (object); + + if (self->notifier != NULL) + { + g_signal_handlers_disconnect_by_func (self->notifier, + G_CALLBACK (on_tracker_notifier_events), + self); + } + + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + g_clear_object (&self->notifier); + g_clear_object (&self->db); + g_clear_object (&self->query_file_is_starred); + g_clear_object (&self->query_starred_files); + + g_hash_table_destroy (self->starred_file_uris); + g_clear_object (&self->home); + + G_OBJECT_CLASS (nautilus_tag_manager_parent_class)->finalize (object); +} + +static void +nautilus_tag_manager_class_init (NautilusTagManagerClass *klass) +{ + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS (klass); + + oclass->finalize = nautilus_tag_manager_finalize; + + signals[STARRED_CHANGED] = g_signal_new ("starred-changed", + NAUTILUS_TYPE_TAG_MANAGER, + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); +} + +/** + * nautilus_tag_manager_new: + * + * Returns: (transfer full): the #NautilusTagManager singleton object. + */ +NautilusTagManager * +nautilus_tag_manager_new (void) +{ + if (tag_manager != NULL) + { + return g_object_ref (tag_manager); + } + + tag_manager = g_object_new (NAUTILUS_TYPE_TAG_MANAGER, NULL); + g_object_add_weak_pointer (G_OBJECT (tag_manager), (gpointer) & tag_manager); + + return tag_manager; +} + +/** + * nautilus_tag_manager_new_dummy: + * + * Creates a dummy tag manager without database. + * + * Useful only for tests where the tag manager is needed but not being tested + * and we don't want to fail the tests due to irrelevant D-Bus failures. + * + * Returns: (transfer full): the #NautilusTagManager singleton object. + */ +NautilusTagManager * +nautilus_tag_manager_new_dummy (void) +{ + make_dummy_instance = TRUE; + return nautilus_tag_manager_new (); +} + +/** + * nautilus_tag_manager_get: + * + * Returns: (transfer none): the #NautilusTagManager singleton object. + */ +NautilusTagManager * +nautilus_tag_manager_get (void) +{ + return tag_manager; +} + +static gboolean +setup_database (NautilusTagManager *self, + GCancellable *cancellable, + GError **error) +{ + const gchar *datadir; + g_autofree gchar *store_path = NULL; + g_autofree gchar *ontology_path = NULL; + g_autoptr (GFile) store = NULL; + g_autoptr (GFile) ontology = NULL; + + /* Open private database to store nautilus:starred property. */ + + datadir = NAUTILUS_DATADIR; + + store_path = g_build_filename (g_get_user_data_dir (), "nautilus", "tags", NULL); + ontology_path = g_build_filename (datadir, "ontology", NULL); + + store = g_file_new_for_path (store_path); + ontology = g_file_new_for_path (ontology_path); + + self->db = tracker_sparql_connection_new (TRACKER_SPARQL_CONNECTION_FLAGS_NONE, + store, + ontology, + cancellable, + error); + + if (*error) + { + return FALSE; + } + + /* Prepare reusable queries. */ + self->query_file_is_starred = tracker_sparql_connection_query_statement (self->db, + QUERY_FILE_IS_STARRED, + cancellable, + error); + + if (*error) + { + return FALSE; + } + + self->query_starred_files = tracker_sparql_connection_query_statement (self->db, + QUERY_STARRED_FILES, + cancellable, + error); + + if (*error) + { + return FALSE; + } + + return TRUE; +} + +static void +nautilus_tag_manager_init (NautilusTagManager *self) +{ + g_autoptr (GError) error = NULL; + + self->starred_file_uris = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + /* values are keys */ + NULL); + self->home = g_file_new_for_path (g_get_home_dir ()); + + if (make_dummy_instance) + { + /* Skip database initiation for nautilus_tag_manager_new_dummy(). */ + return; + } + + self->cancellable = g_cancellable_new (); + self->database_ok = setup_database (self, self->cancellable, &error); + if (error) + { + g_warning ("Unable to initialize tag manager: %s", error->message); + return; + } + + self->notifier = tracker_sparql_connection_create_notifier (self->db); + + nautilus_tag_manager_query_starred_files (self, self->cancellable); + + g_signal_connect (self->notifier, + "events", + G_CALLBACK (on_tracker_notifier_events), + self); +} + +gboolean +nautilus_tag_manager_can_star_contents (NautilusTagManager *self, + GFile *directory) +{ + /* We only allow files to be starred inside the home directory for now. + * This avoids the starred files database growing too big. + * See https://gitlab.gnome.org/GNOME/nautilus/-/merge_requests/553#note_903108 + */ + return g_file_has_prefix (directory, self->home) || g_file_equal (directory, self->home); +} + +static void +update_moved_uris_callback (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr (GError) error = NULL; + g_autoptr (GPtrArray) new_uris = user_data; + + tracker_sparql_connection_update_finish (TRACKER_SPARQL_CONNECTION (object), + result, + &error); + + if (error != NULL && error->code != G_IO_ERROR_CANCELLED) + { + g_warning ("Error updating moved uris: %s", error->message); + } + else + { + g_autolist (NautilusFile) updated_files = NULL; + + for (guint i = 0; i < new_uris->len; i++) + { + gchar *new_uri = g_ptr_array_index (new_uris, i); + + updated_files = g_list_prepend (updated_files, nautilus_file_get_by_uri (new_uri)); + } + + g_signal_emit_by_name (tag_manager, "starred-changed", updated_files); + } +} + +/** + * nautilus_tag_manager_update_moved_uris: + * @self: The tag manager singleton + * @src: The original location as a #GFile + * @dest: The new location as a #GFile + * + * Checks whether the rename/move operation (@src to @dest) has modified + * the URIs of any starred files, and updates the database accordingly. + */ +void +nautilus_tag_manager_update_moved_uris (NautilusTagManager *self, + GFile *src, + GFile *dest) +{ + GHashTableIter starred_iter; + gchar *starred_uri; + g_autoptr (GPtrArray) old_uris = NULL; + g_autoptr (GPtrArray) new_uris = NULL; + g_autoptr (GString) query = NULL; + + if (!self->database_ok) + { + g_message ("nautilus-tag-manager: No Tracker connection"); + return; + } + + old_uris = g_ptr_array_new (); + new_uris = g_ptr_array_new_with_free_func (g_free); + + g_hash_table_iter_init (&starred_iter, self->starred_file_uris); + while (g_hash_table_iter_next (&starred_iter, (gpointer *) &starred_uri, NULL)) + { + g_autoptr (GFile) starred_location = NULL; + g_autofree gchar *relative_path = NULL; + + starred_location = g_file_new_for_uri (starred_uri); + + if (g_file_equal (starred_location, src)) + { + /* The moved file/folder is starred */ + g_ptr_array_add (old_uris, starred_uri); + g_ptr_array_add (new_uris, g_file_get_uri (dest)); + continue; + } + + relative_path = g_file_get_relative_path (src, starred_location); + if (relative_path != NULL) + { + /* The starred file/folder is descendant of the moved/renamed directory */ + g_autoptr (GFile) new_location = NULL; + + new_location = g_file_resolve_relative_path (dest, relative_path); + + g_ptr_array_add (old_uris, starred_uri); + g_ptr_array_add (new_uris, g_file_get_uri (new_location)); + } + } + + if (new_uris->len == 0) + { + /* No starred files are affected by this move/rename */ + return; + } + + DEBUG ("Updating moved URI for %i starred files", new_uris->len); + + query = g_string_new ("DELETE DATA {"); + + for (guint i = 0; i < old_uris->len; i++) + { + gchar *old_uri = g_ptr_array_index (old_uris, i); + g_string_append_printf (query, + " <%s> a nautilus:File ; " + " nautilus:starred true . ", + old_uri); + } + + g_string_append (query, "} ; INSERT DATA {"); + + for (guint i = 0; i < new_uris->len; i++) + { + gchar *new_uri = g_ptr_array_index (new_uris, i); + g_string_append_printf (query, + " <%s> a nautilus:File ; " + " nautilus:starred true . ", + new_uri); + } + + g_string_append (query, "}"); + + /* Forward the new_uris list to later pass in the ::files-changed signal. + * There is no need to pass the old_uris because the file model is updated + * independently; we need only inform the view where to display stars now. + */ + tracker_sparql_connection_update_async (self->db, + query->str, + self->cancellable, + update_moved_uris_callback, + g_steal_pointer (&new_uris)); +} + +static void +process_tracker2_data_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NautilusTagManager *self = NAUTILUS_TAG_MANAGER (source_object); + g_autofree gchar *path = tracker2_migration_stamp (); + g_autoptr (GError) error = NULL; + + tracker_sparql_connection_update_finish (self->db, res, &error); + + if (!error) + { + DEBUG ("Data migration was successful. Creating stamp %s", path); + + g_file_set_contents (path, "", -1, &error); + if (error) + { + g_warning ("Failed to create %s after migration: %s", path, error->message); + } + } + else + { + g_warning ("Error during data migration: %s", error->message); + } +} + +static void +process_tracker2_data (NautilusTagManager *self, + GBytes *key_file_data) +{ + g_autoptr (GKeyFile) key_file = NULL; + g_autoptr (GError) error = NULL; + gchar **groups, **group; + GList *selection = NULL; + NautilusFile *file; + + key_file = g_key_file_new (); + g_key_file_load_from_bytes (key_file, + key_file_data, + G_KEY_FILE_NONE, + &error); + g_bytes_unref (key_file_data); + + if (error) + { + g_warning ("Tracker 2 migration: Failed to parse key file data: %s", error->message); + return; + } + + groups = g_key_file_get_groups (key_file, NULL); + + for (group = groups; *group != NULL; group++) + { + file = nautilus_file_get_by_uri (*group); + + if (file) + { + DEBUG ("Tracker 2 migration: starring %s", *group); + selection = g_list_prepend (selection, file); + } + else + { + DEBUG ("Tracker 2 migration: couldn't get NautilusFile for %s", *group); + } + } + + nautilus_tag_manager_star_files (self, + G_OBJECT (self), + selection, + process_tracker2_data_cb, + self->cancellable); + + g_free (groups); +} + +static void +export_tracker2_data_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GInputStream *stream = G_INPUT_STREAM (source_object); + NautilusTagManager *self = NAUTILUS_TAG_MANAGER (user_data); + g_autoptr (GError) error = NULL; + GBytes *key_file_data; + + key_file_data = g_input_stream_read_bytes_finish (stream, res, &error); + + if (key_file_data) + { + process_tracker2_data (self, key_file_data); + } + else + { + g_warning ("Tracker2 migration: Failed to read data from pipe: %s", error->message); + } +} + +static void +child_watch_cb (GPid pid, + gint status, + gpointer user_data) +{ + DEBUG ("Child %" G_PID_FORMAT " exited %s", pid, + g_spawn_check_exit_status (status, NULL) ? "normally" : "abnormally"); + g_spawn_close_pid (pid); +} + +static void +export_tracker2_data (NautilusTagManager *self) +{ + gchar *argv[] = {"tracker3", "export", "--2to3", "files-starred", "--keyfile", NULL}; + gint stdout_fd; + GPid child_pid; + g_autoptr (GError) error = NULL; + gboolean success; + g_autoptr (GInputStream) stream = NULL; + GSpawnFlags flags; + + flags = G_SPAWN_DO_NOT_REAP_CHILD | + G_SPAWN_STDERR_TO_DEV_NULL | + G_SPAWN_SEARCH_PATH; + success = g_spawn_async_with_pipes (NULL, + argv, + NULL, + flags, + NULL, + NULL, + &child_pid, + NULL, + &stdout_fd, + NULL, + &error); + if (!success) + { + g_warning ("Tracker 2 migration: Couldn't run `tracker3`: %s", error->message); + return; + } + + g_child_watch_add (child_pid, child_watch_cb, NULL); + + stream = g_unix_input_stream_new (stdout_fd, TRUE); + g_input_stream_read_bytes_async (stream, + TRACKER2_MAX_IMPORT_BYTES, + G_PRIORITY_LOW, + self->cancellable, + export_tracker2_data_cb, + self); +} + +void +nautilus_tag_manager_maybe_migrate_tracker2_data (NautilusTagManager *self) +{ + g_autofree gchar *path = tracker2_migration_stamp (); + + if (g_file_test (path, G_FILE_TEST_EXISTS)) + { + DEBUG ("Tracker 2 migration: already completed."); + } + else + { + DEBUG ("Tracker 2 migration: starting."); + export_tracker2_data (self); + } +} |