WIP
This commit is contained in:
parent
2d7c14cc0d
commit
0ab03c0a28
18 changed files with 160 additions and 132 deletions
|
|
@ -216,7 +216,7 @@ public class Geary.Engine : BaseObject {
|
|||
return error_code;
|
||||
|
||||
// validate IMAP, which requires logging in and establishing an AUTHORIZED cx state
|
||||
Geary.Imap.ClientSession? imap_session = new Imap.ClientSession(account.get_imap_endpoint(), true);
|
||||
Geary.Imap.ClientSession? imap_session = new Imap.ClientSession(account.get_imap_endpoint());
|
||||
try {
|
||||
yield imap_session.connect_async(cancellable);
|
||||
} catch (Error err) {
|
||||
|
|
|
|||
|
|
@ -96,12 +96,12 @@ private class Geary.ImapDB.Account : BaseObject {
|
|||
throws Error {
|
||||
check_open();
|
||||
|
||||
Geary.Imap.FolderProperties? properties = imap_folder.get_properties();
|
||||
Geary.Imap.FolderProperties? properties = imap_folder.properties;
|
||||
|
||||
// properties *must* be available to perform a clone
|
||||
assert(properties != null);
|
||||
|
||||
Geary.FolderPath path = imap_folder.get_path();
|
||||
Geary.FolderPath path = imap_folder.path;
|
||||
|
||||
yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => {
|
||||
// get the parent of this folder, creating parents if necessary ... ok if this fails,
|
||||
|
|
@ -137,8 +137,8 @@ private class Geary.ImapDB.Account : BaseObject {
|
|||
throws Error {
|
||||
check_open();
|
||||
|
||||
Geary.Imap.FolderProperties properties = imap_folder.get_properties();
|
||||
Geary.FolderPath path = imap_folder.get_path();
|
||||
Geary.Imap.FolderProperties properties = imap_folder.properties;
|
||||
Geary.FolderPath path = imap_folder.path;
|
||||
|
||||
yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => {
|
||||
int64 parent_id;
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
|
|||
Gee.Collection<Geary.Folder> engine_folders, Cancellable? cancellable) {
|
||||
Gee.Collection<Geary.Imap.Folder> remote_folders;
|
||||
try {
|
||||
remote_folders = yield remote.list_folders_async(parent, cancellable);
|
||||
remote_folders = yield remote.list_mailboxes_async(parent, cancellable);
|
||||
} catch (Error remote_error) {
|
||||
debug("Unable to retrieve folder list from server: %s", remote_error.message);
|
||||
|
||||
|
|
@ -321,20 +321,20 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
|
|||
Hashable.hash_func, Equalable.equal_func);
|
||||
foreach (Imap.Folder remote_folder in remote_folders) {
|
||||
// only worry about alterations if the remote is openable
|
||||
if (remote_folder.get_properties().is_openable.is_possible()) {
|
||||
if (remote_folder.properties.is_openable.is_possible()) {
|
||||
ImapDB.Folder? local_folder = null;
|
||||
try {
|
||||
local_folder = yield local.fetch_folder_async(remote_folder.get_path(), cancellable);
|
||||
local_folder = yield local.fetch_folder_async(remote_folder.path, cancellable);
|
||||
} catch (Error err) {
|
||||
if (!(err is EngineError.NOT_FOUND)) {
|
||||
debug("Unable to fetch local folder for remote %s: %s", remote_folder.get_path().to_string(),
|
||||
debug("Unable to fetch local folder for remote %s: %s", remote_folder.path.to_string(),
|
||||
err.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (local_folder != null) {
|
||||
if (remote_folder.get_properties().have_contents_changed(local_folder.get_properties()).is_possible())
|
||||
altered_paths.add(remote_folder.get_path());
|
||||
if (remote_folder.properties.have_contents_changed(local_folder.get_properties()).is_possible())
|
||||
altered_paths.add(remote_folder.path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -356,23 +356,23 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
|
|||
Gee.Set<Geary.FolderPath> remote_paths = new Gee.HashSet<Geary.FolderPath>(
|
||||
Geary.Hashable.hash_func, Geary.Equalable.equal_func);
|
||||
foreach (Geary.Imap.Folder remote_folder in remote_folders) {
|
||||
remote_paths.add(remote_folder.get_path());
|
||||
remote_paths.add(remote_folder.path);
|
||||
|
||||
// use this iteration to add discovered properties to map
|
||||
properties_map.set(remote_folder.get_path(), remote_folder.get_properties());
|
||||
properties_map.set(remote_folder.path, remote_folder.properties);
|
||||
|
||||
// also use this iteration to set the local folder's special type
|
||||
// (but only promote, not demote, since getting the special folder type via its
|
||||
// properties relies on the optional XLIST extension)
|
||||
GenericFolder? local_folder = existing_folders.get(remote_folder.get_path());
|
||||
GenericFolder? local_folder = existing_folders.get(remote_folder.path);
|
||||
if (local_folder != null && local_folder.get_special_folder_type() == SpecialFolderType.NONE)
|
||||
local_folder.set_special_folder_type(remote_folder.get_properties().attrs.get_special_folder_type());
|
||||
local_folder.set_special_folder_type(remote_folder.properties.attrs.get_special_folder_type());
|
||||
}
|
||||
|
||||
// If path in remote but not local, need to add it
|
||||
Gee.List<Geary.Imap.Folder> to_add = new Gee.ArrayList<Geary.Imap.Folder>();
|
||||
foreach (Geary.Imap.Folder folder in remote_folders) {
|
||||
if (!local_paths.contains(folder.get_path()))
|
||||
if (!local_paths.contains(folder.path))
|
||||
to_add.add(folder);
|
||||
}
|
||||
|
||||
|
|
@ -396,7 +396,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
|
|||
try {
|
||||
yield local.clone_folder_async(folder, cancellable);
|
||||
} catch (Error err) {
|
||||
debug("Unable to add/remove folder %s: %s", folder.get_path().to_string(),
|
||||
debug("Unable to add/remove folder %s: %s", folder.path.to_string(),
|
||||
err.message);
|
||||
}
|
||||
}
|
||||
|
|
@ -411,7 +411,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
|
|||
foreach (Geary.Imap.Folder remote_folder in to_add) {
|
||||
try {
|
||||
folders_to_build.add((ImapDB.Folder) yield local.fetch_folder_async(
|
||||
remote_folder.get_path(), cancellable));
|
||||
remote_folder.path, cancellable));
|
||||
} catch (Error convert_err) {
|
||||
// This isn't fatal, but irksome ... in the future, when local folders are
|
||||
// removed, it's possible for one to disappear between cloning it and fetching
|
||||
|
|
@ -449,11 +449,11 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
|
|||
|
||||
// enumerate children of each remote folder
|
||||
foreach (Imap.Folder remote_folder in remote_folders) {
|
||||
if (remote_folder.get_properties().has_children.is_possible()) {
|
||||
if (remote_folder.properties.has_children.is_possible()) {
|
||||
try {
|
||||
yield enumerate_folders_async(remote_folder.get_path(), cancellable);
|
||||
yield enumerate_folders_async(remote_folder.path, cancellable);
|
||||
} catch (Error err) {
|
||||
debug("Unable to enumerate children of %s: %s", remote_folder.get_path().to_string(),
|
||||
debug("Unable to enumerate children of %s: %s", remote_folder.path.to_string(),
|
||||
err.message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
// - From open remote folder
|
||||
// - Fetch from local store
|
||||
if (remote_folder != null && get_open_state() == OpenState.BOTH)
|
||||
return remote_folder.get_properties();
|
||||
return remote_folder.properties;
|
||||
|
||||
return local_folder.get_properties();
|
||||
}
|
||||
|
|
@ -106,7 +106,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
debug("normalize_folders %s", to_string());
|
||||
|
||||
Geary.Imap.FolderProperties local_properties = local_folder.get_properties();
|
||||
Geary.Imap.FolderProperties remote_properties = remote_folder.get_properties();
|
||||
Geary.Imap.FolderProperties remote_properties = remote_folder.properties;
|
||||
|
||||
// and both must have their next UID's (it's possible they don't if it's a non-selectable
|
||||
// folder)
|
||||
|
|
@ -454,12 +454,14 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
yield local.update_folder_async(folder, cancellable);
|
||||
|
||||
// signals
|
||||
/*
|
||||
folder.messages_appended.connect(on_remote_messages_appended);
|
||||
folder.message_at_removed.connect(on_remote_message_at_removed);
|
||||
folder.disconnected.connect(on_remote_disconnected);
|
||||
*/
|
||||
|
||||
// state
|
||||
remote_count = folder.get_email_count();
|
||||
remote_count = folder.properties.email_total;
|
||||
|
||||
// all set; bless the remote folder as opened
|
||||
remote_folder = folder;
|
||||
|
|
@ -547,9 +549,11 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
}
|
||||
|
||||
if (closing_remote_folder != null) {
|
||||
/*
|
||||
closing_remote_folder.messages_appended.disconnect(on_remote_messages_appended);
|
||||
closing_remote_folder.message_at_removed.disconnect(on_remote_message_at_removed);
|
||||
closing_remote_folder.disconnected.disconnect(on_remote_disconnected);
|
||||
*/
|
||||
|
||||
// to avoid keeping the caller waiting while the remote end closes, close it in the
|
||||
// background
|
||||
|
|
|
|||
|
|
@ -10,6 +10,13 @@ private class Geary.Imap.Account : BaseObject {
|
|||
public const string INBOX_NAME = "INBOX";
|
||||
public const string ASSUMED_SEPARATOR = "/";
|
||||
|
||||
private static MailboxParameter? _GLOB_PARAMETER = null;
|
||||
private static MailboxParameter GLOB_PARAMETER {
|
||||
get {
|
||||
return (_GLOB_PARAMETER != null) ? _GLOB_PARAMETER : _GLOB_PARAMETER = new MailboxParameter("%");
|
||||
}
|
||||
}
|
||||
|
||||
public bool is_open { get; private set; default = false; }
|
||||
|
||||
private string name;
|
||||
|
|
@ -18,7 +25,7 @@ private class Geary.Imap.Account : BaseObject {
|
|||
private Gee.HashMap<string, string?> delims = new Gee.HashMap<string, string?>();
|
||||
private ClientSession? account_session = null;
|
||||
private NonblockingMutex cmd_mutex = new NonblockingMutex();
|
||||
private Gee.ArrayList<MailboxInformation> mailbox_collector = new Gee.ArrayList<MailboxInformation>();
|
||||
private Gee.ArrayList<MailboxInformation> list_collector = new Gee.ArrayList<MailboxInformation>();
|
||||
private Gee.ArrayList<StatusData> status_collector = new Gee.ArrayList<StatusData>();
|
||||
|
||||
public signal void email_sent(Geary.RFC822.Message rfc822);
|
||||
|
|
@ -33,6 +40,11 @@ private class Geary.Imap.Account : BaseObject {
|
|||
session_mgr.login_failed.connect(on_login_failed);
|
||||
}
|
||||
|
||||
private void check_open() throws Error {
|
||||
if (!is_open)
|
||||
throw new EngineError.OPEN_REQUIRED("Imap.Account not open");
|
||||
}
|
||||
|
||||
public async void open_async(Cancellable? cancellable = null) throws Error {
|
||||
if (is_open)
|
||||
throw new EngineError.ALREADY_OPEN("Imap.Account already open");
|
||||
|
|
@ -71,13 +83,14 @@ private class Geary.Imap.Account : BaseObject {
|
|||
|
||||
Geary.FolderPath? processed = process_path(path, null, path.get_root().default_separator);
|
||||
if (processed == null)
|
||||
throw new ImapError.INVALID_PATH("Invalid path %s", path.to_string());
|
||||
throw new ImapError.INVALID("Invalid path %s", path.to_string());
|
||||
|
||||
bool can_xlist = account_session.capabilities.has_capability(Capabilities.XLIST);
|
||||
|
||||
Gee.List<MailboxInformation> list_results = new Gee.ArrayList<MailboxInformation>();
|
||||
CompletionStatusResponse response = yield send_command_async(
|
||||
new ListCommand(processed.get_fullpath(), can_xlist), list_results, null, cancellable);
|
||||
new ListCommand(new Imap.MailboxParameter(processed.get_fullpath()), can_xlist), list_results,
|
||||
null, cancellable);
|
||||
|
||||
if (response.status != Status.OK) {
|
||||
throw new ImapError.SERVER_ERROR("Server reports LIST error for path %s: %s", path.to_string(),
|
||||
|
|
@ -89,7 +102,7 @@ private class Geary.Imap.Account : BaseObject {
|
|||
list_results.size, path.get_fullpath());
|
||||
}
|
||||
|
||||
return (list_results == 1) ? list_results[0] : null;
|
||||
return (list_results.size == 1) ? list_results[0] : null;
|
||||
}
|
||||
|
||||
public async Gee.List<MailboxInformation>? list_children_command(FolderPath? parent, Cancellable? cancellable = null)
|
||||
|
|
@ -103,14 +116,14 @@ private class Geary.Imap.Account : BaseObject {
|
|||
|
||||
ListCommand cmd;
|
||||
if (processed == null) {
|
||||
cmd = new ListCommand.wildcarded("", "%", can_xlist);
|
||||
cmd = new ListCommand.wildcarded("", new MailboxParameter("%"), can_xlist);
|
||||
} else {
|
||||
string specifier = processed.get_fullpath();
|
||||
string delim = processed.get_root().default_separator;
|
||||
|
||||
specified += specifier.has_suffix(delim) ? "%" : (delim + "%");
|
||||
specifier += specifier.has_suffix(delim) ? "%" : (delim + "%");
|
||||
|
||||
cmd = new ListCommand(specifier, can_xlist);
|
||||
cmd = new ListCommand(new Imap.MailboxParameter(specifier), can_xlist);
|
||||
}
|
||||
|
||||
Gee.List<MailboxInformation> list_results = new Gee.ArrayList<MailboxInformation>();
|
||||
|
|
@ -118,7 +131,7 @@ private class Geary.Imap.Account : BaseObject {
|
|||
cancellable);
|
||||
|
||||
if (response.status != Status.OK)
|
||||
throw_not_found(path);
|
||||
throw_not_found(processed ?? parent);
|
||||
|
||||
return (list_results.size > 0) ? list_results : null;
|
||||
}
|
||||
|
|
@ -129,12 +142,12 @@ private class Geary.Imap.Account : BaseObject {
|
|||
|
||||
Geary.FolderPath? processed = process_path(path, null, path.get_root().default_separator);
|
||||
if (processed == null)
|
||||
throw new ImapError.INVALID_PATH("Invalid path %s", path.to_string());
|
||||
throw new ImapError.INVALID("Invalid path %s", path.to_string());
|
||||
|
||||
Gee.List<StatusData> status_results = new Gee.ArrayList<StatusData>();
|
||||
CompletionStatusResponse response = yield send_command_async(
|
||||
new StatusCommand(processed.get_fullpath(), StatusDataType.all()), null, status_results,
|
||||
cancellable);
|
||||
new StatusCommand(new MailboxParameter(processed.get_fullpath()), StatusDataType.all()),
|
||||
null, status_results, cancellable);
|
||||
|
||||
if (response.status != Status.OK) {
|
||||
throw new ImapError.SERVER_ERROR("Server reports STATUS error for path %s: %s", path.to_string(),
|
||||
|
|
@ -201,7 +214,7 @@ private class Geary.Imap.Account : BaseObject {
|
|||
// 3. Parent and basename supplied, verify parent is not Inbox, as IMAP does not allow it
|
||||
// to have children
|
||||
if (parent != null && !empty_basename && parent.get_root().basename.up() == INBOX_NAME)
|
||||
throw new ImapError.INVALID_PATH("Inbox may not have children");
|
||||
throw new ImapError.INVALID("Inbox may not have children");
|
||||
|
||||
// 4. Parent supplied but basename is not; if parent points to Inbox, normalize it
|
||||
if (parent != null && empty_basename && parent.basename.up() == INBOX_NAME)
|
||||
|
|
|
|||
|
|
@ -6,17 +6,18 @@
|
|||
|
||||
public class Geary.Imap.FolderProperties : Geary.FolderProperties {
|
||||
/**
|
||||
* -1 if the Folder was not opened via SELECT or EXAMINE.
|
||||
* -1 if the Folder was not opened via SELECT or EXAMINE. Updated as EXISTS server data
|
||||
* arrives.
|
||||
*/
|
||||
public int select_examine_messages { get; private set; }
|
||||
/**
|
||||
* -1 if the FolderProperties were not obtained via a STATUS command
|
||||
*/
|
||||
public int status_messages { get; private set; }
|
||||
public int unseen { get; private set; }
|
||||
public int recent { get; private set; }
|
||||
public UIDValidity? uid_validity { get; private set; }
|
||||
public UID? uid_next { get; private set; }
|
||||
public int unseen { get; internal set; }
|
||||
public int recent { get; internal set; }
|
||||
public UIDValidity? uid_validity { get; internal set; }
|
||||
public UID? uid_next { get; internal set; }
|
||||
public MailboxAttributes attrs { get; private set; }
|
||||
|
||||
// Note that unseen from SELECT/EXAMINE is the *position* of the first unseen message,
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
session.expunge.connect(on_expunge);
|
||||
session.fetch.connect(on_fetch);
|
||||
session.recent.connect(on_recent);
|
||||
session.coded_status_response.connect(on_coded_status_response);
|
||||
session.disconnected.connect(on_disconnected);
|
||||
session.coded_response_received.connect(on_coded_status_response);
|
||||
//session.disconnected.connect(on_disconnected);
|
||||
|
||||
CompletionStatusResponse response = yield session.select_examine_async(path.get_fullpath(info.delim),
|
||||
!readonly, cancellable);
|
||||
|
|
@ -74,11 +74,6 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
|
||||
// update with new information
|
||||
this.readonly = Trillian.from_boolean(readonly);
|
||||
|
||||
int old_status_messages = properties.status_messages;
|
||||
properties = new Imap.FolderProperties(mailbox.exists, mailbox.recent, properties.unseen,
|
||||
mailbox.uid_validity, mailbox.uid_next, properties.attrs);
|
||||
properties.set_status_message_count(old_status_messages, false);
|
||||
}
|
||||
|
||||
public async void close_async(Cancellable? cancellable = null) throws Error {
|
||||
|
|
@ -89,8 +84,8 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
session.expunge.disconnect(on_expunge);
|
||||
session.fetch.disconnect(on_fetch);
|
||||
session.recent.disconnect(on_recent);
|
||||
session.coded_status_response.disconnect(on_coded_status_response);
|
||||
session.disconnected.disconnect(on_disconnected);
|
||||
session.coded_response_received.disconnect(on_coded_status_response);
|
||||
//session.disconnected.disconnect(on_disconnected);
|
||||
|
||||
try {
|
||||
yield session.close_mailbox_async(cancellable);
|
||||
|
|
@ -104,7 +99,7 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
}
|
||||
|
||||
private void on_exists(int count) {
|
||||
properties.messages = count;
|
||||
properties.set_select_examine_message_count(count);
|
||||
|
||||
exists(count);
|
||||
}
|
||||
|
|
@ -124,10 +119,23 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
}
|
||||
|
||||
private void on_coded_status_response(CodedStatusResponse coded_response) {
|
||||
switch (coded_response.response_code_type) {
|
||||
case ResponseCodeType.UIDNEXT:
|
||||
properties.uid_next = coded_response.get_uid_next();
|
||||
break;
|
||||
try {
|
||||
switch (coded_response.response_code_type) {
|
||||
case ResponseCodeType.UIDNEXT:
|
||||
properties.uid_next = coded_response.get_uid_next();
|
||||
break;
|
||||
|
||||
case ResponseCodeType.UIDVALIDITY:
|
||||
properties.uid_validity = coded_response.get_uid_validity();
|
||||
break;
|
||||
|
||||
case ResponseCodeType.UNSEEN:
|
||||
properties.unseen = coded_response.get_unseen();
|
||||
break;
|
||||
}
|
||||
} catch (ImapError ierr) {
|
||||
debug("Unable to parse CodedStatusResponse %s: %s", coded_response.to_string(),
|
||||
ierr.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -150,13 +158,18 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
|
||||
public async Gee.List<Geary.Email>? list_email_async(MessageSet msg_set, Geary.Email.Field fields,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
/*
|
||||
if (mailbox == null)
|
||||
throw new EngineError.OPEN_REQUIRED("%s not opened", to_string());
|
||||
|
||||
return yield mailbox.list_set_async(msg_set, fields, cancellable);
|
||||
*/
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async void remove_email_async(MessageSet msg_set, Cancellable? cancellable = null) throws Error {
|
||||
/*
|
||||
if (mailbox == null)
|
||||
throw new EngineError.OPEN_REQUIRED("%s not opened", to_string());
|
||||
|
||||
|
|
@ -168,10 +181,12 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
// mailbox could've closed during call
|
||||
if (mailbox != null)
|
||||
yield mailbox.expunge_email_async(msg_set, cancellable);
|
||||
*/
|
||||
}
|
||||
|
||||
public async void mark_email_async(MessageSet msg_set, Geary.EmailFlags? flags_to_add,
|
||||
Geary.EmailFlags? flags_to_remove, Cancellable? cancellable = null) throws Error {
|
||||
/*
|
||||
if (mailbox == null)
|
||||
throw new EngineError.OPEN_REQUIRED("%s not opened", to_string());
|
||||
|
||||
|
|
@ -181,23 +196,28 @@ private class Geary.Imap.Folder : BaseObject {
|
|||
out msg_flags_remove);
|
||||
|
||||
yield mailbox.mark_email_async(msg_set, msg_flags_add, msg_flags_remove, cancellable);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
public async void copy_email_async(MessageSet msg_set, Geary.FolderPath destination,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
/*
|
||||
if (mailbox == null)
|
||||
throw new EngineError.OPEN_REQUIRED("%s not opened", to_string());
|
||||
|
||||
|
||||
yield mailbox.copy_email_async(msg_set, destination, cancellable);
|
||||
*/
|
||||
}
|
||||
|
||||
public async void move_email_async(MessageSet msg_set, Geary.FolderPath destination,
|
||||
Cancellable? cancellable = null) throws Error {
|
||||
/*
|
||||
if (mailbox == null)
|
||||
throw new EngineError.OPEN_REQUIRED("%s not opened", to_string());
|
||||
|
||||
|
||||
yield copy_email_async(msg_set, destination, cancellable);
|
||||
yield remove_email_async(msg_set, cancellable);
|
||||
*/
|
||||
}
|
||||
|
||||
public string to_string() {
|
||||
|
|
|
|||
|
|
@ -49,9 +49,9 @@ public class Geary.Imap.FetchedData : Object {
|
|||
|
||||
// watch for empty return values
|
||||
if (has_value)
|
||||
fetched_data.set_data(data_item, decoder.decode(list.get_required(ctr + 1)));
|
||||
fetched_data.map.set(data_item, decoder.decode(list.get_required(ctr + 1)));
|
||||
else
|
||||
fetched_data.set_data(data_item, decoder.decode(NilParameter.instance));
|
||||
fetched_data.map.set(data_item, decoder.decode(NilParameter.instance));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,17 +54,17 @@ public class Geary.Imap.MailboxInformation : Object {
|
|||
}
|
||||
|
||||
public static MailboxInformation decode(ServerData server_data) throws ImapError {
|
||||
StringParameter cmd = data.get_as_string(1);
|
||||
StringParameter cmd = server_data.get_as_string(1);
|
||||
if (!cmd.equals_ci(ListCommand.NAME) && !cmd.equals_ci(ListCommand.XLIST_NAME))
|
||||
throw ImapError.PARSE_ERROR("Not LIST or XLIST data: %s", server_data.to_string());
|
||||
throw new ImapError.PARSE_ERROR("Not LIST or XLIST data: %s", server_data.to_string());
|
||||
|
||||
ListParameter attrs = data.get_as_list(2);
|
||||
ListParameter attrs = server_data.get_as_list(2);
|
||||
Gee.Collection<MailboxAttribute> attrlist = new Gee.ArrayList<MailboxAttribute>();
|
||||
foreach (Parameter attr in attrs.get_all()) {
|
||||
StringParameter? stringp = attr as StringParameter;
|
||||
if (stringp == null) {
|
||||
debug("Bad list attribute \"%s\": Attribute not a string value",
|
||||
data.to_string());
|
||||
server_data.to_string());
|
||||
|
||||
continue;
|
||||
}
|
||||
|
|
@ -72,8 +72,8 @@ public class Geary.Imap.MailboxInformation : Object {
|
|||
attrlist.add(new MailboxAttribute(stringp.value));
|
||||
}
|
||||
|
||||
StringParameter? delim = data.get_as_nullable_string(3);
|
||||
StringParameter mailbox = data.get_as_string(4);
|
||||
StringParameter? delim = server_data.get_as_nullable_string(3);
|
||||
StringParameter mailbox = server_data.get_as_string(4);
|
||||
|
||||
// Set \Inbox to standard path
|
||||
MailboxInformation info;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ public class Geary.Imap.StatusData : Object {
|
|||
public static StatusData decode(ServerData server_data) throws ImapError {
|
||||
if (!server_data.get_as_string(1).equals_ci(StatusCommand.NAME)) {
|
||||
throw new ImapError.PARSE_ERROR("Bad STATUS command name in response \"%s\"",
|
||||
response.to_string());
|
||||
server_data.to_string());
|
||||
}
|
||||
|
||||
int messages = UNSET;
|
||||
|
|
@ -83,7 +83,7 @@ public class Geary.Imap.StatusData : Object {
|
|||
}
|
||||
} catch (ImapError ierr) {
|
||||
message("Bad value at %d/%d in STATUS response \"%s\": %s", ctr, ctr + 1,
|
||||
response.to_string(), ierr.message);
|
||||
server_data.to_string(), ierr.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ public class Geary.Imap.CodedStatusResponse : StatusResponse {
|
|||
public ResponseCodeType response_code_type { get; private set; }
|
||||
public ResponseCode response_code { get; private set; }
|
||||
|
||||
public CodedStatusResponse() {
|
||||
public CodedStatusResponse(Tag tag) {
|
||||
base (tag);
|
||||
}
|
||||
|
||||
public CodedStatusResponse.reconstitute(RootParameters root) throws ImapError {
|
||||
base.reconstitute(root);
|
||||
public CodedStatusResponse.migrate(RootParameters root) throws ImapError {
|
||||
base.migrate(root);
|
||||
|
||||
if (tag.is_tagged()) {
|
||||
throw new ImapError.PARSE_ERROR("Not a CodedStatusResponse: tagged response: %s",
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@
|
|||
*/
|
||||
|
||||
public class Geary.Imap.CompletionStatusResponse : StatusResponse {
|
||||
private CompletionStatusResponse() {
|
||||
private CompletionStatusResponse(Tag tag) {
|
||||
base (tag);
|
||||
}
|
||||
|
||||
public CompletionStatusResponse.reconstitute(RootParameters root) throws ImapError {
|
||||
base.reconstitute(root);
|
||||
public CompletionStatusResponse.migrate(RootParameters root) throws ImapError {
|
||||
base.migrate(root);
|
||||
|
||||
// check this is actually a CompletionStatusResponse
|
||||
if (!tag.is_tagged()) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
public class Geary.Imap.ServerData : ServerResponse {
|
||||
public ServerDataType server_data_type { get; private set; }
|
||||
|
||||
protected ServerData(Tag tag) {
|
||||
base (tag);
|
||||
}
|
||||
|
||||
public ServerData.migrate(RootParameters root) throws ImapError {
|
||||
base.migrate(root);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public abstract class Geary.Imap.ServerResponse : RootParameters {
|
|||
}
|
||||
|
||||
// The RootParameters are migrated and will be stripped upon exit.
|
||||
public static ServerResponse migrate_from_server(RootParameters root, out Type response_type)
|
||||
public static ServerResponse migrate_from_server(RootParameters root)
|
||||
throws ImapError {
|
||||
Tag tag = new Tag.from_parameter(root.get_as_string(0));
|
||||
if (tag.is_tagged()) {
|
||||
|
|
@ -28,30 +28,24 @@ public abstract class Geary.Imap.ServerResponse : RootParameters {
|
|||
if (statusparam != null)
|
||||
Status.decode(statusparam.value);
|
||||
|
||||
// tagged and has proper status, so it's a status response
|
||||
response_type = Type.STATUS_RESPONSE;
|
||||
|
||||
return new StatusResponse.migrate(root);
|
||||
} else if (tag.is_continuation()) {
|
||||
// nothing to decode; everything after the tag is human-readable stuff
|
||||
response_type = Type.CONTINUATION_RESPONSE;
|
||||
|
||||
return new ContinuationResponse.migrate(root);
|
||||
}
|
||||
|
||||
// All CompletionStatusResponses are StatusResponses, so check for it first
|
||||
if (CompletionStatusResponse.is_completion_status_response(root))
|
||||
return new CompletionStatusResponse.reconstitute(root);
|
||||
return new CompletionStatusResponse.migrate(root);
|
||||
|
||||
// All CodedStatusResponses are StatusResponses, so check for it first
|
||||
if (CodedStatusResponse.is_coded_status_response(root))
|
||||
return new CodedStatusResponse.reconstitute(root);
|
||||
return new CodedStatusResponse.migrate(root);
|
||||
|
||||
if (StatusResponse.is_status_response(root))
|
||||
return new StatusResponse.reconstitute(root);
|
||||
return new StatusResponse.migrate(root);
|
||||
|
||||
if (ServerData.is_server_data(root))
|
||||
return new ServerData.reconstitute(root);
|
||||
return new ServerData.migrate(root);
|
||||
|
||||
throw new ImapError.PARSE_ERROR("Unknown server response: %s", root.to_string());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
public class Geary.Imap.StatusResponse : ServerResponse {
|
||||
public Status status { get; private set; }
|
||||
|
||||
public StatusResponse() {
|
||||
public StatusResponse(Tag tag) {
|
||||
base (tag);
|
||||
}
|
||||
|
||||
public StatusResponse.migrate(RootParameters root) throws ImapError {
|
||||
|
|
|
|||
|
|
@ -440,8 +440,9 @@ public class Geary.Imap.ClientConnection : BaseObject {
|
|||
}
|
||||
|
||||
private void on_parameters_ready(RootParameters root) {
|
||||
ServerResponse response;
|
||||
try {
|
||||
ServerResponse response = ServerResponse.from_server(root);
|
||||
response = ServerResponse.migrate_from_server(root);
|
||||
} catch (ImapError err) {
|
||||
received_bad_response(root, err);
|
||||
|
||||
|
|
@ -469,7 +470,7 @@ public class Geary.Imap.ClientConnection : BaseObject {
|
|||
return;
|
||||
}
|
||||
|
||||
error("[%s] Unknown ServerResponse of type %s received: %s:", to_string(), response.type().name(),
|
||||
error("[%s] Unknown ServerResponse of type %s received: %s:", to_string(), response.get_type().name(),
|
||||
response.to_string());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,31 +53,13 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
|
|||
|
||||
public ClientSessionManager(AccountInformation account_information) {
|
||||
this.account_information = account_information;
|
||||
}
|
||||
|
||||
~ClientSessionManager() {
|
||||
if (is_opened)
|
||||
warning("Destroying opened ClientSessionManager");
|
||||
}
|
||||
|
||||
public async void open_async() throws Error {
|
||||
if (is_opened)
|
||||
throw ImapError.INVALID("ClientSessionManager is already open");
|
||||
|
||||
is_opened = true;
|
||||
|
||||
account_information.notify["imap-credentials"].connect(on_imap_credentials_notified);
|
||||
}
|
||||
|
||||
public async void close_async() throws Error {
|
||||
if (!is_opened)
|
||||
return;
|
||||
|
||||
is_opened = false;
|
||||
|
||||
account_information.notify["imap-credentials"].disconnect(on_imap_credentials_notified);
|
||||
|
||||
// TODO: Tear down all connections
|
||||
~ClientSessionManager() {
|
||||
if (is_open)
|
||||
warning("Destroying opened ClientSessionManager");
|
||||
}
|
||||
|
||||
public async void open_async(Cancellable? cancellable) throws Error {
|
||||
|
|
@ -304,7 +286,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
|
|||
assert_not_reached();
|
||||
}
|
||||
|
||||
int token = yield session_mutex.claim_async(cancellable);
|
||||
int token = yield sessions_mutex.claim_async(cancellable);
|
||||
|
||||
if (!sessions.contains(session))
|
||||
debug("Attempting to release a session not owned by client session manager: %s", session.to_string());
|
||||
|
|
@ -312,7 +294,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
|
|||
if (!reserved_sessions.remove(session))
|
||||
debug("Attempting to release an unreserved session: %s", session.to_string());
|
||||
|
||||
session_mutex.release(ref token);
|
||||
sessions_mutex.release(ref token);
|
||||
}
|
||||
|
||||
private void on_disconnected(ClientSession session, ClientSession.DisconnectReason reason) {
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
* capabilities, only watch for them as they're reported. Thus, it's recommended that users
|
||||
* of ClientSession issue a CapabilityCommand (if needed) before login.
|
||||
*/
|
||||
private Capabilities capabilities { get; private set; default = new Capabilities(0); }
|
||||
public Capabilities capabilities { get; private set; default = new Capabilities(0); }
|
||||
|
||||
private Endpoint imap_endpoint;
|
||||
private Geary.State.Machine fsm;
|
||||
|
|
@ -270,7 +270,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
new Geary.State.Mapping(State.SELECTING, Event.CLOSE_MAILBOX, on_close_mailbox),
|
||||
new Geary.State.Mapping(State.SELECTING, Event.LOGOUT, on_logout),
|
||||
new Geary.State.Mapping(State.SELECTING, Event.DISCONNECT, on_disconnect),
|
||||
new Geary.State.Mapping(State.SELECTING, Event.RECV_STATUS, on_selecting_recv_status),
|
||||
new Geary.State.Mapping(State.SELECTING, Event.RECV_STATUS, on_recv_status),
|
||||
new Geary.State.Mapping(State.SELECTING, Event.RECV_COMPLETION, on_selecting_recv_completion),
|
||||
new Geary.State.Mapping(State.SELECTING, Event.SEND_ERROR, on_send_error),
|
||||
new Geary.State.Mapping(State.SELECTING, Event.RECV_ERROR, on_recv_error),
|
||||
|
|
@ -448,7 +448,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
// only use IDLE when in SELECTED or EXAMINED state
|
||||
cx.set_idle_when_quiet(false);
|
||||
|
||||
result.proceed = true;
|
||||
params.proceed = true;
|
||||
|
||||
return State.CONNECTING;
|
||||
}
|
||||
|
|
@ -487,7 +487,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
}
|
||||
}
|
||||
|
||||
private void on_connected(uint state, uint event) {
|
||||
private uint on_connected(uint state, uint event) {
|
||||
debug("[%s] Connected", to_string());
|
||||
|
||||
fsm.do_post_transition(() => { connected(); });
|
||||
|
|
@ -496,7 +496,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
return state;
|
||||
}
|
||||
|
||||
private void on_connecting_recv_status(uint state, uint event, void *user, Object? object) {
|
||||
private uint on_connecting_recv_status(uint state, uint event, void *user, Object? object) {
|
||||
StatusResponse status_response = (StatusResponse) object;
|
||||
|
||||
if (status_response.status == Status.OK)
|
||||
|
|
@ -504,7 +504,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
|
||||
debug("[%s] Connect denied: %s", to_string(), status_response.to_string());
|
||||
|
||||
fsm.do_post_transition(() => { session_denied(status_response.text); });
|
||||
fsm.do_post_transition(() => { session_denied(status_response.get_text()); });
|
||||
|
||||
return State.LOGGED_OUT;
|
||||
}
|
||||
|
|
@ -522,12 +522,13 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
LoginCommand cmd = new LoginCommand(credentials.user, credentials.pass);
|
||||
|
||||
MachineParams params = new MachineParams(cmd);
|
||||
fsm.issue(Event.LOGIN, null, param);
|
||||
fsm.issue(Event.LOGIN, null, params);
|
||||
|
||||
if (params.err != null)
|
||||
throw params.err;
|
||||
|
||||
assert(result.proceed);
|
||||
// should always proceed; only an Error could change this
|
||||
assert(params.proceed);
|
||||
|
||||
return yield command_transaction_async(cmd, cancellable);
|
||||
}
|
||||
|
|
@ -562,12 +563,12 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
yield cx.starttls_async(cancellable);
|
||||
debug("[%s] STARTTLS completed", to_string());
|
||||
} else {
|
||||
debug("[%s} STARTTLS refused: %s", to_string(), resp.status_response.to_string());
|
||||
debug("[%s} STARTTLS refused: %s", to_string(), resp.status.to_string());
|
||||
|
||||
// throw an exception and fail rather than send credentials under suspect
|
||||
// conditions
|
||||
throw new ImapError.NOT_SUPPORTED("STARTTLS refused by %s: %s", to_string(),
|
||||
resp.status_response.to_string());
|
||||
resp.status.to_string());
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -781,7 +782,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
}
|
||||
|
||||
public bool supports_idle() {
|
||||
return get_capabilities().has_capability(Capabilities.IDLE);
|
||||
return capabilities.has_capability(Capabilities.IDLE);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -862,7 +863,12 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
Cancellable? cancellable) throws Error {
|
||||
string? old_mailbox = current_mailbox;
|
||||
|
||||
Command cmd = is_select ? new SelectCommand(mailbox) : new ExamineCommand(mailbox);
|
||||
// Ternary troubles
|
||||
Command cmd;
|
||||
if (is_select)
|
||||
cmd = new SelectCommand(new MailboxParameter(mailbox));
|
||||
else
|
||||
cmd = new ExamineCommand(new MailboxParameter(mailbox));
|
||||
|
||||
MachineParams params = new MachineParams(cmd);
|
||||
fsm.issue(Event.SELECT, null, params);
|
||||
|
|
@ -1055,7 +1061,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
if (params.err != null)
|
||||
throw params.err;
|
||||
|
||||
if (result.proceed)
|
||||
if (params.proceed)
|
||||
yield cx.disconnect_async(cancellable);
|
||||
}
|
||||
|
||||
|
|
@ -1228,14 +1234,14 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
// server) in the context of send_async(), wait for it now
|
||||
if (!seen_completion_responses.has_key(cmd.tag)) {
|
||||
debug("[%s] Waiting for completion status response %s...", to_string(), cmd.to_string());
|
||||
waiting_for_completion.set(cmd.tag, new CommandCallback(issue_command_async.callback));
|
||||
waiting_for_completion.set(cmd.tag, new CommandCallback(command_transaction_async.callback));
|
||||
yield;
|
||||
}
|
||||
|
||||
// it should be seen now; if not, it's because of disconnection cancelling all the outstanding
|
||||
// requests
|
||||
CompletionStatusResponse? completion_response;
|
||||
if (!seen_completion_response.remove(cmd.tag, out completion_response)) {
|
||||
if (!seen_completion_responses.unset(cmd.tag, out completion_response)) {
|
||||
assert(cx == null);
|
||||
|
||||
throw new ImapError.NOT_CONNECTED("Not connected to %s", imap_endpoint.to_string());
|
||||
|
|
@ -1295,7 +1301,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
// update state machine before notifying subscribers, who may turn around and query ClientSession
|
||||
fsm.issue(Event.RECV_STATUS, null, coded_response, null);
|
||||
|
||||
coded_status_response_received(coded_response);
|
||||
coded_response_received(coded_response);
|
||||
}
|
||||
|
||||
private void on_received_completion_status_response(CompletionStatusResponse completion_status_response) {
|
||||
|
|
@ -1309,14 +1315,14 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
// this command to the server ... this mechanism (seen_completion_response and
|
||||
// waiting_for_completion) assures that in either case issue_command_async() returns
|
||||
// when the command is completed
|
||||
seen_completion_response.set(completion_status_response.tag, completion_status_response);
|
||||
seen_completion_responses.set(completion_status_response.tag, completion_status_response);
|
||||
|
||||
CommandCallback? cmd_cb;
|
||||
if (waiting_for_completion.remove(completion_status_response.tag, out cmd_cb))
|
||||
Idle.schedule(cmd_cb.callback);
|
||||
if (waiting_for_completion.unset(completion_status_response.tag, out cmd_cb))
|
||||
Idle.add(cmd_cb.callback);
|
||||
}
|
||||
|
||||
private void notify(ServerData server_data) throws ImapError {
|
||||
private void notify_received_data(ServerData server_data) throws ImapError {
|
||||
switch (server_data.server_data_type) {
|
||||
case ServerDataType.CAPABILITY:
|
||||
// update ClientSession capabilities before firing signal, so external signal
|
||||
|
|
@ -1372,7 +1378,7 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
|
||||
// send ServerData to upper layers for processing and storage
|
||||
try {
|
||||
notify(server_data);
|
||||
notify_received_data(server_data);
|
||||
} catch (ImapError ierr) {
|
||||
debug("[%s] Failure notifying of server data: %s %s", to_string(), server_data.to_string(),
|
||||
ierr.message);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue