diff --git a/po/POTFILES.in b/po/POTFILES.in index 271ab79c..eb636b79 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -191,6 +191,7 @@ src/engine/imap/command/imap-list-return-parameter.vala src/engine/imap/command/imap-login-command.vala src/engine/imap/command/imap-logout-command.vala src/engine/imap/command/imap-message-set.vala +src/engine/imap/command/imap-namespace-command.vala src/engine/imap/command/imap-noop-command.vala src/engine/imap/command/imap-search-command.vala src/engine/imap/command/imap-search-criteria.vala @@ -275,6 +276,7 @@ src/engine/imap/message/imap-mailbox-specifier.vala src/engine/imap/message/imap-message-data.vala src/engine/imap/message/imap-message-flag.vala src/engine/imap/message/imap-message-flags.vala +src/engine/imap/message/imap-namespace.vala src/engine/imap/message/imap-sequence-number.vala src/engine/imap/message/imap-status-data-type.vala src/engine/imap/message/imap-tag.vala @@ -297,6 +299,7 @@ src/engine/imap/response/imap-fetched-data.vala src/engine/imap/response/imap-mailbox-attribute.vala src/engine/imap/response/imap-mailbox-attributes.vala src/engine/imap/response/imap-mailbox-information.vala +src/engine/imap/response/imap-namespace-response.vala src/engine/imap/response/imap-response-code-type.vala src/engine/imap/response/imap-response-code.vala src/engine/imap/response/imap-server-data-type.vala diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 218f5255..ca97d352 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -108,6 +108,7 @@ engine/imap/command/imap-list-return-parameter.vala engine/imap/command/imap-login-command.vala engine/imap/command/imap-logout-command.vala engine/imap/command/imap-message-set.vala +engine/imap/command/imap-namespace-command.vala engine/imap/command/imap-noop-command.vala engine/imap/command/imap-search-command.vala engine/imap/command/imap-search-criteria.vala @@ -128,6 +129,7 @@ engine/imap/message/imap-mailbox-parameter.vala engine/imap/message/imap-message-data.vala engine/imap/message/imap-message-flag.vala engine/imap/message/imap-message-flags.vala +engine/imap/message/imap-namespace.vala engine/imap/message/imap-sequence-number.vala engine/imap/message/imap-status-data-type.vala engine/imap/message/imap-tag.vala @@ -150,6 +152,7 @@ engine/imap/response/imap-fetched-data.vala engine/imap/response/imap-mailbox-attribute.vala engine/imap/response/imap-mailbox-attributes.vala engine/imap/response/imap-mailbox-information.vala +engine/imap/response/imap-namespace-response.vala engine/imap/response/imap-response-code.vala engine/imap/response/imap-response-code-type.vala engine/imap/response/imap-server-data.vala diff --git a/src/console/main.vala b/src/console/main.vala index 1523d66c..179dfd4b 100644 --- a/src/console/main.vala +++ b/src/console/main.vala @@ -88,6 +88,7 @@ class ImapConsole : Gtk.Window { "logout", "id", "bye", + "namespace", "list", "xlist", "examine", @@ -166,7 +167,11 @@ class ImapConsole : Gtk.Window { case "id": id(cmd, args); break; - + + case "namespace": + namespace(cmd, args); + break; + case "list": case "xlist": list(cmd, args); @@ -433,7 +438,23 @@ class ImapConsole : Gtk.Window { exception(err); } } - + + private void namespace(string cmd, string[] args) throws Error { + check_connected(cmd, args, 0, null); + + status("Retrieving NAMESPACE..."); + cx.send_async.begin(new Geary.Imap.NamespaceCommand(), null, on_namespace); + } + + private void on_namespace(Object? source, AsyncResult result) { + try { + cx.send_async.end(result); + status("Retrieved NAMESPACE"); + } catch (Error err) { + exception(err); + } + } + private void list(string cmd, string[] args) throws Error { check_connected(cmd, args, 2, " "); diff --git a/src/engine/imap/command/imap-namespace-command.vala b/src/engine/imap/command/imap-namespace-command.vala new file mode 100644 index 00000000..2402d464 --- /dev/null +++ b/src/engine/imap/command/imap-namespace-command.vala @@ -0,0 +1,24 @@ +/* + * Copyright 2016 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. + */ + +/** + * The RFC 2342 NAMESPACE command. + * + * Determines the mailbox name prefix and hierarchy delimiter for the + * personal, other user's and public namespaces. + * + * @see [[https://tools.ietf.org/html/rfc2342]] + */ +public class Geary.Imap.NamespaceCommand : Command { + + public const string NAME = "NAMESPACE"; + + public NamespaceCommand() { + base(NAME); + } + +} diff --git a/src/engine/imap/message/imap-namespace.vala b/src/engine/imap/message/imap-namespace.vala new file mode 100644 index 00000000..060ab2cb --- /dev/null +++ b/src/engine/imap/message/imap-namespace.vala @@ -0,0 +1,29 @@ +/* + * Copyright 2016 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. + */ + +/** + * Namespace component in a response for a NAMESPACE command. + * + * @see Geary.Imap.NamespaceCommand + */ +public class Geary.Imap.Namespace : BaseObject { + + + public string prefix { get; private set; } + public string? delim { get; private set; } + + + public Namespace(string prefix, string? delim) { + this.prefix = prefix; + this.delim = delim; + } + + public string to_string() { + return "(%s,%s)".printf(this.prefix, this.delim ?? "NIL"); + } + +} diff --git a/src/engine/imap/response/imap-capabilities.vala b/src/engine/imap/response/imap-capabilities.vala index 3d3eef82..67a3dfb3 100644 --- a/src/engine/imap/response/imap-capabilities.vala +++ b/src/engine/imap/response/imap-capabilities.vala @@ -12,7 +12,8 @@ public class Geary.Imap.Capabilities : Geary.GenericCapabilities { public const string DEFLATE_SETTING = "DEFLATE"; public const string UIDPLUS = "UIDPLUS"; public const string SPECIAL_USE = "SPECIAL-USE"; - + public const string NAMESPACE = "NAMESPACE"; + public const string NAME_SEPARATOR = "="; public const string? VALUE_SEPARATOR = null; diff --git a/src/engine/imap/response/imap-namespace-response.vala b/src/engine/imap/response/imap-namespace-response.vala new file mode 100644 index 00000000..c6abb9dd --- /dev/null +++ b/src/engine/imap/response/imap-namespace-response.vala @@ -0,0 +1,83 @@ +/* + * Copyright 2016 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. + */ + +/** + * Response for a NAMESPACE command. + * + * @see Geary.Imap.NamespaceCommand + */ +public class Geary.Imap.NamespaceResponse : BaseObject { + + + public Namespace[]? personal { get; private set; default = null; } + public Namespace[]? user { get; private set; default = null; } + public Namespace[]? shared { get; private set; default = null; } + + + /** + * Decodes {@link ServerData} into a NamespaceResponse representation. + * + * The ServerData must be the response to a NAMESPACE command. + * + * @see ServerData.get_list + */ + public static NamespaceResponse decode(ServerData server_data) throws ImapError { + StringParameter cmd = server_data.get_as_string(1); + if (!cmd.equals_ci(NamespaceCommand.NAME)) + throw new ImapError.PARSE_ERROR( + "Not NAMESPACE data: %s", server_data.to_string() + ); + + if (server_data.size <= 2) { + throw new ImapError.PARSE_ERROR( + "No NAMESPACEs privided: %s", server_data.to_string() + ); + } + + ListParameter? personal = server_data.get_as_nullable_list(2); + ListParameter? user = null; + if (server_data.size >= 4) { + user = server_data.get_as_nullable_list(3); + } + ListParameter? shared = null; + if (server_data.size >= 5) { + shared = server_data.get_as_nullable_list(4); + } + + return new NamespaceResponse( + parse_namespaces(personal), + user != null ? parse_namespaces(user) : null, + shared != null ? parse_namespaces(shared) : null + ); + } + + private static Namespace[]? parse_namespaces(ListParameter? list) throws ImapError { + Namespace[] nss = new Namespace[list.size]; + for (int i = 0; i < list.size; i++) { + nss[i] = parse_namespace(list.get_as_list(i)); + } + return nss; + } + + private static Namespace? parse_namespace(ListParameter? list) throws ImapError { + Namespace? ns = null; + if (list != null && list.size >= 1) { + ns = new Namespace( + list.get_as_string(0).ascii, + list.get_as_nullable_string(1).nullable_ascii + ); + } + return ns; + } + + public NamespaceResponse(Namespace[]? personal, Namespace[]? user, Namespace[]? shared) { + this.personal = personal; + this.user = user; + this.shared = shared; + } + +} diff --git a/src/engine/imap/response/imap-server-data-type.vala b/src/engine/imap/response/imap-server-data-type.vala index f2ff5f5f..f7246f72 100644 --- a/src/engine/imap/response/imap-server-data-type.vala +++ b/src/engine/imap/response/imap-server-data-type.vala @@ -18,6 +18,7 @@ public enum Geary.Imap.ServerDataType { FLAGS, LIST, LSUB, + NAMESPACE, RECENT, SEARCH, STATUS, @@ -45,7 +46,10 @@ public enum Geary.Imap.ServerDataType { case LSUB: return "lsub"; - + + case NAMESPACE: + return "namespace"; + case RECENT: return "recent"; @@ -91,7 +95,10 @@ public enum Geary.Imap.ServerDataType { case "lsub": return LSUB; - + + case "namespace": + return NAMESPACE; + case "recent": return RECENT; @@ -137,6 +144,9 @@ public enum Geary.Imap.ServerDataType { case "lsub": return LSUB; + case "namespace": + return NAMESPACE; + case "search": return SEARCH; diff --git a/src/engine/imap/response/imap-server-data.vala b/src/engine/imap/response/imap-server-data.vala index 7c1e2c32..e2e91aa4 100644 --- a/src/engine/imap/response/imap-server-data.vala +++ b/src/engine/imap/response/imap-server-data.vala @@ -129,7 +129,19 @@ public class Geary.Imap.ServerData : ServerResponse { return MailboxInformation.decode(this, true); } - + + /** + * Parses the {@link ServerData} into {@link MailboxInformation}, if possible. + * + * @throws ImapError.INVALID if not a NAMESPACE response. + */ + public NamespaceResponse get_namespace() throws ImapError { + if (server_data_type != ServerDataType.NAMESPACE) + throw new ImapError.INVALID("Not NAMESPACE data: %s", to_string()); + + return NamespaceResponse.decode(this); + } + /** * Parses the {@link ServerData} into a {@link ServerDataType.RECENT} value, if possible. *