From d7e05ab20e780042e6d7ee90f323a65c97b857f4 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Tue, 13 Oct 2020 19:23:58 +1100 Subject: [PATCH 1/9] client: Move Migrate namespace to be under Util --- src/client/application/application-configuration.vala | 2 +- src/client/application/application-controller.vala | 6 ++++-- src/client/util/util-migrate.vala | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/client/application/application-configuration.vala b/src/client/application/application-configuration.vala index 48542df6..eaaed36f 100644 --- a/src/client/application/application-configuration.vala +++ b/src/client/application/application-configuration.vala @@ -146,7 +146,7 @@ public class Application.Configuration : Geary.BaseObject { settings = new Settings(schema_id); gnome_interface = new Settings("org.gnome.desktop.interface"); - Migrate.old_app_config(settings); + Util.Migrate.old_app_config(settings); this.bind(SINGLE_KEY_SHORTCUTS, this, SINGLE_KEY_SHORTCUTS); } diff --git a/src/client/application/application-controller.vala b/src/client/application/application-controller.vala index 55864cda..9fd5966f 100644 --- a/src/client/application/application-controller.vala +++ b/src/client/application/application-controller.vala @@ -171,8 +171,10 @@ internal class Application.Controller : ); // Migrate configuration if necessary. - Migrate.xdg_config_dir(this.application.get_user_data_directory(), - this.application.get_user_config_directory()); + Util.Migrate.xdg_config_dir( + this.application.get_user_data_directory(), + this.application.get_user_config_directory() + ); // Hook up cert, accounts and credentials machinery diff --git a/src/client/util/util-migrate.vala b/src/client/util/util-migrate.vala index f4a905ed..9cebcfc6 100644 --- a/src/client/util/util-migrate.vala +++ b/src/client/util/util-migrate.vala @@ -4,7 +4,7 @@ * (version 2.1 or later). See the COPYING file in this distribution. */ -namespace Migrate { +namespace Util.Migrate { private const string GROUP = "AccountInformation"; private const string PRIMARY_EMAIL_KEY = "primary_email"; private const string SETTINGS_FILENAME = Accounts.Manager.SETTINGS_FILENAME; From e8061379ec022f8ce1ecb163a574192af5b8ac80 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Tue, 13 Oct 2020 23:32:46 +1100 Subject: [PATCH 2/9] Application.Controller: Clean up config/data vars in ctor --- src/client/application/application-controller.vala | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/client/application/application-controller.vala b/src/client/application/application-controller.vala index 9fd5966f..ef634489 100644 --- a/src/client/application/application-controller.vala +++ b/src/client/application/application-controller.vala @@ -125,6 +125,9 @@ internal class Application.Controller : this.application = application; this.controller_open = cancellable; + GLib.File config_dir = application.get_user_config_directory(); + GLib.File data_dir = application.get_user_data_directory(); + // This initializes the IconFactory, important to do before // the actions are created (as they refer to some of Geary's // custom icons) @@ -139,9 +142,7 @@ internal class Application.Controller : this.application.get_web_extensions_dir(), this.application.get_user_cache_directory().get_child("web-resources") ); - Components.WebView.load_resources( - this.application.get_user_config_directory() - ); + Components.WebView.load_resources(config_dir); Composer.WebView.load_resources(); ConversationWebView.load_resources(); Accounts.SignatureWebView.load_resources(); @@ -179,7 +180,7 @@ internal class Application.Controller : // Hook up cert, accounts and credentials machinery this.certificate_manager = yield new Application.CertificateManager( - this.application.get_user_data_directory().get_child("pinned-certs"), + config_dir.get_child("pinned-certs"), cancellable ); @@ -189,8 +190,8 @@ internal class Application.Controller : this.account_manager = new Accounts.Manager( libsecret, - this.application.get_user_config_directory(), - this.application.get_user_data_directory() + config_dir, + data_dir ); this.account_manager.account_added.connect( on_account_added From 693484148432c7e23ca255e9759b1ff5b24fdfca Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Tue, 13 Oct 2020 23:33:55 +1100 Subject: [PATCH 3/9] Application.Controller: Clean up invoking XDG config migration Order args to better, create the config dir in the controller since that is always needed, regardless of any migrations. --- src/client/application/application-controller.vala | 12 ++++++++---- src/client/util/util-migrate.vala | 13 +++---------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/client/application/application-controller.vala b/src/client/application/application-controller.vala index ef634489..24c244c8 100644 --- a/src/client/application/application-controller.vala +++ b/src/client/application/application-controller.vala @@ -171,11 +171,15 @@ internal class Application.Controller : this.application.get_app_plugins_dir() ); + // Create standard config directory + try { + config_dir.make_directory_with_parents(); + } catch (GLib.IOError.EXISTS err) { + // fine + } + // Migrate configuration if necessary. - Util.Migrate.xdg_config_dir( - this.application.get_user_data_directory(), - this.application.get_user_config_directory() - ); + Util.Migrate.xdg_config_dir(config_dir, data_dir); // Hook up cert, accounts and credentials machinery diff --git a/src/client/util/util-migrate.vala b/src/client/util/util-migrate.vala index 9cebcfc6..edb07b45 100644 --- a/src/client/util/util-migrate.vala +++ b/src/client/util/util-migrate.vala @@ -19,21 +19,14 @@ namespace Util.Migrate { * It also appends a "primary_email" key to the new configuration file to reliaby keep * track of the user's email address. */ - public static void xdg_config_dir(File user_data_dir, File user_config_dir) throws Error { + public static void xdg_config_dir(GLib.File user_config_dir, + GLib.File user_data_dir) + throws GLib.Error { File new_config_dir; File old_data_dir; File new_config_file; File old_config_file; - // Create ~/.config/geary - try { - user_config_dir.make_directory_with_parents(); - } catch (Error err) { - // The user may have already created the directory, so don't throw EXISTS. - if (!(err is IOError.EXISTS)) - throw err; - } - // Return if Geary has never been run (~/.local/share/geary does not exist). if (!user_data_dir.query_exists()) return; From 468ea6df5845b0014396114debc5ace6cbded769 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Tue, 13 Oct 2020 23:40:02 +1100 Subject: [PATCH 4/9] Application.Client: Rename app user dirs to home dirs --- src/client/application/application-client.vala | 12 ++++++------ src/client/application/application-controller.vala | 8 +++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/client/application/application-client.vala b/src/client/application/application-client.vala index e7f980c0..9bd05c31 100644 --- a/src/client/application/application-client.vala +++ b/src/client/application/application-client.vala @@ -759,22 +759,22 @@ public class Application.Client : Gtk.Application { } } - /** Returns the application's base user configuration directory. */ - public GLib.File get_user_config_directory() { + /** Returns the application's base home configuration directory. */ + public GLib.File get_home_config_directory() { return GLib.File.new_for_path( Environment.get_user_config_dir() ).get_child("geary"); } - /** Returns the application's base user cache directory. */ - public GLib.File get_user_cache_directory() { + /** Returns the application's base home cache directory. */ + public GLib.File get_home_cache_directory() { return GLib.File.new_for_path( GLib.Environment.get_user_cache_dir() ).get_child("geary"); } - /** Returns the application's base user data directory. */ - public GLib.File get_user_data_directory() { + /** Returns the application's base home data directory. */ + public GLib.File get_home_data_directory() { return GLib.File.new_for_path( GLib.Environment.get_user_data_dir() ).get_child("geary"); diff --git a/src/client/application/application-controller.vala b/src/client/application/application-controller.vala index 24c244c8..a70b702b 100644 --- a/src/client/application/application-controller.vala +++ b/src/client/application/application-controller.vala @@ -125,8 +125,8 @@ internal class Application.Controller : this.application = application; this.controller_open = cancellable; - GLib.File config_dir = application.get_user_config_directory(); - GLib.File data_dir = application.get_user_data_directory(); + GLib.File config_dir = application.get_home_config_directory(); + GLib.File data_dir = application.get_home_data_directory(); // This initializes the IconFactory, important to do before // the actions are created (as they refer to some of Geary's @@ -140,7 +140,9 @@ internal class Application.Controller : Components.WebView.init_web_context( this.application.config, this.application.get_web_extensions_dir(), - this.application.get_user_cache_directory().get_child("web-resources") + this.application.get_home_cache_directory().get_child( + "web-resources" + ) ); Components.WebView.load_resources(config_dir); Composer.WebView.load_resources(); From 9658e9e3b41e9f851384c2a0ddce187fd40f1414 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Tue, 13 Oct 2020 23:42:53 +1100 Subject: [PATCH 5/9] Geary.Controller: Migrate release config if needed If the current config directory is empty, go looking for config data in other well known locations and if found, copy it all across from the most recently modified directory. This supports migrating config from non-Flatpak to Flatpak locations, and release config to devel profile locations. Fixes #326 --- .../application/application-client.vala | 28 ++++ .../application/application-controller.vala | 3 + src/client/util/util-migrate.vala | 149 +++++++++++++++++- 3 files changed, 177 insertions(+), 3 deletions(-) diff --git a/src/client/application/application-client.vala b/src/client/application/application-client.vala index 9bd05c31..a61a6855 100644 --- a/src/client/application/application-client.vala +++ b/src/client/application/application-client.vala @@ -858,6 +858,34 @@ public class Application.Client : Gtk.Application { } } + /** + * Returns a set of paths of possible config locations. + * + * This is useful only for migrating configuration from + * non-Flatpak to Flatpak or release-builds to non-release builds. + */ + internal GLib.File[] get_config_search_path() { + var paths = new GLib.File[] {}; + var home = GLib.File.new_for_path(GLib.Environment.get_home_dir()); + paths += home.get_child( + ".config" + ).get_child( + "geary" + ); + paths += home.get_child( + ".var" + ).get_child( + "app" + ).get_child( + "org.gnome.Geary" + ).get_child( + "config" + ).get_child( + "geary" + ); + return paths; + } + /** * Displays an error notification. * diff --git a/src/client/application/application-controller.vala b/src/client/application/application-controller.vala index a70b702b..2237aa61 100644 --- a/src/client/application/application-controller.vala +++ b/src/client/application/application-controller.vala @@ -182,6 +182,9 @@ internal class Application.Controller : // Migrate configuration if necessary. Util.Migrate.xdg_config_dir(config_dir, data_dir); + Util.Migrate.release_config( + application.get_config_search_path(), config_dir + ); // Hook up cert, accounts and credentials machinery diff --git a/src/client/util/util-migrate.vala b/src/client/util/util-migrate.vala index edb07b45..31109c45 100644 --- a/src/client/util/util-migrate.vala +++ b/src/client/util/util-migrate.vala @@ -1,8 +1,10 @@ -/* Copyright 2016 Software Freedom Conservancy Inc. +/* + * Copyright © 2016 Software Freedom Conservancy Inc. + * Copyright © 2020 Michael Gratton * * This software is licensed under the GNU Lesser General Public License - * (version 2.1 or later). See the COPYING file in this distribution. - */ + * (version 2.1 or later). See the COPYING file in this distribution. +n */ namespace Util.Migrate { private const string GROUP = "AccountInformation"; @@ -102,6 +104,145 @@ namespace Util.Migrate { } } + /** + * Migrates configuration from release build locations. + * + * This will migrate configuration from release build locations to + * the current config directory, if and only if the current config + * directory is empty. For example, from the standard + * distro-package config location to the current Flatpak location, + * or from either to a development config location. + */ + public static void release_config(GLib.File[] search_path, + GLib.File config_dir) + throws GLib.Error { + if (is_directory_empty(config_dir)) { + GLib.File? most_recent = null; + GLib.DateTime most_recent_modified = null; + foreach (var source in search_path) { + if (!source.equal(config_dir)) { + GLib.DateTime? src_modified = null; + try { + GLib.FileInfo? src_info = source.query_info( + GLib.FileAttribute.TIME_MODIFIED, 0 + ); + if (src_info != null) { + src_modified = + src_info.get_modification_date_time(); + } + } catch (GLib.IOError.NOT_FOUND err) { + // fine + } catch (GLib.Error err) { + debug( + "Error querying release config dir %s: %s", + source.get_path(), + err.message + ); + } + if (most_recent_modified == null || + (src_modified != null && + most_recent_modified.compare(src_modified) < 0)) { + most_recent = source; + most_recent_modified = src_modified; + } + } + } + + if (most_recent != null) { + try { + debug( + "Migrating release config from %s to %s", + most_recent.get_path(), + config_dir.get_path() + ); + recursive_copy(most_recent, config_dir); + } catch (GLib.Error err) { + debug("Error migrating release config: %s", err.message); + } + } + } + } + + private bool is_directory_empty(GLib.File dir) { + bool is_empty = true; + GLib.FileEnumerator? existing = null; + try { + existing = dir.enumerate_children( + GLib.FileAttribute.STANDARD_TYPE, 0 + ); + } catch (GLib.IOError.NOT_FOUND err) { + // fine + } catch (GLib.Error err) { + debug( + "Error enumerating directory %s: %s", + dir.get_path(), + err.message + ); + } + + if (existing != null) { + try { + is_empty = existing.next_file() == null; + } catch (GLib.Error err) { + debug( + "Error getting next child in directory %s: %s", + dir.get_path(), + err.message + ); + } + + try { + existing.close(); + } catch (GLib.Error err) { + debug( + "Error closing directory enumeration %s: %s", + dir.get_path(), + err.message + ); + } + } + + return is_empty; + } + + private static void recursive_copy(GLib.File src, + GLib.File dest, + GLib.Cancellable? cancellable = null + ) throws GLib.Error { + switch (src.query_file_type(NONE, cancellable)) { + case DIRECTORY: + try { + dest.make_directory(cancellable); + } catch (GLib.IOError.EXISTS err) { + // fine + } + src.copy_attributes(dest, NONE, cancellable); + + GLib.FileEnumerator children = src.enumerate_children( + GLib.FileAttribute.STANDARD_NAME, + NONE, + cancellable + ); + GLib.FileInfo? child = children.next_file(cancellable); + while (child != null) { + recursive_copy( + src.get_child(child.get_name()), + dest.get_child(child.get_name()) + ); + child = children.next_file(cancellable); + } + break; + + case REGULAR: + src.copy(dest, NONE, cancellable); + break; + + default: + // no-op + break; + } + } + public const string OLD_APP_ID = "org.yorba.geary"; private const string MIGRATED_CONFIG_KEY = "migrated-config"; @@ -130,4 +271,6 @@ namespace Util.Migrate { newSettings.set_boolean(MIGRATED_CONFIG_KEY, true); } + + } From 899834189cb8a077fe2b67b0088761cfae5d7452 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Tue, 13 Oct 2020 23:45:44 +1100 Subject: [PATCH 6/9] org.gnome.Geary.yaml: Enable copying release config for nightlies --- org.gnome.Geary.json | 2 ++ org.gnome.Geary.yaml | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/org.gnome.Geary.json b/org.gnome.Geary.json index 99e41612..7f55fb1f 100644 --- a/org.gnome.Geary.json +++ b/org.gnome.Geary.json @@ -25,6 +25,8 @@ "--talk-name=org.gnome.evolution.dataserver.Sources5", "--filesystem=xdg-cache/evolution/addressbook:ro", "--metadata=X-DConf=migrate-path=/org/gnome/Geary/", + "--filesystem=~/.config/geary:ro", + "--filesystem=~/.var/app/org.gnome.Geary/config/geary:ro", "--filesystem=/tmp" ], "cleanup": [ diff --git a/org.gnome.Geary.yaml b/org.gnome.Geary.yaml index 0dd89a18..f5d200f6 100644 --- a/org.gnome.Geary.yaml +++ b/org.gnome.Geary.yaml @@ -57,6 +57,10 @@ finish-args: # Migrate GSettings into the sandbox - "--metadata=X-DConf=migrate-path=/org/gnome/Geary/" + # Migrate Geary settings from other release versions + - "--filesystem=~/.config/geary:ro" + - "--filesystem=~/.var/app/org.gnome.Geary/config/geary:ro" + # Let view source keep on working as-sis for now. Bug 779311. */ - "--filesystem=/tmp" From 072156096d61889cc61445738e5122ad451bad61 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Wed, 14 Oct 2020 00:53:04 +1100 Subject: [PATCH 7/9] Application.Client: Support determining if running under flatpak --- src/client/application/application-client.vala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/client/application/application-client.vala b/src/client/application/application-client.vala index a61a6855..7e8d9fbd 100644 --- a/src/client/application/application-client.vala +++ b/src/client/application/application-client.vala @@ -212,6 +212,14 @@ public class Application.Client : Gtk.Application { } } + /** + * Determines if Geary appears to be running under Flatpak. + * + * If this returns `true`, then the application instance + * appears to be running inside a Flatpak sandbox. + */ + public bool is_flatpak_sandboxed { get; private set; } + /** * The global controller for this application instance. * @@ -317,6 +325,7 @@ public class Application.Client : Gtk.Application { ); this.add_main_option_entries(OPTION_ENTRIES); this.window_removed.connect_after(on_window_removed); + this.is_flatpak_sandboxed = GLib.FileUtils.test("/.flatpak-info", EXISTS); } public override bool local_command_line(ref unowned string[] args, From 99fc14a4e590596de263ecaa7a834b80e7793fa3 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Wed, 14 Oct 2020 00:53:35 +1100 Subject: [PATCH 8/9] Application.Client: Introduce and use consts for build profiles --- src/client/application/application-client.vala | 5 +++++ src/client/application/application-main-window.vala | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/client/application/application-client.vala b/src/client/application/application-client.vala index 7e8d9fbd..6b3e7113 100644 --- a/src/client/application/application-client.vala +++ b/src/client/application/application-client.vala @@ -43,6 +43,11 @@ public class Application.Client : Gtk.Application { public const string SOURCE_ROOT_DIR = _SOURCE_ROOT_DIR; public const string BUILD_ROOT_DIR = _BUILD_ROOT_DIR; + // keep these in sync with meson_options.txt + public const string PROFILE_RELEASE = "release"; + public const string PROFILE_BETA = "beta"; + public const string PROFILE_DEVEL = "development"; + public const string[] AUTHORS = { "Jim Nelson ", "Eric Gregory ", diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index 2b77a6d7..90d5b249 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -507,7 +507,7 @@ public class Application.MainWindow : load_config(application.config); restore_saved_window_state(); - if (_PROFILE != "release") { + if (_PROFILE != Client.PROFILE_RELEASE) { this.get_style_context().add_class("devel"); } From 368a0ced9785a2270ff9ddbda37e150b3970558b Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Wed, 14 Oct 2020 00:58:51 +1100 Subject: [PATCH 9/9] Application.Client: Ensure non-release builds don't clobber release data Append the build profile to Geary's data directories when not running a release build (or under Flatpak) so that e.g. development builds use different config, cache and data directories. This allows us to perform things like database schema updates with relative abandon, since if we ask people to test development builds with schema updates, they can always safely go back to their release builds again. --- .../application/application-client.vala | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/client/application/application-client.vala b/src/client/application/application-client.vala index 6b3e7113..db8cdfdc 100644 --- a/src/client/application/application-client.vala +++ b/src/client/application/application-client.vala @@ -777,21 +777,21 @@ public class Application.Client : Gtk.Application { public GLib.File get_home_config_directory() { return GLib.File.new_for_path( Environment.get_user_config_dir() - ).get_child("geary"); + ).get_child(get_geary_home_dir_name()); } /** Returns the application's base home cache directory. */ public GLib.File get_home_cache_directory() { return GLib.File.new_for_path( GLib.Environment.get_user_cache_dir() - ).get_child("geary"); + ).get_child(get_geary_home_dir_name()); } /** Returns the application's base home data directory. */ public GLib.File get_home_data_directory() { return GLib.File.new_for_path( GLib.Environment.get_user_data_dir() - ).get_child("geary"); + ).get_child(get_geary_home_dir_name()); } /** Returns the application's base static resources directory. */ @@ -1188,6 +1188,22 @@ public class Application.Client : Gtk.Application { } } + private string get_geary_home_dir_name() { + // Return the standard name if running a release build or + // running under Flatpak, otherwise append the build profile + // as a suffix so (e.g.) devel builds don't mess with release + // build's config and databases. + // + // Note that non-release Flatpak builds already have their own + // separate directories since they have different app ids, and + // hence don't need the suffix. + return ( + _PROFILE == PROFILE_RELEASE || this.is_flatpak_sandboxed + ? "geary" + : "geary-" + _PROFILE + ); + } + private void on_activate_about() { this.show_about.begin(); }