[geary] Show Inbox children on XLIST non-English accounts: Closes bug #729372
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary] Show Inbox children on XLIST non-English accounts: Closes bug #729372
- Date: Thu, 15 May 2014 01:39:17 +0000 (UTC)
commit 711ae1ca83a98c87d83207b3022b06cec570fb23
Author: Jim Nelson <jim yorba org>
Date: Wed May 14 18:35:35 2014 -0700
Show Inbox children on XLIST non-English accounts: Closes bug #729372
Although a prior fix dealt with the XLIST issue of a translated Inbox
name ("Posteingang") not being available when directly addressing the
folder ("INBOX"), it didn't deal with addressing children (subfolders)
of Inbox ("Posteingang/eBay") -> "INBOX/eBay"). This ensures Geary
can always identify Inbox, whether its translated name or INBOX.
.../yahoo/imap-engine-yahoo-account.vala | 2 +-
src/engine/imap/api/imap-account.vala | 78 ++++++++++++++++++--
src/engine/imap/api/imap-folder.vala | 8 +-
.../imap/message/imap-mailbox-specifier.vala | 21 +++++-
.../imap/response/imap-mailbox-information.vala | 28 ++++---
src/engine/imap/response/imap-server-data.vala | 2 +-
6 files changed, 112 insertions(+), 27 deletions(-)
---
diff --git a/src/engine/imap-engine/yahoo/imap-engine-yahoo-account.vala
b/src/engine/imap-engine/yahoo/imap-engine-yahoo-account.vala
index 68958e5..01a3086 100644
--- a/src/engine/imap-engine/yahoo/imap-engine-yahoo-account.vala
+++ b/src/engine/imap-engine/yahoo/imap-engine-yahoo-account.vala
@@ -40,7 +40,7 @@ private class Geary.ImapEngine.YahooAccount : Geary.ImapEngine.GenericAccount {
if (special_map == null) {
special_map = new Gee.HashMap<Geary.FolderPath, Geary.SpecialFolderType>();
- special_map.set(Imap.MailboxSpecifier.inbox.to_folder_path(), Geary.SpecialFolderType.INBOX);
+ special_map.set(Imap.MailboxSpecifier.inbox.to_folder_path(null, null),
Geary.SpecialFolderType.INBOX);
special_map.set(new Imap.FolderRoot("Sent", null), Geary.SpecialFolderType.SENT);
special_map.set(new Imap.FolderRoot("Draft", null), Geary.SpecialFolderType.DRAFTS);
special_map.set(new Imap.FolderRoot("Bulk Mail", null), Geary.SpecialFolderType.SPAM);
diff --git a/src/engine/imap/api/imap-account.vala b/src/engine/imap/api/imap-account.vala
index 0f4de58..6be8706 100644
--- a/src/engine/imap/api/imap-account.vala
+++ b/src/engine/imap/api/imap-account.vala
@@ -32,6 +32,8 @@ private class Geary.Imap.Account : BaseObject {
private Gee.HashMap<FolderPath, Imap.Folder> folders = new Gee.HashMap<FolderPath, Imap.Folder>();
private Gee.List<MailboxInformation>? list_collector = null;
private Gee.List<StatusData>? status_collector = null;
+ private Gee.List<ServerData>? server_data_collector = null;
+ private Imap.MailboxSpecifier? inbox_specifier = null;
public signal void login_failed(Geary.Credentials cred);
@@ -89,7 +91,14 @@ private class Geary.Imap.Account : BaseObject {
account_session.list.connect(on_list_data);
account_session.status.connect(on_status_data);
+ account_session.server_data_received.connect(on_server_data_received);
account_session.disconnected.connect(on_disconnected);
+
+ // Learn the magic XLIST <translated Inbox name> -> Inbox mapping
+ if (account_session.capabilities.has_capability(Imap.Capabilities.XLIST))
+ yield determine_xlist_inbox(account_session, cancellable);
+ else
+ inbox_specifier = MailboxSpecifier.inbox;
} catch (Error claim_err) {
err = claim_err;
}
@@ -97,12 +106,52 @@ private class Geary.Imap.Account : BaseObject {
account_session_mutex.release(ref token);
- if (err != null)
+ if (err != null) {
+ if (account_session != null)
+ yield drop_session_async(null);
+
throw err;
+ }
return account_session;
}
+ private async void determine_xlist_inbox(ClientSession session, Cancellable? cancellable) throws Error {
+ // can't use send_command_async() directly because this is called by claim_session_async(),
+ // which is called by send_command_async()
+
+ int token = yield cmd_mutex.claim_async(cancellable);
+
+ // clear for now
+ inbox_specifier = null;
+
+ // collect server data directly for direct decoding
+ server_data_collector = new Gee.ArrayList<ServerData>();
+
+ Error? throw_err = null;
+ try {
+ Imap.StatusResponse response = yield session.send_command_async(
+ new ListCommand(MailboxSpecifier.inbox, true, null), cancellable);
+ if (response.status == Imap.Status.OK && server_data_collector.size > 0)
+ inbox_specifier = MailboxInformation.decode(server_data_collector[0], false).mailbox;
+ } catch (Error err) {
+ throw_err = err;
+ }
+
+ server_data_collector = null;
+
+ // fall back on standard name
+ if (inbox_specifier == null)
+ inbox_specifier = MailboxSpecifier.inbox;
+
+ debug("[%s] INBOX specifier: %s", to_string(), inbox_specifier.to_string());
+
+ cmd_mutex.release(ref token);
+
+ if (throw_err != null)
+ throw throw_err;
+ }
+
private async void drop_session_async(Cancellable? cancellable) {
int token;
try {
@@ -122,6 +171,7 @@ private class Geary.Imap.Account : BaseObject {
account_session.list.disconnect(on_list_data);
account_session.status.disconnect(on_status_data);
+ account_session.server_data_received.disconnect(on_server_data_received);
account_session.disconnected.disconnect(on_disconnected);
account_session = null;
@@ -144,6 +194,11 @@ private class Geary.Imap.Account : BaseObject {
status_collector.add(status_data);
}
+ private void on_server_data_received(ServerData server_data) {
+ if (server_data_collector != null)
+ server_data_collector.add(server_data);
+ }
+
private void on_disconnected() {
drop_session_async.begin(null);
}
@@ -185,13 +240,16 @@ private class Geary.Imap.Account : BaseObject {
if (mailbox_info == null)
throw_not_found(path);
+ // construct folder path for new folder, converting XLIST Inbox name to canonical INBOX
+ FolderPath folder_path = mailbox_info.get_path(inbox_specifier);
+
Imap.Folder folder;
if (!mailbox_info.attrs.is_no_select) {
StatusData status = yield fetch_status_async(path, StatusDataType.all(), cancellable);
- folder = new Imap.Folder(session_mgr, status, mailbox_info);
+ folder = new Imap.Folder(folder_path, session_mgr, status, mailbox_info);
} else {
- folder = new Imap.Folder.unselectable(session_mgr, mailbox_info);
+ folder = new Imap.Folder.unselectable(folder_path, session_mgr, mailbox_info);
}
folders.set(path, folder);
@@ -268,7 +326,8 @@ private class Geary.Imap.Account : BaseObject {
foreach (MailboxInformation mailbox_info in child_info) {
// if new mailbox is unselectable, don't bother doing a STATUS command
if (mailbox_info.attrs.is_no_select) {
- Imap.Folder folder = new Imap.Folder.unselectable(session_mgr, mailbox_info);
+ Imap.Folder folder = new Imap.Folder.unselectable(mailbox_info.get_path(inbox_specifier),
+ session_mgr, mailbox_info);
folders.set(folder.path, folder);
child_folders.add(folder);
@@ -318,12 +377,14 @@ private class Geary.Imap.Account : BaseObject {
status_results.remove(found_status);
+ FolderPath folder_path = mailbox_info.get_path(inbox_specifier);
+
// if already have an Imap.Folder for this mailbox, use that
- Imap.Folder? folder = folders.get(mailbox_info.path);
+ Imap.Folder? folder = folders.get(folder_path);
if (folder != null) {
folder.properties.update_status(found_status);
} else {
- folder = new Imap.Folder(session_mgr, found_status, mailbox_info);
+ folder = new Imap.Folder(folder_path, session_mgr, found_status, mailbox_info);
folders.set(folder.path, folder);
}
@@ -375,7 +436,8 @@ private class Geary.Imap.Account : BaseObject {
if (parent != null) {
Gee.Iterator<MailboxInformation> iter = list_results.iterator();
while (iter.next()) {
- FolderPath list_path =
iter.get().mailbox.to_folder_path(parent.get_root().default_separator);
+ FolderPath list_path = iter.get().mailbox.to_folder_path(parent.get_root().default_separator,
+ inbox_specifier);
if (list_path.equal_to(parent)) {
debug("Removing parent from LIST results: %s", list_path.to_string());
iter.remove();
@@ -387,7 +449,7 @@ private class Geary.Imap.Account : BaseObject {
// TODO: remove any MailboxInformation for this parent that is not found (i.e. has been
// removed on the server)
foreach (MailboxInformation mailbox_info in list_results)
- path_to_mailbox.set(mailbox_info.path, mailbox_info);
+ path_to_mailbox.set(mailbox_info.get_path(inbox_specifier), mailbox_info);
return (list_results.size > 0) ? list_results : null;
}
diff --git a/src/engine/imap/api/imap-folder.vala b/src/engine/imap/api/imap-folder.vala
index c3fe6d4..e853ae9 100644
--- a/src/engine/imap/api/imap-folder.vala
+++ b/src/engine/imap/api/imap-folder.vala
@@ -71,20 +71,20 @@ private class Geary.Imap.Folder : BaseObject {
*/
public signal void disconnected(ClientSession.DisconnectReason reason);
- internal Folder(ClientSessionManager session_mgr, StatusData status, MailboxInformation info) {
+ internal Folder(FolderPath path, ClientSessionManager session_mgr, StatusData status, MailboxInformation
info) {
assert(status.mailbox.equal_to(info.mailbox));
this.session_mgr = session_mgr;
this.info = info;
- path = info.path;
+ this.path = path;
properties = new Imap.FolderProperties.status(status, info.attrs);
}
- internal Folder.unselectable(ClientSessionManager session_mgr, MailboxInformation info) {
+ internal Folder.unselectable(FolderPath path, ClientSessionManager session_mgr, MailboxInformation info)
{
this.session_mgr = session_mgr;
this.info = info;
- path = info.path;
+ this.path = path;
properties = new Imap.FolderProperties(0, 0, 0, null, null, info.attrs);
}
diff --git a/src/engine/imap/message/imap-mailbox-specifier.vala
b/src/engine/imap/message/imap-mailbox-specifier.vala
index d6a864a..333d101 100644
--- a/src/engine/imap/message/imap-mailbox-specifier.vala
+++ b/src/engine/imap/message/imap-mailbox-specifier.vala
@@ -133,9 +133,26 @@ public class Geary.Imap.MailboxSpecifier : BaseObject, Gee.Hashable<MailboxSpeci
return path;
}
- public FolderPath to_folder_path(string? delim = null) {
+ /**
+ * Converts the { link MailboxSpecifier} into a { link FolderPath}.
+ *
+ * If the inbox_specifier is supplied, if the root element matches it, the canonical Inbox
+ * name is used in its place. This is useful for XLIST where that command returns a translated
+ * name but the standard IMAP name ("INBOX") must be used in addressing its children.
+ */
+ public FolderPath to_folder_path(string? delim, MailboxSpecifier? inbox_specifier) {
+ // convert path to list of elements
Gee.List<string> list = to_list(delim);
- FolderPath path = new Imap.FolderRoot(list[0], delim);
+
+ // if root element is same as supplied inbox specifier, use canonical inbox name, otherwise
+ // keep
+ FolderPath path;
+ if (inbox_specifier != null && list[0] == inbox_specifier.name)
+ path = new Imap.FolderRoot(CANONICAL_INBOX_NAME, delim);
+ else
+ path = new Imap.FolderRoot(list[0], delim);
+
+ // walk down rest of elements adding as we go
for (int ctr = 1; ctr < list.size; ctr++)
path = path.get_child(list[ctr]);
diff --git a/src/engine/imap/response/imap-mailbox-information.vala
b/src/engine/imap/response/imap-mailbox-information.vala
index f29294e..741a958 100644
--- a/src/engine/imap/response/imap-mailbox-information.vala
+++ b/src/engine/imap/response/imap-mailbox-information.vala
@@ -30,30 +30,25 @@ public class Geary.Imap.MailboxInformation : Object {
*/
public MailboxAttributes attrs { get; private set; }
- /**
- * The { link Geary.FolderPath} for the mailbox.
- *
- * This is constructed from the supplied { link mailbox} and { link delim} returned from the
- * server.
- */
- public Geary.FolderPath path { get; private set; }
-
public MailboxInformation(MailboxSpecifier mailbox, string? delim, MailboxAttributes attrs) {
this.mailbox = mailbox;
this.delim = delim;
this.attrs = attrs;
- path = mailbox.to_folder_path(delim);
}
/**
* Decodes { link ServerData} into a MailboxInformation representation.
*
+ * If canonical_inbox is true, the { link MailboxAttributes} are searched for the \Inbox flag.
+ * If found, { link MailboxSpecifier.CANONICAL_INBOX_NAME} is used rather than the one returned
+ * by the server.
+ *
* The ServerData must be the response to a LIST or XLIST command.
*
* @see ListCommand
* @see ServerData.get_list
*/
- public static MailboxInformation decode(ServerData server_data) throws ImapError {
+ public static MailboxInformation decode(ServerData server_data, bool canonical_inbox) throws ImapError {
StringParameter cmd = server_data.get_as_string(1);
if (!cmd.equals_ci(ListCommand.NAME) && !cmd.equals_ci(ListCommand.XLIST_NAME))
throw new ImapError.PARSE_ERROR("Not LIST or XLIST data: %s", server_data.to_string());
@@ -80,7 +75,7 @@ public class Geary.Imap.MailboxInformation : Object {
server_data.get_as_string(4));
// Set \Inbox to standard path
- if (Geary.Imap.MailboxAttribute.SPECIAL_FOLDER_INBOX in attributes) {
+ if (canonical_inbox && Geary.Imap.MailboxAttribute.SPECIAL_FOLDER_INBOX in attributes) {
return new MailboxInformation(MailboxSpecifier.inbox,
(delim != null) ? delim.nullable_value : null, attributes);
} else {
@@ -88,5 +83,16 @@ public class Geary.Imap.MailboxInformation : Object {
(delim != null) ? delim.nullable_value : null, attributes);
}
}
+
+ /**
+ * The { link Geary.FolderPath} for the { link mailbox}.
+ *
+ * This is constructed from the supplied { link mailbox} and { link delim} returned from the
+ * server. If the mailbox is the same as the supplied inbox_specifier, a canonical name for
+ * the Inbox is returned.
+ */
+ public Geary.FolderPath get_path(MailboxSpecifier? inbox_specifier) {
+ return mailbox.to_folder_path(delim, inbox_specifier);
+ }
}
diff --git a/src/engine/imap/response/imap-server-data.vala b/src/engine/imap/response/imap-server-data.vala
index ecb5060..81c3ef0 100644
--- a/src/engine/imap/response/imap-server-data.vala
+++ b/src/engine/imap/response/imap-server-data.vala
@@ -127,7 +127,7 @@ public class Geary.Imap.ServerData : ServerResponse {
if (server_data_type != ServerDataType.LIST && server_data_type != ServerDataType.XLIST)
throw new ImapError.INVALID("Not LIST/XLIST data: %s", to_string());
- return MailboxInformation.decode(this);
+ return MailboxInformation.decode(this, true);
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]