diff --git a/src/engine/api/geary-account-information.vala b/src/engine/api/geary-account-information.vala index 980ef711..cdc05eb6 100644 --- a/src/engine/api/geary-account-information.vala +++ b/src/engine/api/geary-account-information.vala @@ -29,7 +29,7 @@ public class Geary.AccountInformation : BaseObject { if (parts == null || parts.size == 0) return null; - Geary.FolderPath path = new Imap.FolderRoot(); + Geary.FolderPath path = new Imap.FolderRoot("#geary-config"); foreach (string basename in parts) { path = path.get_child(basename); } diff --git a/src/engine/api/geary-folder-path.vala b/src/engine/api/geary-folder-path.vala index 746c5d58..dfd01be0 100644 --- a/src/engine/api/geary-folder-path.vala +++ b/src/engine/api/geary-folder-path.vala @@ -19,7 +19,7 @@ public class Geary.FolderPath : /** Type of the GLib.Variant used to represent folder paths */ - public const string VARIANT_TYPE = "as"; + public const string VARIANT_TYPE = "(sas)"; // Workaround for Vala issue #659. See children below. @@ -233,7 +233,10 @@ public class Geary.FolderPath : * @see FolderRoot.from_folder_path */ public GLib.Variant to_variant() { - return new GLib.Variant.strv(as_array()); + return new GLib.Variant.tuple(new GLib.Variant[] { + get_root().label, + as_array() + }); } /** @@ -278,11 +281,14 @@ public class Geary.FolderPath : bool allow_case_sensitive, bool normalize) { int cmp = 0; - if (a.parent != null && b.parent != null) { + if (a.parent == null && b.parent == null) { + cmp = strcmp(((FolderRoot) a).label, ((FolderRoot) b).label); + } else { cmp = compare_names( a.parent, b.parent, allow_case_sensitive, normalize ); } + if (cmp == 0) { string a_name = a.name; string b_name = b.name; @@ -318,6 +324,15 @@ public class Geary.FolderPath : public class Geary.FolderRoot : FolderPath { + /** + * A label for a folder root. + * + * Since there may be multiple folder roots (for example, local + * and remote folders, or for different remote namespaces), the + * label can be used to look up a specific root. + */ + public string label { get; private set; } + /** * The default case sensitivity of descendant folders. * @@ -329,26 +344,51 @@ public class Geary.FolderRoot : FolderPath { /** * Constructs a new folder root with given default sensitivity. */ - public FolderRoot(bool default_case_sensitivity) { + public FolderRoot(string label, bool default_case_sensitivity) { base(); + this.label = label; this.default_case_sensitivity = default_case_sensitivity; } + /** + * Copies a folder path using this as the root. + * + * This method can be used to simply copy a path, or change the + * root that a path is attached to. + */ + public FolderPath copy(FolderPath original) { + FolderPath copy = this; + foreach (string step in original.as_array()) { + copy = copy.get_child(step); + } + return copy; + } + /** * Reconstructs a path under this root from a GLib variant. * * @see FolderPath.to_variant + * @throws EngineError.BAD_PARAMETERS when the variant is not the + * have the correct type or if the given root label does not match + * this root's label. */ public FolderPath from_variant(GLib.Variant serialised) - throws EngineError { + throws EngineError.BAD_PARAMETERS { if (serialised.get_type_string() != VARIANT_TYPE) { throw new EngineError.BAD_PARAMETERS( "Invalid serialised id type: %s", serialised.get_type_string() ); } + string label = (string) serialised.get_child_value(0); + if (this.label != label) { + throw new EngineError.BAD_PARAMETERS( + "Invalid serialised folder root label: %s", label + ); + } + FolderPath path = this; - foreach (string step in serialised.get_strv()) { + foreach (string step in serialised.get_child_value(1).get_strv()) { path = path.get_child(step); } return path; diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala index c8341fe8..6b7d5b28 100644 --- a/src/engine/imap-db/imap-db-account.vala +++ b/src/engine/imap-db/imap-db-account.vala @@ -86,7 +86,7 @@ private class Geary.ImapDB.Account : BaseObject { * @see list_folders_async */ public Imap.FolderRoot imap_folder_root { - get; private set; default = new Imap.FolderRoot(); + get; private set; default = new Imap.FolderRoot("$geary-imap"); } // Only available when the Account is opened diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala b/src/engine/imap-engine/imap-engine-generic-account.vala index 908d8d94..acb92b48 100644 --- a/src/engine/imap-engine/imap-engine-generic-account.vala +++ b/src/engine/imap-engine/imap-engine-generic-account.vala @@ -43,7 +43,9 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account { * No folder exists for this path, it merely exists to provide a * common root for the paths of all local folders. */ - protected FolderRoot local_folder_root = new Geary.FolderRoot(true); + protected FolderRoot local_folder_root = new Geary.FolderRoot( + "$geary-local", true + ); private bool open = false; private Cancellable? open_cancellable = null; @@ -723,13 +725,20 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account { Folder? special = get_special_folder(type); if (special == null) { FolderPath? path = information.get_special_folder_path(type); - if (path != null && !remote.is_folder_path_valid(path)) { - debug("%s: Ignoring bad special folder path '%s' for type %s", - to_string(), - path.to_string(), - type.to_string()); - path = null; + if (path != null) { + if (!remote.is_folder_path_valid(path)) { + warning( + "%s: Ignoring bad special folder path '%s' for type %s", + to_string(), + path.to_string(), + type.to_string() + ); + path = null; + } else { + path = this.local.imap_folder_root.copy(path); + } } + if (path == null) { FolderPath root = yield remote.get_default_personal_namespace(cancellable); @@ -1113,6 +1122,7 @@ internal class Geary.ImapEngine.LoadFolders : AccountOperation { if (generic.get_special_folder(type) == null) { Geary.FolderPath? path = generic.information.get_special_folder_path(type); + path = this.local.imap_folder_root.copy(path); if (path != null) { try { Geary.Folder target = yield generic.fetch_folder_async( diff --git a/src/engine/imap/api/imap-folder-root.vala b/src/engine/imap/api/imap-folder-root.vala index bbe525b6..1a1f2e99 100644 --- a/src/engine/imap/api/imap-folder-root.vala +++ b/src/engine/imap/api/imap-folder-root.vala @@ -31,8 +31,8 @@ public class Geary.Imap.FolderRoot : Geary.FolderRoot { public FolderPath inbox { get; private set; } - public FolderRoot() { - base(false); + public FolderRoot(string label) { + base(label, false); this.inbox = base.get_child( MailboxSpecifier.CANONICAL_INBOX_NAME, Trillian.FALSE diff --git a/test/engine/api/geary-folder-path-test.vala b/test/engine/api/geary-folder-path-test.vala index 952ca44c..018a3045 100644 --- a/test/engine/api/geary-folder-path-test.vala +++ b/test/engine/api/geary-folder-path-test.vala @@ -8,6 +8,8 @@ public class Geary.FolderPathTest : TestCase { + private const string TEST_LABEL = "#test"; + private FolderRoot? root = null; @@ -19,18 +21,19 @@ public class Geary.FolderPathTest : TestCase { add_test("child_is_not_root", root_is_root); add_test("as_array", as_array); add_test("is_top_level", is_top_level); + add_test("distinct_roots_compare", distinct_roots_compare); add_test("path_to_string", path_to_string); add_test("path_parent", path_parent); add_test("path_equal", path_equal); add_test("path_hash", path_hash); add_test("path_compare", path_compare); add_test("path_compare_normalised", path_compare_normalised); - add_test("distinct_roots_compare", distinct_roots_compare); + add_test("root_instances_compare", root_instances_compare); add_test("variant_representation", variant_representation); } public override void set_up() { - this.root = new FolderRoot(false); + this.root = new FolderRoot(TEST_LABEL, false); } public override void tear_down() { @@ -100,6 +103,17 @@ public class Geary.FolderPathTest : TestCase { ); } + public void distinct_roots_compare() throws GLib.Error { + assert_true( + this.root.compare_to(new FolderRoot(TEST_LABEL, false)) == 0, + "Root label equality" + ); + assert_true( + this.root.compare_to(new FolderRoot("#other", false)) > 0, + "Root label inequality" + ); + } + public void path_to_string() throws GLib.Error { assert_string(">", this.root.to_string()); assert_string(">test", this.root.get_child("test").to_string()); @@ -257,50 +271,59 @@ public class Geary.FolderPathTest : TestCase { ); } - public void distinct_roots_compare() throws GLib.Error { - assert_int(0, this.root.compare_to(new FolderRoot(false)), "Root equality"); - assert_int(0, - this.root.get_child("a").compare_to(new FolderRoot(false).get_child("a")), + public void root_instances_compare() throws GLib.Error { + assert_int( + 0, this.root.compare_to(new FolderRoot(TEST_LABEL, false)), + "Root equality" + ); + assert_int( + 0, this.root.get_child("a").compare_to(new FolderRoot(TEST_LABEL, false).get_child("a")), "Equal child comparison" ); + assert_true( + this.root.get_child("a").compare_to( + new FolderRoot("#other", false).get_child("a")) > 0, + "Root label inequality with children" + ); + // a is less than b assert_true( - this.root.get_child("a").compare_to(new FolderRoot(false).get_child("b")) < 0, + this.root.get_child("a").compare_to(new FolderRoot(TEST_LABEL, false).get_child("b")) < 0, "Greater than child comparison" ); // b is greater than than a assert_true( - this.root.get_child("b").compare_to(new FolderRoot(false).get_child("a")) > 0, + this.root.get_child("b").compare_to(new FolderRoot(TEST_LABEL, false).get_child("a")) > 0, "Less than child comparison" ); assert_true( this.root.get_child("a").get_child("test") - .compare_to(new FolderRoot(false).get_child("a")) > 0, + .compare_to(new FolderRoot(TEST_LABEL, false).get_child("a")) > 0, "Greater than descendant" ); assert_true( this.root.get_child("a") - .compare_to(new FolderRoot(false).get_child("a").get_child("test")) < 0, + .compare_to(new FolderRoot(TEST_LABEL, false).get_child("a").get_child("test")) < 0, "Less than descendant" ); assert_true( this.root.get_child("a").get_child("b") - .compare_to(new FolderRoot(false).get_child("a").get_child("b")) == 0, + .compare_to(new FolderRoot(TEST_LABEL, false).get_child("a").get_child("b")) == 0, "N-path equality" ); assert_true( this.root.get_child("a").get_child("a") - .compare_to(new FolderRoot(false).get_child("b").get_child("b")) < 0, + .compare_to(new FolderRoot(TEST_LABEL, false).get_child("b").get_child("b")) < 0, "Less than double disjoint" ); assert_true( this.root.get_child("b").get_child("a") - .compare_to(new FolderRoot(false).get_child("a").get_child("a")) > 0, + .compare_to(new FolderRoot(TEST_LABEL, false).get_child("a").get_child("a")) > 0, "Greater than double disjoint" ); diff --git a/test/engine/app/app-conversation-monitor-test.vala b/test/engine/app/app-conversation-monitor-test.vala index 582aa0b7..a120e714 100644 --- a/test/engine/app/app-conversation-monitor-test.vala +++ b/test/engine/app/app-conversation-monitor-test.vala @@ -37,7 +37,7 @@ class Geary.App.ConversationMonitorTest : TestCase { new RFC822.MailboxAddress(null, "test1@example.com") ); this.account = new MockAccount(this.account_info); - this.folder_root = new FolderRoot(false); + this.folder_root = new FolderRoot("#test", false); this.base_folder = new MockFolder( this.account, null, diff --git a/test/engine/app/app-conversation-set-test.vala b/test/engine/app/app-conversation-set-test.vala index 7a7fedc2..29ac6512 100644 --- a/test/engine/app/app-conversation-set-test.vala +++ b/test/engine/app/app-conversation-set-test.vala @@ -28,7 +28,7 @@ class Geary.App.ConversationSetTest : TestCase { } public override void set_up() { - this.folder_root = new FolderRoot(false); + this.folder_root = new FolderRoot("#test", false); this.base_folder = new MockFolder( null, null, diff --git a/test/engine/app/app-conversation-test.vala b/test/engine/app/app-conversation-test.vala index 1ee1f4a4..a20273ac 100644 --- a/test/engine/app/app-conversation-test.vala +++ b/test/engine/app/app-conversation-test.vala @@ -28,7 +28,7 @@ class Geary.App.ConversationTest : TestCase { } public override void set_up() { - this.folder_root = new FolderRoot(false); + this.folder_root = new FolderRoot("#test", false); this.base_folder = new MockFolder( null, null, diff --git a/test/engine/imap-db/imap-db-account-test.vala b/test/engine/imap-db/imap-db-account-test.vala index 26ccf91b..85b54c60 100644 --- a/test/engine/imap-db/imap-db-account-test.vala +++ b/test/engine/imap-db/imap-db-account-test.vala @@ -49,7 +49,7 @@ class Geary.ImapDB.AccountTest : TestCase { ); this.account.open_async.end(async_result()); - this.root = new FolderRoot(false); + this.root = new FolderRoot("#test", false); } public override void tear_down() throws GLib.Error { diff --git a/test/engine/imap/message/imap-mailbox-specifier-test.vala b/test/engine/imap/message/imap-mailbox-specifier-test.vala index 6488e5e9..d3a52046 100644 --- a/test/engine/imap/message/imap-mailbox-specifier-test.vala +++ b/test/engine/imap/message/imap-mailbox-specifier-test.vala @@ -60,7 +60,7 @@ class Geary.Imap.MailboxSpecifierTest : TestCase { } public void from_folder_path() throws Error { - FolderRoot root = new FolderRoot(); + FolderRoot root = new FolderRoot("#test"); MailboxSpecifier inbox = new MailboxSpecifier("Inbox"); assert_string( "Foo", @@ -110,7 +110,7 @@ class Geary.Imap.MailboxSpecifierTest : TestCase { } public void folder_path_is_inbox() throws GLib.Error { - FolderRoot root = new FolderRoot(); + FolderRoot root = new FolderRoot("#test"); assert_true( MailboxSpecifier.folder_path_is_inbox(root.get_child("Inbox")) );