Add support for creating special use mailboxes.

The IMAP CREATE-SPECIAL-USE extension allows specifying the use of a
mailbox being created. We should use it if present.

* src/engine/imap/api/imap-account.vala (Account::create_folder_async):
  Add optional SpecialFolderType param, if present use command variant
  that accepts it.

* src/engine/imap/command/imap-create-command.vala (Command): Add
  additional ctor that accepts a SpecialFolderType param. If present,
  add a USE list to the command sent.

* src/engine/imap/response/imap-capabilities.vala (Capabilities): Add
  CREATE-SPECIAL-USE to the list of known capabilities, sort the list.
This commit is contained in:
Michael James Gratton 2017-11-03 13:53:14 +11:00
parent bcca3f0332
commit 2015a72802
4 changed files with 94 additions and 25 deletions

View file

@ -716,16 +716,15 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
minimal_folder = folder_map.get(path);
} else {
debug("Creating %s to use as special folder %s", path.to_string(), special.to_string());
// TODO: ignore error due to already existing.
yield remote.create_folder_async(path, cancellable);
yield remote.create_folder_async(path, special, cancellable);
minimal_folder = (MinimalFolder) yield fetch_folder_async(path, cancellable);
}
minimal_folder.set_special_folder_type(special);
return minimal_folder;
}
public override async Geary.Folder get_required_special_folder_async(Geary.SpecialFolderType special,
Cancellable? cancellable) throws Error {
if (!(special in get_supported_special_folders())) {

View file

@ -111,19 +111,36 @@ private class Geary.Imap.Account : BaseObject {
return exists;
}
public async void create_folder_async(FolderPath path, Cancellable? cancellable)
/**
* Creates a new special folder on the remote server.
*
* The given path must be a fully-qualified path, including
* namespace prefix.
*
* If the optional special folder type is specified, and
* CREATE-SPECIAL-USE is supported by the connection, that will be
* used to specify the type of the new folder.
*/
public async void create_folder_async(FolderPath path,
Geary.SpecialFolderType? type,
Cancellable? cancellable)
throws Error {
ClientSession session = yield claim_session_async(cancellable);
MailboxSpecifier mailbox = session.get_mailbox_for_path(path);
bool can_create_special = session.capabilities.has_capability(Capabilities.CREATE_SPECIAL_USE);
CreateCommand cmd = (type != null && can_create_special)
? new CreateCommand.special_use(mailbox, type)
: new CreateCommand(mailbox);
StatusResponse response = yield send_command_async(
session,
new CreateCommand(session.get_mailbox_for_path(path)),
null, null,
cancellable
session, cmd, null, null, cancellable
);
if (response.status != Status.OK) {
throw new ImapError.SERVER_ERROR("Server reports error creating path %s: %s", path.to_string(),
response.to_string());
throw new ImapError.SERVER_ERROR(
"Server reports error creating folder %s: %s",
mailbox.to_string(), response.to_string()
);
}
}

View file

@ -1,4 +1,6 @@
/* Copyright 2016 Software Freedom Conservancy Inc.
/*
* Copyright 2017 Michael Gratton <mike@vee.net>
* Copyright 2016 Software Freedom Conservancy Inc.
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
@ -10,14 +12,60 @@
public class Geary.Imap.CreateCommand : Command {
public const string NAME = "create";
public const string USE = "use";
public MailboxSpecifier mailbox { get; private set; }
public Geary.SpecialFolderType? use { get; private set; default = null; }
private static MailboxAttribute? get_special_folder_type(Geary.SpecialFolderType type) {
switch (type) {
case Geary.SpecialFolderType.TRASH:
return MailboxAttribute.SPECIAL_FOLDER_TRASH;
case Geary.SpecialFolderType.DRAFTS:
return MailboxAttribute.SPECIAL_FOLDER_DRAFTS;
case Geary.SpecialFolderType.SENT:
return MailboxAttribute.SPECIAL_FOLDER_SENT;
case Geary.SpecialFolderType.ARCHIVE:
return MailboxAttribute.SPECIAL_FOLDER_ARCHIVE;
case Geary.SpecialFolderType.SPAM:
return MailboxAttribute.SPECIAL_FOLDER_JUNK;
case Geary.SpecialFolderType.FLAGGED:
return MailboxAttribute.SPECIAL_FOLDER_STARRED;
case Geary.SpecialFolderType.ALL_MAIL:
return MailboxAttribute.SPECIAL_FOLDER_ALL;
default:
return null;
}
}
public CreateCommand(MailboxSpecifier mailbox) {
base (NAME);
base(NAME);
this.mailbox = mailbox;
add(mailbox.to_parameter());
}
public CreateCommand.special_use(MailboxSpecifier mailbox, Geary.SpecialFolderType use) {
this(mailbox);
MailboxAttribute? attr = get_special_folder_type(use);
if (attr != null) {
ListParameter use_types = new ListParameter();
use_types.add(new AtomParameter(attr.to_string()));
ListParameter use_param = new ListParameter();
use_param.add(new AtomParameter(USE));
use_param.add(use_types);
add(use_param);
}
}
}

View file

@ -5,20 +5,25 @@
*/
public class Geary.Imap.Capabilities : Geary.GenericCapabilities {
public const string IDLE = "IDLE";
public const string STARTTLS = "STARTTLS";
public const string XLIST = "XLIST";
public const string CREATE_SPECIAL_USE = "CREATE-SPECIAL-USE";
public const string COMPRESS = "COMPRESS";
public const string DEFLATE_SETTING = "DEFLATE";
public const string UIDPLUS = "UIDPLUS";
public const string SPECIAL_USE = "SPECIAL-USE";
public const string IDLE = "IDLE";
public const string NAMESPACE = "NAMESPACE";
public const string SPECIAL_USE = "SPECIAL-USE";
public const string STARTTLS = "STARTTLS";
public const string UIDPLUS = "UIDPLUS";
public const string XLIST = "XLIST";
public const string NAME_SEPARATOR = "=";
public const string? VALUE_SEPARATOR = null;
public int revision { get; private set; }
/**
* Creates an empty set of capabilities. revision represents the different variations of
* capabilities that an IMAP session might offer (i.e. changes after login or STARTTLS, for
@ -26,7 +31,7 @@ public class Geary.Imap.Capabilities : Geary.GenericCapabilities {
*/
public Capabilities(int revision) {
base (NAME_SEPARATOR, VALUE_SEPARATOR);
this.revision = revision;
}