2017-11-23 16:47:19 +11:00
|
|
|
/*
|
|
|
|
|
* Copyright 2016 Software Freedom Conservancy Inc.
|
Convert Geary.FolderRoot to be an actual root, not just a top-level
Instead of each top-level IMAP folder being a FolderRoot object, then
children of that being FolderPath objects, this makes FolderRoot an
"empty" FolderPath, so that both top-level and descendant folders are
plain FolderPath objects. Aside from being more technically correct,
this means that empty namespace roots can now be used interchangably
with non-empty namespace roots (addressing issue #181), and custom
folder implementations no longer need to provide their own trivial,
custom FolderRoot.
To support this, a notion of an IMAP root and a local root have been
added from which all remote and local folder paths are now derived,
existing places that assume top-level == root have been fixed, and
unit tests have been added.
2019-01-14 12:10:48 +11:00
|
|
|
* Copyright 2017-2019 Michael Gratton <mike@vee.net>.
|
2011-06-16 16:27:08 -07:00
|
|
|
*
|
|
|
|
|
* This software is licensed under the GNU Lesser General Public License
|
2013-04-12 12:31:58 -07:00
|
|
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
2011-06-16 16:27:08 -07:00
|
|
|
*/
|
|
|
|
|
|
2015-01-09 17:50:19 -08:00
|
|
|
private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
|
2016-06-05 18:36:16 +10:00
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
|
|
|
|
|
/** Default IMAP session pool size. */
|
|
|
|
|
private const int IMAP_MIN_POOL_SIZE = 2;
|
|
|
|
|
|
2017-11-28 10:24:46 +11:00
|
|
|
// This is high since it's an expensive operation, and we'll go
|
|
|
|
|
// looking changes caused by local operations as they happen, so
|
|
|
|
|
// we don't need to double check.
|
|
|
|
|
private const int REFRESH_FOLDER_LIST_SEC = 15 * 60;
|
|
|
|
|
|
2020-01-10 17:41:06 +01:00
|
|
|
/** Minimum interval between account storage cleanup work */
|
2020-01-08 14:08:52 +01:00
|
|
|
private const uint APP_BACKGROUNDED_CLEANUP_WORK_INTERVAL_MINUTES = 60 * 24;
|
|
|
|
|
|
2016-06-05 18:36:16 +10:00
|
|
|
private const Geary.SpecialFolderType[] SUPPORTED_SPECIAL_FOLDERS = {
|
|
|
|
|
Geary.SpecialFolderType.DRAFTS,
|
|
|
|
|
Geary.SpecialFolderType.SENT,
|
|
|
|
|
Geary.SpecialFolderType.SPAM,
|
|
|
|
|
Geary.SpecialFolderType.TRASH,
|
|
|
|
|
Geary.SpecialFolderType.ARCHIVE,
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-30 14:12:52 +11:00
|
|
|
private static GLib.VariantType email_id_type = new GLib.VariantType(
|
|
|
|
|
EmailIdentifier.BASE_VARIANT_TYPE
|
|
|
|
|
);
|
2018-01-03 15:01:21 +11:00
|
|
|
|
|
|
|
|
|
2018-11-18 21:31:35 +11:00
|
|
|
/** Service for incoming IMAP connections. */
|
2018-11-18 22:13:25 +11:00
|
|
|
public Imap.ClientService imap { get; private set; }
|
2018-11-18 21:31:35 +11:00
|
|
|
|
|
|
|
|
/** Service for outgoing SMTP connections. */
|
2018-11-18 23:10:11 +11:00
|
|
|
public Smtp.ClientService smtp { get; private set; }
|
2018-11-18 21:31:35 +11:00
|
|
|
|
|
|
|
|
/** Local database for the account. */
|
|
|
|
|
public ImapDB.Account local { get; private set; }
|
|
|
|
|
|
2020-01-09 14:08:46 +01:00
|
|
|
public signal void old_messages_background_cleanup_request(GLib.Cancellable? cancellable);
|
2020-01-08 14:08:52 +01:00
|
|
|
|
Remove SQLHeavy: Closes #5034
It is done.
Initial implementation of the new database subsystem
These pieces represent the foundation for ticket #5034
Expanded transactions, added VersionedDatabase
Further expansions of the async code.
Moved async pool logic into Database, where it realistically
belongs.
Further improvements. Introduced geary-db-test.
Added SQL create and update files for Geary.Db
version-001 to version-003 are exact copies of the SQLHeavy scripts
to ensure no slight changes when migrating. version-004 upgrades
the database to remove the ImapFolderPropertiesTable and
ImapMessagePropertiesTable, now that the database code is pure
IMAP.
When we support other messaging systems (such as POP3), those
subsystems will need to code their own database layers OR rely on
the IMAP schema and simply ignore the IMAP-specific fields.
ImapDB.Account fleshed out
ImapDB.Folder is commented out, however. Need to port next.
ImapDB.Folder fleshed out
MessageTable, MessageLocationTable, and AttachementTable are now
handled inside ImapDB.Folder.
chmod -x imap-db-database.vala
OutboxEmailIdentifier/Properties -> SmtpOutboxEmailIdentifier/Properties
Moved SmtpOutboxFolderRoot into its own source file
SmtpOutboxFolder ported to new database code
Move Engine implementations to ImapDB.
Integration and cleanup of new database code with main source
This commit performs the final integration steps to move Geary
completely over to the new database model. This also cleans out
the old SQLHeavy-based code and fixes a handful of small bugs that
were detected during basic test runs.
Moved Outbox to ImapDB
As the Outbox is tied to the database that ImapDB runs, move the
Outbox code into that folder.
Outbox fixes and better parameter checking
Bumped Database thread pool count and made them exclusive
My reasoning is that there may be a need for a lot of threads at
once (when a big batch of commands comes in, especially at
startup). If performance looks ok, we might consider relaxing
this later.
2012-06-14 14:47:53 -07:00
|
|
|
private bool open = false;
|
2018-01-26 09:52:20 +10:30
|
|
|
private Cancellable? open_cancellable = null;
|
2018-02-27 01:03:21 +11:00
|
|
|
private Nonblocking.Semaphore? remote_ready_lock = null;
|
2018-01-26 09:52:20 +10:30
|
|
|
|
2019-12-13 12:12:07 +11:00
|
|
|
private Gee.Map<FolderPath,MinimalFolder> folder_map =
|
|
|
|
|
new Gee.HashMap<FolderPath,MinimalFolder>();
|
2018-01-26 09:52:20 +10:30
|
|
|
|
2017-11-23 10:11:30 +11:00
|
|
|
private AccountProcessor? processor;
|
2017-11-11 15:40:00 +11:00
|
|
|
private AccountSynchronizer sync;
|
2017-11-09 17:12:48 +11:00
|
|
|
private TimeoutManager refresh_folder_timer;
|
2016-06-15 18:24:50 -04:00
|
|
|
|
|
|
|
|
private Gee.Map<Geary.SpecialFolderType, Gee.List<string>> special_search_names =
|
|
|
|
|
new Gee.HashMap<Geary.SpecialFolderType, Gee.List<string>>();
|
|
|
|
|
|
2017-11-09 17:12:48 +11:00
|
|
|
|
2019-03-14 16:59:24 +01:00
|
|
|
protected GenericAccount(AccountInformation config,
|
|
|
|
|
ImapDB.Account local,
|
|
|
|
|
Endpoint incoming_remote,
|
|
|
|
|
Endpoint outgoing_remote) {
|
2018-12-30 11:32:03 +11:00
|
|
|
Imap.ClientService imap = new Imap.ClientService(
|
|
|
|
|
config,
|
|
|
|
|
config.incoming,
|
|
|
|
|
incoming_remote
|
|
|
|
|
);
|
|
|
|
|
Smtp.ClientService smtp = new Smtp.ClientService(
|
|
|
|
|
config,
|
|
|
|
|
config.outgoing,
|
|
|
|
|
outgoing_remote
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
base(config, imap, smtp);
|
|
|
|
|
|
2018-12-27 10:20:58 +10:30
|
|
|
this.local = local;
|
2019-06-07 18:06:31 +10:00
|
|
|
this.contact_store = new ContactStoreImpl(local.db);
|
2018-11-18 23:18:21 +11:00
|
|
|
|
2018-12-30 11:32:03 +11:00
|
|
|
imap.min_pool_size = IMAP_MIN_POOL_SIZE;
|
|
|
|
|
imap.notify["current-status"].connect(
|
2018-12-30 09:03:57 +11:00
|
|
|
on_imap_status_notify
|
2018-12-29 16:18:39 +11:00
|
|
|
);
|
2019-12-01 19:37:15 +11:00
|
|
|
imap.set_logging_parent(this);
|
2018-12-30 11:32:03 +11:00
|
|
|
this.imap = imap;
|
2017-11-09 17:12:48 +11:00
|
|
|
|
Convert Geary.FolderRoot to be an actual root, not just a top-level
Instead of each top-level IMAP folder being a FolderRoot object, then
children of that being FolderPath objects, this makes FolderRoot an
"empty" FolderPath, so that both top-level and descendant folders are
plain FolderPath objects. Aside from being more technically correct,
this means that empty namespace roots can now be used interchangably
with non-empty namespace roots (addressing issue #181), and custom
folder implementations no longer need to provide their own trivial,
custom FolderRoot.
To support this, a notion of an IMAP root and a local root have been
added from which all remote and local folder paths are now derived,
existing places that assume top-level == root have been fixed, and
unit tests have been added.
2019-01-14 12:10:48 +11:00
|
|
|
smtp.outbox = new Outbox.Folder(this, local_folder_root, local);
|
2018-12-30 11:32:03 +11:00
|
|
|
smtp.report_problem.connect(notify_report_problem);
|
2019-12-01 19:37:15 +11:00
|
|
|
smtp.set_logging_parent(this);
|
2018-12-30 11:32:03 +11:00
|
|
|
this.smtp = smtp;
|
2018-11-18 23:10:11 +11:00
|
|
|
|
|
|
|
|
this.sync = new AccountSynchronizer(this);
|
2017-11-09 17:12:48 +11:00
|
|
|
|
|
|
|
|
this.refresh_folder_timer = new TimeoutManager.seconds(
|
|
|
|
|
REFRESH_FOLDER_LIST_SEC,
|
2017-11-23 16:47:19 +11:00
|
|
|
() => { this.update_remote_folders(); }
|
2017-11-09 17:12:48 +11:00
|
|
|
);
|
|
|
|
|
|
2019-12-02 19:08:01 +08:00
|
|
|
this.background_progress = new ReentrantProgressMonitor(ACTIVITY);
|
2018-11-18 23:10:11 +11:00
|
|
|
this.db_upgrade_monitor = local.upgrade_monitor;
|
|
|
|
|
this.db_vacuum_monitor = local.vacuum_monitor;
|
2016-06-15 18:24:50 -04:00
|
|
|
|
|
|
|
|
compile_special_search_names();
|
2011-06-16 16:27:08 -07:00
|
|
|
}
|
2016-06-15 18:24:50 -04:00
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
/** {@inheritDoc} */
|
2012-06-11 12:03:57 -07:00
|
|
|
public override async void open_async(Cancellable? cancellable = null) throws Error {
|
Remove SQLHeavy: Closes #5034
It is done.
Initial implementation of the new database subsystem
These pieces represent the foundation for ticket #5034
Expanded transactions, added VersionedDatabase
Further expansions of the async code.
Moved async pool logic into Database, where it realistically
belongs.
Further improvements. Introduced geary-db-test.
Added SQL create and update files for Geary.Db
version-001 to version-003 are exact copies of the SQLHeavy scripts
to ensure no slight changes when migrating. version-004 upgrades
the database to remove the ImapFolderPropertiesTable and
ImapMessagePropertiesTable, now that the database code is pure
IMAP.
When we support other messaging systems (such as POP3), those
subsystems will need to code their own database layers OR rely on
the IMAP schema and simply ignore the IMAP-specific fields.
ImapDB.Account fleshed out
ImapDB.Folder is commented out, however. Need to port next.
ImapDB.Folder fleshed out
MessageTable, MessageLocationTable, and AttachementTable are now
handled inside ImapDB.Folder.
chmod -x imap-db-database.vala
OutboxEmailIdentifier/Properties -> SmtpOutboxEmailIdentifier/Properties
Moved SmtpOutboxFolderRoot into its own source file
SmtpOutboxFolder ported to new database code
Move Engine implementations to ImapDB.
Integration and cleanup of new database code with main source
This commit performs the final integration steps to move Geary
completely over to the new database model. This also cleans out
the old SQLHeavy-based code and fixes a handful of small bugs that
were detected during basic test runs.
Moved Outbox to ImapDB
As the Outbox is tied to the database that ImapDB runs, move the
Outbox code into that folder.
Outbox fixes and better parameter checking
Bumped Database thread pool count and made them exclusive
My reasoning is that there may be a need for a lot of threads at
once (when a big batch of commands comes in, especially at
startup). If performance looks ok, we might consider relaxing
this later.
2012-06-14 14:47:53 -07:00
|
|
|
if (open)
|
|
|
|
|
throw new EngineError.ALREADY_OPEN("Account %s already opened", to_string());
|
2018-01-26 09:52:20 +10:30
|
|
|
|
2019-12-02 19:08:01 +08:00
|
|
|
this.background_progress.notify_start();
|
2014-06-12 13:01:47 -07:00
|
|
|
try {
|
|
|
|
|
yield internal_open_async(cancellable);
|
2018-01-26 09:52:20 +10:30
|
|
|
} finally {
|
2019-12-02 19:08:01 +08:00
|
|
|
this.background_progress.notify_finish();
|
2014-06-12 13:01:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
|
2014-06-12 13:01:47 -07:00
|
|
|
private async void internal_open_async(Cancellable? cancellable) throws Error {
|
2018-01-26 09:52:20 +10:30
|
|
|
this.open_cancellable = new Cancellable();
|
|
|
|
|
this.remote_ready_lock = new Nonblocking.Semaphore(this.open_cancellable);
|
|
|
|
|
|
2019-12-02 19:08:01 +08:00
|
|
|
this.processor = new AccountProcessor(this.background_progress);
|
2017-11-23 16:47:19 +11:00
|
|
|
this.processor.operation_error.connect(on_operation_error);
|
2019-12-02 16:31:42 +11:00
|
|
|
this.processor.set_logging_parent(this);
|
2017-11-23 10:11:30 +11:00
|
|
|
|
2013-09-09 14:44:36 -07:00
|
|
|
try {
|
2019-06-12 07:31:51 +10:00
|
|
|
yield this.local.open_async(cancellable);
|
2013-09-09 14:44:36 -07:00
|
|
|
} catch (Error err) {
|
|
|
|
|
// convert database-open errors
|
|
|
|
|
if (err is DatabaseError.CORRUPT)
|
|
|
|
|
throw new EngineError.CORRUPT("%s", err.message);
|
|
|
|
|
else if (err is DatabaseError.ACCESS)
|
|
|
|
|
throw new EngineError.PERMISSIONS("%s", err.message);
|
2013-09-09 15:41:31 -07:00
|
|
|
else if (err is DatabaseError.SCHEMA_VERSION)
|
|
|
|
|
throw new EngineError.VERSION("%s", err.message);
|
2013-09-09 14:44:36 -07:00
|
|
|
else
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
|
2020-01-21 17:57:33 +01:00
|
|
|
this.last_storage_cleanup = yield this.local.fetch_last_cleanup_async(cancellable);
|
|
|
|
|
this.last_storage_cleanup_changed.connect ((dt) => {
|
|
|
|
|
this.local.set_last_cleanup_async.begin(dt, cancellable);
|
|
|
|
|
});
|
|
|
|
|
|
2017-11-09 17:12:48 +11:00
|
|
|
this.open = true;
|
|
|
|
|
notify_opened();
|
|
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
this.queue_operation(
|
|
|
|
|
new LoadFolders(this, this.local, get_supported_special_folders())
|
|
|
|
|
);
|
2018-11-18 22:13:25 +11:00
|
|
|
|
2018-11-18 23:10:11 +11:00
|
|
|
// Start the mail services. Start incoming directly, but queue
|
|
|
|
|
// outgoing so local folders can be loaded first in case
|
|
|
|
|
// queued mail gets sent and needs to get saved somewhere.
|
2018-11-18 22:13:25 +11:00
|
|
|
yield this.imap.start(cancellable);
|
2018-11-18 23:10:11 +11:00
|
|
|
this.queue_operation(new StartPostie(this));
|
|
|
|
|
|
2019-12-12 11:19:51 +11:00
|
|
|
// Kick off a background update of the search table.
|
2019-06-23 13:26:07 +10:00
|
|
|
//
|
2019-12-12 11:19:51 +11:00
|
|
|
// XXX since this hammers the database, this is an example of
|
|
|
|
|
// an operation for which we need an engine-wide operation
|
|
|
|
|
// queue, not just an account-wide queue.
|
|
|
|
|
this.queue_operation(new PopulateSearchTable(this));
|
2012-06-11 12:03:57 -07:00
|
|
|
}
|
2017-11-09 17:12:48 +11:00
|
|
|
|
2012-06-11 12:03:57 -07:00
|
|
|
public override async void close_async(Cancellable? cancellable = null) throws Error {
|
Remove SQLHeavy: Closes #5034
It is done.
Initial implementation of the new database subsystem
These pieces represent the foundation for ticket #5034
Expanded transactions, added VersionedDatabase
Further expansions of the async code.
Moved async pool logic into Database, where it realistically
belongs.
Further improvements. Introduced geary-db-test.
Added SQL create and update files for Geary.Db
version-001 to version-003 are exact copies of the SQLHeavy scripts
to ensure no slight changes when migrating. version-004 upgrades
the database to remove the ImapFolderPropertiesTable and
ImapMessagePropertiesTable, now that the database code is pure
IMAP.
When we support other messaging systems (such as POP3), those
subsystems will need to code their own database layers OR rely on
the IMAP schema and simply ignore the IMAP-specific fields.
ImapDB.Account fleshed out
ImapDB.Folder is commented out, however. Need to port next.
ImapDB.Folder fleshed out
MessageTable, MessageLocationTable, and AttachementTable are now
handled inside ImapDB.Folder.
chmod -x imap-db-database.vala
OutboxEmailIdentifier/Properties -> SmtpOutboxEmailIdentifier/Properties
Moved SmtpOutboxFolderRoot into its own source file
SmtpOutboxFolder ported to new database code
Move Engine implementations to ImapDB.
Integration and cleanup of new database code with main source
This commit performs the final integration steps to move Geary
completely over to the new database model. This also cleans out
the old SQLHeavy-based code and fixes a handful of small bugs that
were detected during basic test runs.
Moved Outbox to ImapDB
As the Outbox is tied to the database that ImapDB runs, move the
Outbox code into that folder.
Outbox fixes and better parameter checking
Bumped Database thread pool count and made them exclusive
My reasoning is that there may be a need for a lot of threads at
once (when a big batch of commands comes in, especially at
startup). If performance looks ok, we might consider relaxing
this later.
2012-06-14 14:47:53 -07:00
|
|
|
if (!open)
|
|
|
|
|
return;
|
2013-01-17 12:50:30 -08:00
|
|
|
|
2018-11-18 23:10:11 +11:00
|
|
|
// Stop attempting to send any outgoing messages
|
|
|
|
|
try {
|
|
|
|
|
yield this.smtp.stop();
|
|
|
|
|
} catch (Error err) {
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Error stopping SMTP service: %s", err.message);
|
2018-11-18 23:10:11 +11:00
|
|
|
}
|
|
|
|
|
|
2017-11-23 10:11:30 +11:00
|
|
|
// Halt internal tasks early so they stop using local and
|
|
|
|
|
// remote connections.
|
2017-11-09 17:12:48 +11:00
|
|
|
this.refresh_folder_timer.reset();
|
2018-01-26 09:52:20 +10:30
|
|
|
this.open_cancellable.cancel();
|
|
|
|
|
this.processor.stop();
|
2017-11-09 17:12:48 +11:00
|
|
|
|
2019-03-17 16:04:07 +11:00
|
|
|
// Block obtaining and reusing IMAP connections. This *must*
|
|
|
|
|
// happen after internal tasks above are cancelled otherwise
|
|
|
|
|
// they may block while waiting/using a remote session.
|
|
|
|
|
this.imap.discard_returned_sessions = true;
|
|
|
|
|
this.remote_ready_lock.reset();
|
|
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
// Close folders and ensure they do in fact close
|
2017-11-21 01:13:21 +11:00
|
|
|
|
2019-02-19 16:39:11 +11:00
|
|
|
Gee.BidirSortedSet<Folder> remotes = sort_by_path(this.folder_map.values);
|
2017-11-21 01:13:21 +11:00
|
|
|
this.folder_map.clear();
|
|
|
|
|
notify_folders_available_unavailable(null, remotes);
|
|
|
|
|
|
|
|
|
|
foreach (Geary.Folder folder in remotes) {
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Waiting for remote to close: %s", folder.to_string());
|
2017-11-21 01:13:21 +11:00
|
|
|
yield folder.wait_for_close_async();
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-13 12:12:07 +11:00
|
|
|
// Close IMAP service manager now that folders are closed
|
2017-11-21 01:13:21 +11:00
|
|
|
|
2012-06-11 12:03:57 -07:00
|
|
|
try {
|
2018-11-18 22:13:25 +11:00
|
|
|
yield this.imap.stop();
|
2018-01-26 09:52:20 +10:30
|
|
|
} catch (Error err) {
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Error stopping IMAP service: %s", err.message);
|
2012-06-11 12:03:57 -07:00
|
|
|
}
|
2018-02-27 01:03:21 +11:00
|
|
|
this.remote_ready_lock = null;
|
2017-11-21 01:13:21 +11:00
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
// Close local infrastructure
|
2017-11-20 19:51:03 +11:00
|
|
|
|
2012-06-11 12:03:57 -07:00
|
|
|
try {
|
2018-01-26 09:52:20 +10:30
|
|
|
yield local.close_async(cancellable);
|
|
|
|
|
} finally {
|
|
|
|
|
this.open = false;
|
|
|
|
|
notify_closed();
|
2012-06-11 12:03:57 -07:00
|
|
|
}
|
2011-06-23 19:07:04 -07:00
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
|
|
|
|
|
/** {@inheritDoc} */
|
2013-02-04 19:26:48 -08:00
|
|
|
public override bool is_open() {
|
|
|
|
|
return open;
|
|
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
|
2019-06-12 07:31:51 +10:00
|
|
|
public override async void rebuild_async(GLib.Cancellable? cancellable = null)
|
|
|
|
|
throws GLib.Error {
|
|
|
|
|
if (this.open) {
|
|
|
|
|
throw new EngineError.ALREADY_OPEN(
|
|
|
|
|
"Account cannot be open during rebuild"
|
2018-06-17 13:58:33 +10:00
|
|
|
);
|
2013-09-09 14:44:36 -07:00
|
|
|
}
|
2018-06-17 13:58:33 +10:00
|
|
|
|
2018-12-29 13:34:29 +11:00
|
|
|
message("Rebuilding account local data");
|
2019-06-12 07:31:51 +10:00
|
|
|
yield this.local.delete_all_data(cancellable);
|
2018-12-29 13:34:29 +11:00
|
|
|
message("Rebuild complete");
|
2013-09-09 14:44:36 -07:00
|
|
|
}
|
2017-11-09 18:03:44 +11:00
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
/**
|
|
|
|
|
* Queues an operation for execution by this account.
|
|
|
|
|
*
|
|
|
|
|
* The operation will added to the account's {@link
|
|
|
|
|
* AccountProcessor} and executed asynchronously by that when it
|
|
|
|
|
* reaches the front.
|
|
|
|
|
*/
|
|
|
|
|
public void queue_operation(AccountOperation op)
|
|
|
|
|
throws EngineError {
|
|
|
|
|
check_open();
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Enqueuing operation: %s", op.to_string());
|
2018-01-26 09:52:20 +10:30
|
|
|
this.processor.enqueue(op);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-02-27 01:03:21 +11:00
|
|
|
* Claims a new IMAP account session from the pool.
|
2018-01-26 09:52:20 +10:30
|
|
|
*
|
2018-02-27 01:03:21 +11:00
|
|
|
* A new IMAP client session will be retrieved from the pool,
|
|
|
|
|
* connecting if needed, and used for a new account session. This
|
|
|
|
|
* call will wait until the pool is ready to provide sessions. The
|
|
|
|
|
* session must be returned via {@link release_account_session}
|
|
|
|
|
* after use.
|
2018-01-26 09:52:20 +10:30
|
|
|
*
|
|
|
|
|
* The account must have been opened before calling this method.
|
|
|
|
|
*/
|
|
|
|
|
public async Imap.AccountSession claim_account_session(Cancellable? cancellable = null)
|
|
|
|
|
throws Error {
|
|
|
|
|
check_open();
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Acquiring account session");
|
2018-01-26 09:52:20 +10:30
|
|
|
yield this.remote_ready_lock.wait_async(cancellable);
|
2019-12-02 11:43:41 +11:00
|
|
|
var client = yield this.imap.claim_authorized_session_async(cancellable);
|
|
|
|
|
var session = new Imap.AccountSession(this.local.imap_folder_root, client);
|
|
|
|
|
session.set_logging_parent(this.imap);
|
|
|
|
|
return session;
|
2018-02-27 01:03:21 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns an IMAP account session to the pool for re-use.
|
|
|
|
|
*/
|
|
|
|
|
public void release_account_session(Imap.AccountSession session) {
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Releasing account session");
|
2018-02-27 01:03:21 +11:00
|
|
|
Imap.ClientSession? old_session = session.close();
|
|
|
|
|
if (old_session != null) {
|
2018-11-18 22:13:25 +11:00
|
|
|
this.imap.release_session_async.begin(
|
2018-02-27 01:03:21 +11:00
|
|
|
old_session,
|
|
|
|
|
(obj, res) => {
|
|
|
|
|
try {
|
2018-11-18 22:13:25 +11:00
|
|
|
this.imap.release_session_async.end(res);
|
2018-02-27 01:03:21 +11:00
|
|
|
} catch (Error err) {
|
2018-12-29 13:34:29 +11:00
|
|
|
debug(
|
|
|
|
|
"Error releasing account session: %s", err.message
|
|
|
|
|
);
|
2018-02-27 01:03:21 +11:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
2013-01-17 12:50:30 -08:00
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-02-27 01:17:35 +11:00
|
|
|
* Claims a new IMAP folder session from the pool.
|
2018-01-26 09:52:20 +10:30
|
|
|
*
|
|
|
|
|
* A new IMAP client session will be retrieved from the pool,
|
|
|
|
|
* connecting if needed, and used for a new folder session. This
|
|
|
|
|
* call will wait until the pool is ready to provide sessions. The
|
|
|
|
|
* session must be returned via {@link release_folder_session}
|
|
|
|
|
* after use.
|
2018-02-27 01:17:35 +11:00
|
|
|
*
|
|
|
|
|
* The account must have been opened before calling this method.
|
2018-01-26 09:52:20 +10:30
|
|
|
*/
|
2018-02-27 01:17:35 +11:00
|
|
|
public async Imap.FolderSession claim_folder_session(Geary.FolderPath path,
|
|
|
|
|
Cancellable cancellable)
|
2018-01-26 09:52:20 +10:30
|
|
|
throws Error {
|
|
|
|
|
check_open();
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Acquiring folder session for: %s", path.to_string());
|
2018-02-27 01:17:35 +11:00
|
|
|
yield this.remote_ready_lock.wait_async(cancellable);
|
|
|
|
|
|
|
|
|
|
// We manually construct an account session here and then
|
|
|
|
|
// reuse it for the folder session so we only need to claim as
|
|
|
|
|
// single session from the pool, not two.
|
|
|
|
|
|
|
|
|
|
Imap.ClientSession? client =
|
2018-11-18 22:13:25 +11:00
|
|
|
yield this.imap.claim_authorized_session_async(cancellable);
|
2018-02-27 01:17:35 +11:00
|
|
|
Imap.AccountSession account = new Imap.AccountSession(
|
2019-12-02 11:43:41 +11:00
|
|
|
this.local.imap_folder_root, client
|
2018-02-27 01:17:35 +11:00
|
|
|
);
|
2019-12-02 11:43:41 +11:00
|
|
|
account.set_logging_parent(this.imap);
|
2018-02-27 01:17:35 +11:00
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
Imap.Folder? folder = null;
|
2018-02-27 01:17:35 +11:00
|
|
|
GLib.Error? folder_err = null;
|
2017-11-09 18:03:44 +11:00
|
|
|
try {
|
2018-01-26 09:52:20 +10:30
|
|
|
folder = yield account.fetch_folder_async(path, cancellable);
|
2017-11-09 18:03:44 +11:00
|
|
|
} catch (Error err) {
|
2018-02-27 01:17:35 +11:00
|
|
|
folder_err = err;
|
2013-01-17 12:50:30 -08:00
|
|
|
}
|
2018-02-27 01:17:35 +11:00
|
|
|
|
|
|
|
|
account.close();
|
|
|
|
|
|
2018-11-13 15:50:35 +11:00
|
|
|
Imap.FolderSession? folder_session = null;
|
|
|
|
|
if (folder_err == null) {
|
|
|
|
|
try {
|
|
|
|
|
folder_session = yield new Imap.FolderSession(
|
2019-12-02 11:43:41 +11:00
|
|
|
client, folder, cancellable
|
2018-11-13 15:50:35 +11:00
|
|
|
);
|
2019-12-02 11:43:41 +11:00
|
|
|
folder_session.set_logging_parent(this.imap);
|
2018-11-13 15:50:35 +11:00
|
|
|
} catch (Error err) {
|
|
|
|
|
folder_err = err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-27 01:17:35 +11:00
|
|
|
if (folder_err != null) {
|
|
|
|
|
try {
|
2018-11-18 22:13:25 +11:00
|
|
|
yield this.imap.release_session_async(client);
|
2018-02-27 01:17:35 +11:00
|
|
|
} catch (Error release_err) {
|
|
|
|
|
debug("Error releasing folder session: %s", release_err.message);
|
2018-01-26 09:52:20 +10:30
|
|
|
}
|
2018-02-27 01:17:35 +11:00
|
|
|
|
|
|
|
|
throw folder_err;
|
2017-11-09 18:03:44 +11:00
|
|
|
}
|
|
|
|
|
|
2018-11-13 15:50:35 +11:00
|
|
|
return folder_session;
|
2012-06-14 16:47:54 -07:00
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns an IMAP folder session to the pool for cleanup and re-use.
|
|
|
|
|
*/
|
2018-02-27 02:05:58 +11:00
|
|
|
public async void release_folder_session(Imap.FolderSession session) {
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Releasing folder session");
|
2018-01-26 09:52:20 +10:30
|
|
|
Imap.ClientSession? old_session = session.close();
|
|
|
|
|
if (old_session != null) {
|
2018-02-27 02:05:58 +11:00
|
|
|
try {
|
2018-11-18 22:13:25 +11:00
|
|
|
yield this.imap.release_session_async(old_session);
|
2018-02-27 02:05:58 +11:00
|
|
|
} catch (Error err) {
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Error releasing %s session: %s",
|
2018-02-27 02:05:58 +11:00
|
|
|
session.folder.path.to_string(),
|
|
|
|
|
err.message);
|
|
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
}
|
2017-11-09 18:03:44 +11:00
|
|
|
}
|
|
|
|
|
|
2018-01-03 15:01:21 +11:00
|
|
|
/** {@inheritDoc} */
|
|
|
|
|
public override EmailIdentifier to_email_identifier(GLib.Variant serialised)
|
|
|
|
|
throws EngineError.BAD_PARAMETERS {
|
2019-10-30 14:12:52 +11:00
|
|
|
if (!serialised.is_of_type(GenericAccount.email_id_type)) {
|
|
|
|
|
throw new EngineError.BAD_PARAMETERS("Invalid outer serialised type");
|
2018-01-03 15:01:21 +11:00
|
|
|
}
|
|
|
|
|
char type = (char) serialised.get_child_value(0).get_byte();
|
|
|
|
|
if (type == 'i')
|
|
|
|
|
return new ImapDB.EmailIdentifier.from_variant(serialised);
|
2019-10-30 14:17:44 +11:00
|
|
|
if (type == 'o')
|
2018-01-03 15:01:21 +11:00
|
|
|
return new Outbox.EmailIdentifier.from_variant(serialised);
|
|
|
|
|
|
|
|
|
|
throw new EngineError.BAD_PARAMETERS("Unknown serialised type: %c", type);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-14 20:57:31 +10:00
|
|
|
/** {@inheritDoc} */
|
|
|
|
|
public override FolderPath to_folder_path(GLib.Variant serialised)
|
|
|
|
|
throws EngineError.BAD_PARAMETERS {
|
|
|
|
|
FolderPath? path = null;
|
|
|
|
|
try {
|
|
|
|
|
path = this.local.imap_folder_root.from_variant(serialised);
|
|
|
|
|
} catch (EngineError.BAD_PARAMETERS err) {
|
|
|
|
|
path = this.local_folder_root.from_variant(serialised);
|
|
|
|
|
}
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@inheritDoc} */
|
|
|
|
|
public override Folder get_folder(FolderPath path)
|
|
|
|
|
throws EngineError.NOT_FOUND {
|
|
|
|
|
Folder? folder = this.folder_map.get(path);
|
|
|
|
|
if (folder == null) {
|
2019-12-13 12:12:07 +11:00
|
|
|
throw new EngineError.NOT_FOUND(
|
|
|
|
|
"Folder not found: %s", path.to_string()
|
|
|
|
|
);
|
2019-04-14 20:57:31 +10:00
|
|
|
}
|
|
|
|
|
return folder;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@inheritDoc} */
|
2019-10-03 07:52:43 +10:00
|
|
|
public override Gee.Collection<Folder> list_folders() {
|
2019-12-13 12:12:07 +11:00
|
|
|
var all = new Gee.HashSet<Folder>();
|
|
|
|
|
all.add_all(this.folder_map.values);
|
|
|
|
|
return all;
|
2019-10-03 07:52:43 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@inheritDoc} */
|
|
|
|
|
public override Gee.Collection<Folder> list_matching_folders(FolderPath? parent)
|
|
|
|
|
throws EngineError.NOT_FOUND {
|
|
|
|
|
return traverse<FolderPath>(folder_map.keys)
|
2013-12-12 12:42:02 -08:00
|
|
|
.filter(p => {
|
2019-01-14 17:26:18 +11:00
|
|
|
FolderPath? path_parent = p.parent;
|
2013-12-12 12:42:02 -08:00
|
|
|
return ((parent == null && path_parent == null) ||
|
2019-10-03 07:52:43 +10:00
|
|
|
(parent != null && path_parent != null &&
|
|
|
|
|
path_parent.equal_to(parent)));
|
2013-12-12 12:42:02 -08:00
|
|
|
})
|
|
|
|
|
.map<Geary.Folder>(p => folder_map.get(p))
|
|
|
|
|
.to_array_list();
|
2013-01-17 12:50:30 -08:00
|
|
|
}
|
|
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
public override async Geary.Folder get_required_special_folder_async(Geary.SpecialFolderType special,
|
|
|
|
|
Cancellable? cancellable) throws Error {
|
|
|
|
|
if (!(special in get_supported_special_folders())) {
|
|
|
|
|
throw new EngineError.BAD_PARAMETERS(
|
|
|
|
|
"Invalid special folder type %s passed to get_required_special_folder_async",
|
|
|
|
|
special.to_string());
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
2017-11-23 16:47:19 +11:00
|
|
|
check_open();
|
2016-06-15 18:24:50 -04:00
|
|
|
|
2018-02-27 01:03:21 +11:00
|
|
|
Geary.Folder? folder = get_special_folder(special);
|
|
|
|
|
if (folder == null) {
|
|
|
|
|
Imap.AccountSession account = yield claim_account_session();
|
|
|
|
|
try {
|
|
|
|
|
folder = yield ensure_special_folder_async(account, special, cancellable);
|
|
|
|
|
} finally {
|
|
|
|
|
release_account_session(account);
|
2015-02-05 17:57:27 -08:00
|
|
|
}
|
2014-03-03 15:05:41 -08:00
|
|
|
}
|
2018-02-27 01:03:21 +11:00
|
|
|
return folder;
|
2014-03-03 15:05:41 -08:00
|
|
|
}
|
2016-06-15 18:24:50 -04:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
private ImapDB.EmailIdentifier check_id(Geary.EmailIdentifier id) throws EngineError {
|
|
|
|
|
ImapDB.EmailIdentifier? imapdb_id = id as ImapDB.EmailIdentifier;
|
|
|
|
|
if (imapdb_id == null)
|
|
|
|
|
throw new EngineError.BAD_PARAMETERS("EmailIdentifier %s not from ImapDB folder", id.to_string());
|
2019-02-28 09:38:56 +01:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
return imapdb_id;
|
2013-02-25 20:18:37 -08:00
|
|
|
}
|
2019-02-28 09:38:56 +01:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
private Gee.Collection<ImapDB.EmailIdentifier> check_ids(Gee.Collection<Geary.EmailIdentifier> ids)
|
|
|
|
|
throws EngineError {
|
|
|
|
|
foreach (Geary.EmailIdentifier id in ids) {
|
|
|
|
|
if (!(id is ImapDB.EmailIdentifier))
|
|
|
|
|
throw new EngineError.BAD_PARAMETERS("EmailIdentifier %s not from ImapDB folder", id.to_string());
|
2013-02-25 20:18:37 -08:00
|
|
|
}
|
2019-02-28 09:38:56 +01:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
return (Gee.Collection<ImapDB.EmailIdentifier>) ids;
|
2013-02-25 20:18:37 -08:00
|
|
|
}
|
2019-02-28 09:38:56 +01:00
|
|
|
|
2019-12-10 13:36:36 +11:00
|
|
|
/** {@inheritDoc} */
|
|
|
|
|
public override async SearchQuery new_search_query(string query,
|
|
|
|
|
SearchQuery.Strategy strategy,
|
|
|
|
|
GLib.Cancellable? cancellable)
|
|
|
|
|
throws GLib.Error {
|
|
|
|
|
return yield new ImapDB.SearchQuery(
|
|
|
|
|
this, local, query, strategy, cancellable
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
public override async Gee.MultiMap<Geary.Email, Geary.FolderPath?>? local_search_message_id_async(
|
|
|
|
|
Geary.RFC822.MessageID message_id, Geary.Email.Field requested_fields, bool partial_ok,
|
|
|
|
|
Gee.Collection<Geary.FolderPath?>? folder_blacklist, Geary.EmailFlags? flag_blacklist,
|
|
|
|
|
Cancellable? cancellable = null) throws Error {
|
|
|
|
|
return yield local.search_message_id_async(
|
|
|
|
|
message_id, requested_fields, partial_ok, folder_blacklist, flag_blacklist, cancellable);
|
2013-05-31 17:56:48 -07:00
|
|
|
}
|
2019-02-28 09:38:56 +01:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
public override async Geary.Email local_fetch_email_async(Geary.EmailIdentifier email_id,
|
|
|
|
|
Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error {
|
|
|
|
|
return yield local.fetch_email_async(check_id(email_id), required_fields, cancellable);
|
|
|
|
|
}
|
2019-02-28 09:38:56 +01:00
|
|
|
|
2019-12-09 12:35:02 +11:00
|
|
|
/** {@inheritDoc} */
|
2019-12-10 13:36:36 +11:00
|
|
|
public override async Gee.List<Email> list_local_email_async(
|
|
|
|
|
Gee.Collection<EmailIdentifier> ids,
|
|
|
|
|
Email.Field required_fields,
|
|
|
|
|
GLib.Cancellable? cancellable = null
|
|
|
|
|
) throws GLib.Error {
|
|
|
|
|
return yield local.list_email(
|
|
|
|
|
check_ids(ids), required_fields, cancellable
|
2019-12-09 12:35:02 +11:00
|
|
|
);
|
2017-11-23 16:47:19 +11:00
|
|
|
}
|
2019-02-28 09:38:56 +01:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
public override async Gee.Collection<Geary.EmailIdentifier>? local_search_async(Geary.SearchQuery query,
|
|
|
|
|
int limit = 100, int offset = 0, Gee.Collection<Geary.FolderPath?>? folder_blacklist = null,
|
|
|
|
|
Gee.Collection<Geary.EmailIdentifier>? search_ids = null, Cancellable? cancellable = null) throws Error {
|
|
|
|
|
if (offset < 0)
|
|
|
|
|
throw new EngineError.BAD_PARAMETERS("Offset must not be negative");
|
2019-02-28 09:38:56 +01:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
return yield local.search_async(query, limit, offset, folder_blacklist, search_ids, cancellable);
|
|
|
|
|
}
|
2019-02-28 09:38:56 +01:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
public override async Gee.Set<string>? get_search_matches_async(Geary.SearchQuery query,
|
|
|
|
|
Gee.Collection<Geary.EmailIdentifier> ids, Cancellable? cancellable = null) throws Error {
|
|
|
|
|
return yield local.get_search_matches_async(query, check_ids(ids), cancellable);
|
|
|
|
|
}
|
2018-11-18 23:10:11 +11:00
|
|
|
|
|
|
|
|
public override async Gee.MultiMap<EmailIdentifier,FolderPath>?
|
|
|
|
|
get_containing_folders_async(Gee.Collection<Geary.EmailIdentifier> ids,
|
|
|
|
|
GLib.Cancellable? cancellable)
|
|
|
|
|
throws GLib.Error {
|
|
|
|
|
Gee.MultiMap<EmailIdentifier,FolderPath> map =
|
|
|
|
|
new Gee.HashMultiMap<EmailIdentifier,FolderPath>();
|
|
|
|
|
yield this.local.get_containing_folders_async(ids, map, cancellable);
|
|
|
|
|
yield this.smtp.outbox.add_to_containing_folders_async(ids, map, cancellable);
|
|
|
|
|
return (map.size == 0) ? null : map;
|
2017-11-23 16:47:19 +11:00
|
|
|
}
|
2016-06-15 18:24:50 -04:00
|
|
|
|
2020-01-08 14:08:52 +01:00
|
|
|
/** {@inheritDoc} */
|
2020-01-09 14:08:46 +01:00
|
|
|
public override async void cleanup_storage(GLib.Cancellable? cancellable) {
|
2020-01-09 13:07:17 +01:00
|
|
|
debug("Backgrounded storage cleanup check for %s account", this.information.display_name);
|
2020-01-08 14:08:52 +01:00
|
|
|
|
|
|
|
|
DateTime now = new DateTime.now_local();
|
2020-01-09 13:00:52 +01:00
|
|
|
DateTime? last_cleanup = this.last_storage_cleanup;
|
2020-01-08 14:08:52 +01:00
|
|
|
|
|
|
|
|
if (last_cleanup == null ||
|
|
|
|
|
(now.difference(last_cleanup) / TimeSpan.MINUTE > APP_BACKGROUNDED_CLEANUP_WORK_INTERVAL_MINUTES)) {
|
|
|
|
|
// Interval check is OK, start by detaching old messages
|
2020-01-09 13:00:52 +01:00
|
|
|
this.last_storage_cleanup = now;
|
2020-01-08 14:08:52 +01:00
|
|
|
this.old_messages_background_cleanup_request(cancellable);
|
|
|
|
|
} else if (local.db.want_background_vacuum) {
|
|
|
|
|
// Vacuum has been flagged as needed, run it
|
2020-01-10 14:18:01 +01:00
|
|
|
local.db.run_gc.begin(cancellable, false, true, this.imap, this.smtp);
|
2020-01-08 14:08:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
/**
|
|
|
|
|
* Constructs a set of folders and adds them to the account.
|
|
|
|
|
*
|
|
|
|
|
* This constructs a high-level folder representation for each
|
|
|
|
|
* folder, adds them to this account object, fires the appropriate
|
|
|
|
|
* signals, then returns them. Both the local and remote folder
|
|
|
|
|
* equivalents need to exist beforehand — they are not created.
|
|
|
|
|
*
|
|
|
|
|
* If `are_existing` is true, the folders are assumed to have been
|
2018-01-15 16:17:11 +11:00
|
|
|
* seen before and the {@link Geary.Account.folders_created} signal is
|
|
|
|
|
* not fired.
|
2017-11-23 16:47:19 +11:00
|
|
|
*/
|
2019-02-19 16:39:11 +11:00
|
|
|
internal Gee.Collection<Folder> add_folders(Gee.Collection<ImapDB.Folder> db_folders,
|
2017-11-23 16:47:19 +11:00
|
|
|
bool are_existing) {
|
2019-02-19 16:39:11 +11:00
|
|
|
Gee.TreeSet<MinimalFolder> built_folders = new Gee.TreeSet<MinimalFolder>(
|
|
|
|
|
Account.folder_path_comparator
|
|
|
|
|
);
|
2017-11-23 16:47:19 +11:00
|
|
|
foreach(ImapDB.Folder db_folder in db_folders) {
|
|
|
|
|
if (!this.folder_map.has_key(db_folder.get_path())) {
|
|
|
|
|
MinimalFolder folder = new_folder(db_folder);
|
|
|
|
|
folder.report_problem.connect(notify_report_problem);
|
|
|
|
|
built_folders.add(folder);
|
|
|
|
|
this.folder_map.set(folder.path, folder);
|
|
|
|
|
}
|
2011-07-01 15:40:20 -07:00
|
|
|
}
|
2016-06-15 18:24:50 -04:00
|
|
|
|
2019-02-19 16:39:11 +11:00
|
|
|
if (!built_folders.is_empty) {
|
2017-11-23 16:47:19 +11:00
|
|
|
notify_folders_available_unavailable(built_folders, null);
|
|
|
|
|
if (!are_existing) {
|
|
|
|
|
notify_folders_created(built_folders);
|
2013-05-31 17:56:48 -07:00
|
|
|
}
|
2011-07-01 15:40:20 -07:00
|
|
|
}
|
2016-06-15 18:24:50 -04:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
return built_folders;
|
2013-05-31 17:56:48 -07:00
|
|
|
}
|
2016-06-15 18:24:50 -04:00
|
|
|
|
2017-11-28 10:12:41 +11:00
|
|
|
/**
|
|
|
|
|
* Fires appropriate signals for a single altered folder.
|
|
|
|
|
*
|
|
|
|
|
* This is functionally equivalent to {@link update_folders}.
|
|
|
|
|
*/
|
|
|
|
|
internal void update_folder(Geary.Folder folder) {
|
|
|
|
|
Gee.Collection<Geary.Folder> folders =
|
|
|
|
|
new Gee.LinkedList<Geary.Folder>();
|
|
|
|
|
folders.add(folder);
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Folder updated: %s", folder.path.to_string());
|
2017-11-28 10:12:41 +11:00
|
|
|
notify_folders_contents_altered(folders);
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
/**
|
|
|
|
|
* Fires appropriate signals for folders have been altered.
|
2017-11-28 10:12:41 +11:00
|
|
|
*
|
|
|
|
|
* This is functionally equivalent to {@link update_folder}.
|
2017-11-23 16:47:19 +11:00
|
|
|
*/
|
|
|
|
|
internal void update_folders(Gee.Collection<Geary.Folder> folders) {
|
|
|
|
|
if (!folders.is_empty) {
|
|
|
|
|
notify_folders_contents_altered(sort_by_path(folders));
|
2011-07-01 15:40:20 -07:00
|
|
|
}
|
2017-11-23 16:47:19 +11:00
|
|
|
}
|
|
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
/**
|
|
|
|
|
* Marks a folder as a specific special folder type.
|
|
|
|
|
*/
|
|
|
|
|
internal void promote_folders(Gee.Map<Geary.SpecialFolderType,Geary.Folder> specials) {
|
|
|
|
|
Gee.Set<Geary.Folder> changed = new Gee.HashSet<Geary.Folder>();
|
|
|
|
|
foreach (Geary.SpecialFolderType special in specials.keys) {
|
|
|
|
|
MinimalFolder? minimal = specials.get(special) as MinimalFolder;
|
|
|
|
|
if (minimal.special_folder_type != special) {
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Promoting %s to %s",
|
|
|
|
|
minimal.to_string(), special.to_string());
|
2018-01-26 09:52:20 +10:30
|
|
|
minimal.set_special_folder_type(special);
|
|
|
|
|
changed.add(minimal);
|
|
|
|
|
|
2019-10-03 07:52:43 +10:00
|
|
|
MinimalFolder? existing =
|
|
|
|
|
get_special_folder(special) as MinimalFolder;
|
2018-01-26 09:52:20 +10:30
|
|
|
if (existing != null && existing != minimal) {
|
|
|
|
|
existing.set_special_folder_type(SpecialFolderType.NONE);
|
|
|
|
|
changed.add(existing);
|
2013-06-05 18:24:42 -07:00
|
|
|
}
|
2013-05-31 17:56:48 -07:00
|
|
|
}
|
|
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
|
|
|
|
|
if (!changed.is_empty) {
|
|
|
|
|
folders_special_type(changed);
|
|
|
|
|
}
|
2011-06-16 16:27:08 -07:00
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
/**
|
|
|
|
|
* Removes a set of folders from the account.
|
|
|
|
|
*
|
|
|
|
|
* This removes the high-level folder representations from this
|
|
|
|
|
* account object, and fires the appropriate signals. Deletion of
|
|
|
|
|
* both the local and remote folder equivalents must be handled
|
|
|
|
|
* before, then after calling this method.
|
|
|
|
|
*
|
|
|
|
|
* A collection of folders that was actually removed is returned.
|
|
|
|
|
*/
|
2019-02-19 16:39:11 +11:00
|
|
|
internal Gee.BidirSortedSet<MinimalFolder>
|
|
|
|
|
remove_folders(Gee.Collection<Folder> folders) {
|
|
|
|
|
Gee.TreeSet<MinimalFolder> removed = new Gee.TreeSet<MinimalFolder>(
|
|
|
|
|
Account.folder_path_comparator
|
|
|
|
|
);
|
2017-11-23 16:47:19 +11:00
|
|
|
foreach(Geary.Folder folder in folders) {
|
|
|
|
|
MinimalFolder? impl = this.folder_map.get(folder.path);
|
|
|
|
|
if (impl != null) {
|
|
|
|
|
this.folder_map.unset(folder.path);
|
|
|
|
|
removed.add(impl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!removed.is_empty) {
|
|
|
|
|
notify_folders_available_unavailable(null, removed);
|
|
|
|
|
notify_folders_deleted(removed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return removed;
|
2012-07-20 17:51:28 -07:00
|
|
|
}
|
2017-09-25 23:21:33 +10:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
/**
|
2018-02-27 01:03:21 +11:00
|
|
|
* Locates a special folder, creating it if needed.
|
2017-11-23 16:47:19 +11:00
|
|
|
*/
|
2019-01-14 23:22:57 +11:00
|
|
|
internal async Folder
|
|
|
|
|
ensure_special_folder_async(Imap.AccountSession remote,
|
|
|
|
|
SpecialFolderType type,
|
|
|
|
|
GLib.Cancellable? cancellable)
|
|
|
|
|
throws GLib.Error {
|
|
|
|
|
Folder? special = get_special_folder(type);
|
|
|
|
|
if (special == null) {
|
|
|
|
|
FolderPath? path = information.get_special_folder_path(type);
|
2019-04-14 20:55:42 +10:00
|
|
|
if (path != null) {
|
|
|
|
|
if (!remote.is_folder_path_valid(path)) {
|
|
|
|
|
warning(
|
2018-12-29 13:34:29 +11:00
|
|
|
"Ignoring bad special folder path '%s' for type %s",
|
2019-04-14 20:55:42 +10:00
|
|
|
path.to_string(),
|
|
|
|
|
type.to_string()
|
|
|
|
|
);
|
|
|
|
|
path = null;
|
|
|
|
|
} else {
|
|
|
|
|
path = this.local.imap_folder_root.copy(path);
|
|
|
|
|
}
|
2019-01-14 23:22:57 +11:00
|
|
|
}
|
2019-04-14 20:55:42 +10:00
|
|
|
|
2019-01-14 23:22:57 +11:00
|
|
|
if (path == null) {
|
|
|
|
|
FolderPath root =
|
|
|
|
|
yield remote.get_default_personal_namespace(cancellable);
|
|
|
|
|
Gee.List<string> search_names = special_search_names.get(type);
|
|
|
|
|
foreach (string search_name in search_names) {
|
|
|
|
|
FolderPath search_path = root.get_child(search_name);
|
|
|
|
|
foreach (FolderPath test_path in folder_map.keys) {
|
|
|
|
|
if (test_path.compare_normalized_ci(search_path) == 0) {
|
|
|
|
|
path = search_path;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
2019-01-14 23:22:57 +11:00
|
|
|
if (path != null)
|
|
|
|
|
break;
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
2017-11-03 13:48:36 +11:00
|
|
|
|
2019-01-14 23:22:57 +11:00
|
|
|
if (path == null) {
|
|
|
|
|
path = root.get_child(search_names[0]);
|
|
|
|
|
}
|
2017-11-03 13:48:36 +11:00
|
|
|
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Guessed folder \'%s\' for special_path %s",
|
|
|
|
|
path.to_string(), type.to_string()
|
2019-01-14 23:22:57 +11:00
|
|
|
);
|
|
|
|
|
information.set_special_folder_path(type, path);
|
|
|
|
|
}
|
2017-11-03 13:48:36 +11:00
|
|
|
|
2019-01-14 23:22:57 +11:00
|
|
|
if (!this.folder_map.has_key(path)) {
|
2018-12-29 13:34:29 +11:00
|
|
|
debug("Creating \"%s\" to use as special folder %s",
|
|
|
|
|
path.to_string(), type.to_string());
|
2018-09-26 23:28:43 +10:00
|
|
|
|
2019-01-14 23:22:57 +11:00
|
|
|
GLib.Error? created_err = null;
|
|
|
|
|
try {
|
|
|
|
|
yield remote.create_folder_async(path, type, cancellable);
|
|
|
|
|
} catch (GLib.Error err) {
|
|
|
|
|
// Hang on to the error since the folder might exist
|
|
|
|
|
// on the remote, so try fetching it anyway.
|
|
|
|
|
created_err = err;
|
|
|
|
|
}
|
2018-09-26 23:28:43 +10:00
|
|
|
|
2019-01-14 23:22:57 +11:00
|
|
|
Imap.Folder? remote_folder = null;
|
|
|
|
|
try {
|
|
|
|
|
remote_folder = yield remote.fetch_folder_async(
|
|
|
|
|
path, cancellable
|
|
|
|
|
);
|
|
|
|
|
} catch (GLib.Error err) {
|
|
|
|
|
// If we couldn't fetch it after also failing to
|
|
|
|
|
// create it, it's probably due to the problem
|
|
|
|
|
// creating it, so throw that error instead.
|
|
|
|
|
if (created_err != null) {
|
|
|
|
|
throw created_err;
|
|
|
|
|
} else {
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
2018-09-26 23:28:43 +10:00
|
|
|
}
|
2019-01-14 23:22:57 +11:00
|
|
|
|
|
|
|
|
ImapDB.Folder local_folder =
|
|
|
|
|
yield this.local.clone_folder_async(
|
|
|
|
|
remote_folder, cancellable
|
|
|
|
|
);
|
|
|
|
|
add_folders(
|
|
|
|
|
Collection.single(local_folder), created_err != null
|
|
|
|
|
);
|
2018-09-26 23:28:43 +10:00
|
|
|
}
|
|
|
|
|
|
2019-01-14 23:22:57 +11:00
|
|
|
special= this.folder_map.get(path);
|
|
|
|
|
promote_folders(
|
|
|
|
|
Collection.single_map<SpecialFolderType,Folder>(
|
|
|
|
|
type, special
|
|
|
|
|
)
|
2018-09-26 23:28:43 +10:00
|
|
|
);
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
2017-11-03 13:53:14 +11:00
|
|
|
|
2019-01-14 23:22:57 +11:00
|
|
|
return special;
|
2017-09-25 23:21:33 +10:00
|
|
|
}
|
|
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
/**
|
|
|
|
|
* Constructs a concrete folder implementation.
|
|
|
|
|
*
|
|
|
|
|
* Subclasses should implement this to return their flavor of a
|
|
|
|
|
* MinimalFolder with the appropriate interfaces attached. The
|
|
|
|
|
* returned folder should have its SpecialFolderType set using
|
|
|
|
|
* either the properties from the local folder or its path.
|
|
|
|
|
*
|
|
|
|
|
* This won't be called to build the Outbox or search folder, but
|
|
|
|
|
* for all others (including Inbox) it will.
|
|
|
|
|
*/
|
2017-11-24 15:39:14 +11:00
|
|
|
protected abstract MinimalFolder new_folder(ImapDB.Folder local_folder);
|
|
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
/** {@inheritDoc} */
|
2019-02-19 16:39:11 +11:00
|
|
|
protected override void
|
|
|
|
|
notify_folders_available_unavailable(Gee.BidirSortedSet<Folder>? available,
|
|
|
|
|
Gee.BidirSortedSet<Folder>? unavailable) {
|
2018-01-26 09:52:20 +10:30
|
|
|
base.notify_folders_available_unavailable(available, unavailable);
|
|
|
|
|
if (available != null) {
|
|
|
|
|
foreach (Geary.Folder folder in available) {
|
|
|
|
|
folder.email_appended.connect(notify_email_appended);
|
|
|
|
|
folder.email_inserted.connect(notify_email_inserted);
|
|
|
|
|
folder.email_removed.connect(notify_email_removed);
|
2020-01-08 11:04:51 +01:00
|
|
|
folder.email_locally_removed.connect(notify_email_locally_removed);
|
2018-01-26 09:52:20 +10:30
|
|
|
folder.email_locally_complete.connect(notify_email_locally_complete);
|
|
|
|
|
folder.email_flags_changed.connect(notify_email_flags_changed);
|
|
|
|
|
}
|
2011-07-04 13:54:00 -07:00
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
if (unavailable != null) {
|
|
|
|
|
foreach (Geary.Folder folder in unavailable) {
|
|
|
|
|
folder.email_appended.disconnect(notify_email_appended);
|
|
|
|
|
folder.email_inserted.disconnect(notify_email_inserted);
|
|
|
|
|
folder.email_removed.disconnect(notify_email_removed);
|
2020-01-08 11:04:51 +01:00
|
|
|
folder.email_locally_removed.disconnect(notify_email_locally_removed);
|
2018-01-26 09:52:20 +10:30
|
|
|
folder.email_locally_complete.disconnect(notify_email_locally_complete);
|
|
|
|
|
folder.email_flags_changed.disconnect(notify_email_flags_changed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@inheritDoc} */
|
|
|
|
|
protected override void notify_email_appended(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids) {
|
|
|
|
|
base.notify_email_appended(folder, ids);
|
|
|
|
|
schedule_unseen_update(folder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@inheritDoc} */
|
|
|
|
|
protected override void notify_email_inserted(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids) {
|
|
|
|
|
base.notify_email_inserted(folder, ids);
|
|
|
|
|
schedule_unseen_update(folder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@inheritDoc} */
|
|
|
|
|
protected override void notify_email_removed(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids) {
|
|
|
|
|
base.notify_email_removed(folder, ids);
|
|
|
|
|
schedule_unseen_update(folder);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-08 11:04:51 +01:00
|
|
|
/** {@inheritDoc} */
|
|
|
|
|
protected override void notify_email_locally_removed(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids) {
|
|
|
|
|
base.notify_email_locally_removed(folder, ids);
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
/** {@inheritDoc} */
|
|
|
|
|
protected override void notify_email_flags_changed(Geary.Folder folder,
|
|
|
|
|
Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> flag_map) {
|
|
|
|
|
base.notify_email_flags_changed(folder, flag_map);
|
|
|
|
|
schedule_unseen_update(folder);
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-24 15:39:14 +11:00
|
|
|
/**
|
|
|
|
|
* Hooks up and queues an {@link UpdateRemoteFolders} operation.
|
|
|
|
|
*/
|
|
|
|
|
private void update_remote_folders() {
|
2017-11-28 09:51:53 +11:00
|
|
|
this.refresh_folder_timer.reset();
|
|
|
|
|
|
2017-11-24 15:39:14 +11:00
|
|
|
UpdateRemoteFolders op = new UpdateRemoteFolders(
|
|
|
|
|
this,
|
|
|
|
|
get_supported_special_folders()
|
|
|
|
|
);
|
|
|
|
|
op.completed.connect(() => {
|
|
|
|
|
this.refresh_folder_timer.start();
|
|
|
|
|
});
|
|
|
|
|
try {
|
|
|
|
|
queue_operation(op);
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
// oh well
|
2011-07-04 13:54:00 -07:00
|
|
|
}
|
2011-06-16 16:27:08 -07:00
|
|
|
}
|
2016-06-05 18:36:16 +10:00
|
|
|
|
2015-01-09 15:09:53 -08:00
|
|
|
/**
|
2017-11-24 15:39:14 +11:00
|
|
|
* Hooks up and queues an {@link RefreshFolderUnseen} operation.
|
2015-01-09 15:09:53 -08:00
|
|
|
*/
|
2017-11-24 15:39:14 +11:00
|
|
|
private void schedule_unseen_update(Geary.Folder folder) {
|
|
|
|
|
MinimalFolder? impl = folder as MinimalFolder;
|
|
|
|
|
if (impl != null) {
|
|
|
|
|
impl.refresh_unseen();
|
2015-01-09 15:09:53 -08:00
|
|
|
}
|
2017-11-24 15:39:14 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual Geary.SpecialFolderType[] get_supported_special_folders() {
|
|
|
|
|
return SUPPORTED_SPECIAL_FOLDERS;
|
2015-01-09 15:09:53 -08:00
|
|
|
}
|
2016-06-15 18:24:50 -04:00
|
|
|
|
|
|
|
|
private void compile_special_search_names() {
|
|
|
|
|
/*
|
|
|
|
|
* Compiles the list of names used to search for special
|
|
|
|
|
* folders when they aren't known in advance and the server
|
|
|
|
|
* supports neither SPECIAL-USE not XLIST.
|
|
|
|
|
*
|
|
|
|
|
* Uses both translated and untranslated names in case the
|
|
|
|
|
* server has not localised the folders that match the login
|
|
|
|
|
* session's language. Also checks for lower-case versions of
|
|
|
|
|
* each.
|
|
|
|
|
*/
|
2017-09-25 23:21:33 +10:00
|
|
|
foreach (Geary.SpecialFolderType type in get_supported_special_folders()) {
|
2016-06-15 18:24:50 -04:00
|
|
|
Gee.List<string> compiled = new Gee.ArrayList<string>();
|
|
|
|
|
foreach (string names in get_special_search_names(type)) {
|
|
|
|
|
foreach (string name in names.split("|")) {
|
|
|
|
|
name = name.strip();
|
|
|
|
|
if (name.length != 0) {
|
|
|
|
|
if (!(name in compiled)) {
|
|
|
|
|
compiled.add(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
name = name.down();
|
|
|
|
|
if (!(name in compiled)) {
|
|
|
|
|
compiled.add(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
special_search_names.set(type, compiled);
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
|
|
|
|
}
|
2016-06-15 18:24:50 -04:00
|
|
|
|
|
|
|
|
private Gee.List<string> get_special_search_names(Geary.SpecialFolderType type) {
|
|
|
|
|
Gee.List<string> loc_names = new Gee.ArrayList<string>();
|
|
|
|
|
Gee.List<string> unloc_names = new Gee.ArrayList<string>();
|
|
|
|
|
switch (type) {
|
|
|
|
|
case Geary.SpecialFolderType.DRAFTS:
|
|
|
|
|
// List of general possible folder names to match for the
|
|
|
|
|
// Draft mailbox. Separate names using a vertical bar and
|
|
|
|
|
// put the most common localized name to the front for the
|
|
|
|
|
// default. English names do not need to be included.
|
|
|
|
|
loc_names.add(_("Drafts | Draft"));
|
|
|
|
|
unloc_names.add("Drafts | Draft");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Geary.SpecialFolderType.SENT:
|
|
|
|
|
// List of general possible folder names to match for the
|
|
|
|
|
// Sent mailbox. Separate names using a vertical bar and
|
|
|
|
|
// put the most common localized name to the front for the
|
|
|
|
|
// default. English names do not need to be included.
|
|
|
|
|
loc_names.add(_("Sent | Sent Mail | Sent Email | Sent E-Mail"));
|
|
|
|
|
unloc_names.add("Sent | Sent Mail | Sent Email | Sent E-Mail");
|
|
|
|
|
|
|
|
|
|
// The localised name(s) of the Sent folder name as used
|
|
|
|
|
// by MS Outlook/Exchange.
|
|
|
|
|
loc_names.add(NC_("Outlook localised name", "Sent Items"));
|
|
|
|
|
unloc_names.add("Sent Items");
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Geary.SpecialFolderType.SPAM:
|
|
|
|
|
// List of general possible folder names to match for the
|
|
|
|
|
// Spam mailbox. Separate names using a vertical bar and
|
|
|
|
|
// put the most common localized name to the front for the
|
|
|
|
|
// default. English names do not need to be included.
|
|
|
|
|
loc_names.add(_("Junk | Spam | Junk Mail | Junk Email | Junk E-Mail | Bulk Mail | Bulk Email | Bulk E-Mail"));
|
|
|
|
|
unloc_names.add("Junk | Spam | Junk Mail | Junk Email | Junk E-Mail | Bulk Mail | Bulk Email | Bulk E-Mail");
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Geary.SpecialFolderType.TRASH:
|
|
|
|
|
// List of general possible folder names to match for the
|
|
|
|
|
// Trash mailbox. Separate names using a vertical bar and
|
|
|
|
|
// put the most common localized name to the front for the
|
|
|
|
|
// default. English names do not need to be included.
|
|
|
|
|
loc_names.add(_("Trash | Rubbish | Rubbish Bin"));
|
|
|
|
|
unloc_names.add("Trash | Rubbish | Rubbish Bin");
|
|
|
|
|
|
|
|
|
|
// The localised name(s) of the Trash folder name as used
|
|
|
|
|
// by MS Outlook/Exchange.
|
|
|
|
|
loc_names.add(NC_("Outlook localised name", "Deleted Items"));
|
|
|
|
|
unloc_names.add("Deleted Items");
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case Geary.SpecialFolderType.ARCHIVE:
|
|
|
|
|
// List of general possible folder names to match for the
|
|
|
|
|
// Archive mailbox. Separate names using a vertical bar
|
|
|
|
|
// and put the most common localized name to the front for
|
|
|
|
|
// the default. English names do not need to be included.
|
|
|
|
|
loc_names.add(_("Archive | Archives"));
|
|
|
|
|
unloc_names.add("Archive | Archives");
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loc_names.add_all(unloc_names);
|
|
|
|
|
return loc_names;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
private void check_open() throws EngineError {
|
|
|
|
|
if (!open)
|
|
|
|
|
throw new EngineError.OPEN_REQUIRED("Account %s not opened", to_string());
|
2017-11-23 16:47:19 +11:00
|
|
|
}
|
2016-06-15 18:24:50 -04:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
private void on_operation_error(AccountOperation op, Error error) {
|
2019-03-23 14:22:44 +11:00
|
|
|
notify_service_problem(this.information.incoming, error);
|
2017-11-23 16:47:19 +11:00
|
|
|
}
|
2016-06-15 18:24:50 -04:00
|
|
|
|
2018-12-30 09:03:57 +11:00
|
|
|
private void on_imap_status_notify() {
|
2018-12-29 16:18:39 +11:00
|
|
|
if (this.open) {
|
|
|
|
|
if (this.imap.current_status == CONNECTED) {
|
|
|
|
|
this.remote_ready_lock.blind_notify();
|
|
|
|
|
update_remote_folders();
|
|
|
|
|
} else {
|
|
|
|
|
this.remote_ready_lock.reset();
|
|
|
|
|
this.refresh_folder_timer.reset();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Account operation for loading local folders from the database.
|
|
|
|
|
*/
|
|
|
|
|
internal class Geary.ImapEngine.LoadFolders : AccountOperation {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private weak ImapDB.Account local;
|
|
|
|
|
private Geary.SpecialFolderType[] specials;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal LoadFolders(GenericAccount account,
|
|
|
|
|
ImapDB.Account local,
|
|
|
|
|
Geary.SpecialFolderType[] specials) {
|
2017-12-19 10:49:05 +11:00
|
|
|
base(account);
|
2017-11-23 16:47:19 +11:00
|
|
|
this.local = local;
|
|
|
|
|
this.specials = specials;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override async void execute(Cancellable cancellable) throws Error {
|
2017-12-19 10:49:05 +11:00
|
|
|
GenericAccount generic = (GenericAccount) this.account;
|
2017-11-23 16:47:19 +11:00
|
|
|
Gee.List<ImapDB.Folder> folders = new Gee.LinkedList<ImapDB.Folder>();
|
2018-01-26 09:52:20 +10:30
|
|
|
|
Convert Geary.FolderRoot to be an actual root, not just a top-level
Instead of each top-level IMAP folder being a FolderRoot object, then
children of that being FolderPath objects, this makes FolderRoot an
"empty" FolderPath, so that both top-level and descendant folders are
plain FolderPath objects. Aside from being more technically correct,
this means that empty namespace roots can now be used interchangably
with non-empty namespace roots (addressing issue #181), and custom
folder implementations no longer need to provide their own trivial,
custom FolderRoot.
To support this, a notion of an IMAP root and a local root have been
added from which all remote and local folder paths are now derived,
existing places that assume top-level == root have been fixed, and
unit tests have been added.
2019-01-14 12:10:48 +11:00
|
|
|
yield enumerate_local_folders_async(
|
|
|
|
|
folders, generic.local.imap_folder_root, cancellable
|
|
|
|
|
);
|
2017-12-19 10:49:05 +11:00
|
|
|
generic.add_folders(folders, true);
|
2017-11-23 16:47:19 +11:00
|
|
|
if (!folders.is_empty) {
|
|
|
|
|
// If we have some folders to load, then this isn't the
|
|
|
|
|
// first run, and hence the special folders should already
|
|
|
|
|
// exist
|
2018-01-26 09:52:20 +10:30
|
|
|
yield check_special_folders(cancellable);
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
|
|
|
|
}
|
2017-11-03 13:48:36 +11:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
private async void enumerate_local_folders_async(Gee.List<ImapDB.Folder> folders,
|
Convert Geary.FolderRoot to be an actual root, not just a top-level
Instead of each top-level IMAP folder being a FolderRoot object, then
children of that being FolderPath objects, this makes FolderRoot an
"empty" FolderPath, so that both top-level and descendant folders are
plain FolderPath objects. Aside from being more technically correct,
this means that empty namespace roots can now be used interchangably
with non-empty namespace roots (addressing issue #181), and custom
folder implementations no longer need to provide their own trivial,
custom FolderRoot.
To support this, a notion of an IMAP root and a local root have been
added from which all remote and local folder paths are now derived,
existing places that assume top-level == root have been fixed, and
unit tests have been added.
2019-01-14 12:10:48 +11:00
|
|
|
Geary.FolderPath parent,
|
2017-11-23 16:47:19 +11:00
|
|
|
Cancellable? cancellable)
|
|
|
|
|
throws Error {
|
|
|
|
|
Gee.Collection<ImapDB.Folder>? children = null;
|
|
|
|
|
try {
|
|
|
|
|
children = yield this.local.list_folders_async(parent, cancellable);
|
|
|
|
|
} catch (EngineError err) {
|
|
|
|
|
// don't pass on NOT_FOUND's, that means we need to go to
|
|
|
|
|
// the server for more info
|
|
|
|
|
if (!(err is EngineError.NOT_FOUND))
|
|
|
|
|
throw err;
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
2016-06-05 18:36:16 +10:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
if (children != null) {
|
|
|
|
|
foreach (ImapDB.Folder child in children) {
|
|
|
|
|
folders.add(child);
|
|
|
|
|
yield enumerate_local_folders_async(
|
|
|
|
|
folders, child.get_path(), cancellable
|
|
|
|
|
);
|
|
|
|
|
}
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
2017-11-23 16:47:19 +11:00
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
|
2019-01-14 23:22:57 +11:00
|
|
|
private async void check_special_folders(GLib.Cancellable cancellable)
|
|
|
|
|
throws GLib.Error {
|
|
|
|
|
// Local folders loaded that have the SPECIAL-USE flags set
|
|
|
|
|
// will have been promoted already via derived account type's
|
|
|
|
|
// new_child overrides or some other means. However for those
|
|
|
|
|
// that do not have the flag, check here against the local
|
|
|
|
|
// config and promote ASAP.
|
|
|
|
|
//
|
|
|
|
|
// Can't just use ensure_special_folder_async however since
|
|
|
|
|
// that will attempt to create the folders if missing, which
|
|
|
|
|
// is bad if offline.
|
2018-01-26 09:52:20 +10:30
|
|
|
GenericAccount generic = (GenericAccount) this.account;
|
2019-01-14 23:22:57 +11:00
|
|
|
Gee.Map<Geary.SpecialFolderType,Geary.Folder> added_specials =
|
2018-01-26 09:52:20 +10:30
|
|
|
new Gee.HashMap<Geary.SpecialFolderType,Geary.Folder>();
|
2019-01-14 23:22:57 +11:00
|
|
|
foreach (Geary.SpecialFolderType type in this.specials) {
|
|
|
|
|
if (generic.get_special_folder(type) == null) {
|
|
|
|
|
Geary.FolderPath? path =
|
|
|
|
|
generic.information.get_special_folder_path(type);
|
2019-04-14 20:55:42 +10:00
|
|
|
path = this.local.imap_folder_root.copy(path);
|
2019-01-14 23:22:57 +11:00
|
|
|
if (path != null) {
|
|
|
|
|
try {
|
2019-04-14 20:57:31 +10:00
|
|
|
Geary.Folder target = generic.get_folder(path);
|
2019-01-14 23:22:57 +11:00
|
|
|
added_specials.set(type, target);
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
debug(
|
2018-12-29 13:34:29 +11:00
|
|
|
"Previously used special folder %s not loaded: %s",
|
2019-01-14 23:22:57 +11:00
|
|
|
type.to_string(),
|
|
|
|
|
err.message
|
|
|
|
|
);
|
|
|
|
|
}
|
2018-01-26 09:52:20 +10:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 23:22:57 +11:00
|
|
|
generic.promote_folders(added_specials);
|
2018-01-26 09:52:20 +10:30
|
|
|
}
|
|
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
}
|
2017-11-03 13:53:14 +11:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2018-11-18 23:10:11 +11:00
|
|
|
/**
|
|
|
|
|
* Account operation for starting the outgoing service.
|
|
|
|
|
*/
|
|
|
|
|
internal class Geary.ImapEngine.StartPostie : AccountOperation {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal StartPostie(Account account) {
|
|
|
|
|
base(account);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override async void execute(GLib.Cancellable cancellable)
|
|
|
|
|
throws GLib.Error {
|
|
|
|
|
yield this.account.outgoing.start(cancellable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-12-12 11:19:51 +11:00
|
|
|
/**
|
|
|
|
|
* Account operation for populating the full-text-search table.
|
|
|
|
|
*/
|
|
|
|
|
internal class Geary.ImapEngine.PopulateSearchTable : AccountOperation {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal PopulateSearchTable(GenericAccount account) {
|
|
|
|
|
base(account);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override async void execute(GLib.Cancellable cancellable)
|
|
|
|
|
throws GLib.Error {
|
|
|
|
|
yield ((GenericAccount) this.account).local.populate_search_table(
|
|
|
|
|
cancellable
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
/**
|
|
|
|
|
* Account operation that updates folders from the remote.
|
|
|
|
|
*/
|
|
|
|
|
internal class Geary.ImapEngine.UpdateRemoteFolders : AccountOperation {
|
|
|
|
|
|
|
|
|
|
|
2017-12-19 10:49:05 +11:00
|
|
|
private weak GenericAccount generic_account;
|
2017-11-23 16:47:19 +11:00
|
|
|
private Geary.SpecialFolderType[] specials;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal UpdateRemoteFolders(GenericAccount account,
|
|
|
|
|
Geary.SpecialFolderType[] specials) {
|
2017-12-19 10:49:05 +11:00
|
|
|
base(account);
|
|
|
|
|
this.generic_account = account;
|
2017-11-23 16:47:19 +11:00
|
|
|
this.specials = specials;
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
2017-11-03 13:53:14 +11:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
public override async void execute(Cancellable cancellable) throws Error {
|
|
|
|
|
Gee.Map<FolderPath, Geary.Folder> existing_folders =
|
|
|
|
|
Geary.traverse<Geary.Folder>(this.account.list_folders())
|
|
|
|
|
.to_hash_map<FolderPath>(f => f.path);
|
|
|
|
|
Gee.Map<FolderPath, Imap.Folder> remote_folders =
|
|
|
|
|
new Gee.HashMap<FolderPath, Imap.Folder>();
|
2018-01-26 09:52:20 +10:30
|
|
|
|
2018-02-27 01:03:21 +11:00
|
|
|
GenericAccount account = (GenericAccount) this.account;
|
|
|
|
|
Imap.AccountSession remote = yield account.claim_account_session(
|
|
|
|
|
cancellable
|
2017-11-23 16:47:19 +11:00
|
|
|
);
|
2018-02-27 01:03:21 +11:00
|
|
|
try {
|
|
|
|
|
bool is_suspect = yield enumerate_remote_folders_async(
|
Convert Geary.FolderRoot to be an actual root, not just a top-level
Instead of each top-level IMAP folder being a FolderRoot object, then
children of that being FolderPath objects, this makes FolderRoot an
"empty" FolderPath, so that both top-level and descendant folders are
plain FolderPath objects. Aside from being more technically correct,
this means that empty namespace roots can now be used interchangably
with non-empty namespace roots (addressing issue #181), and custom
folder implementations no longer need to provide their own trivial,
custom FolderRoot.
To support this, a notion of an IMAP root and a local root have been
added from which all remote and local folder paths are now derived,
existing places that assume top-level == root have been fixed, and
unit tests have been added.
2019-01-14 12:10:48 +11:00
|
|
|
remote,
|
|
|
|
|
remote_folders,
|
|
|
|
|
account.local.imap_folder_root,
|
|
|
|
|
cancellable
|
2018-02-27 01:03:21 +11:00
|
|
|
);
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2019-01-14 18:08:31 +11:00
|
|
|
debug("Existing folders:");
|
|
|
|
|
foreach (FolderPath path in existing_folders.keys) {
|
|
|
|
|
debug(" - %s (%u)", path.to_string(), path.hash());
|
|
|
|
|
}
|
|
|
|
|
debug("Remote folders:");
|
|
|
|
|
foreach (FolderPath path in remote_folders.keys) {
|
|
|
|
|
debug(" - %s (%u)", path.to_string(), path.hash());
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-27 01:03:21 +11:00
|
|
|
// pair the local and remote folders and make sure
|
|
|
|
|
// everything is up-to-date
|
|
|
|
|
yield update_folders_async(
|
|
|
|
|
remote, existing_folders, remote_folders, is_suspect, cancellable
|
|
|
|
|
);
|
|
|
|
|
} finally {
|
|
|
|
|
account.release_account_session(remote);
|
|
|
|
|
}
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
2016-06-05 18:36:16 +10:00
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
private async bool enumerate_remote_folders_async(Imap.AccountSession remote,
|
|
|
|
|
Gee.Map<FolderPath,Imap.Folder> folders,
|
2017-11-23 16:47:19 +11:00
|
|
|
Geary.FolderPath? parent,
|
|
|
|
|
Cancellable? cancellable)
|
|
|
|
|
throws Error {
|
|
|
|
|
bool results_suspect = false;
|
|
|
|
|
|
|
|
|
|
Gee.List<Imap.Folder>? children = null;
|
|
|
|
|
try {
|
2018-01-26 09:52:20 +10:30
|
|
|
children = yield remote.fetch_child_folders_async(parent, cancellable);
|
2017-11-23 16:47:19 +11:00
|
|
|
} catch (Error err) {
|
|
|
|
|
// ignore everything but I/O and IMAP errors (cancellation is an IOError)
|
|
|
|
|
if (err is IOError || err is ImapError)
|
|
|
|
|
throw err;
|
|
|
|
|
debug("Ignoring error listing child folders of %s: %s",
|
2018-12-29 13:34:29 +11:00
|
|
|
(parent != null ? parent.to_string() : "root"), err.message);
|
2017-11-23 16:47:19 +11:00
|
|
|
results_suspect = true;
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
2016-06-05 18:36:16 +10:00
|
|
|
|
2017-11-23 16:47:19 +11:00
|
|
|
if (children != null) {
|
|
|
|
|
foreach (Imap.Folder child in children) {
|
|
|
|
|
FolderPath path = child.path;
|
|
|
|
|
folders.set(path, child);
|
|
|
|
|
if (child.properties.has_children.is_possible() &&
|
2018-01-26 09:52:20 +10:30
|
|
|
yield enumerate_remote_folders_async(
|
|
|
|
|
remote, folders, path, cancellable)) {
|
2017-11-23 16:47:19 +11:00
|
|
|
results_suspect = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results_suspect;
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
2016-06-05 18:36:16 +10:00
|
|
|
|
2018-01-26 09:52:20 +10:30
|
|
|
private async void update_folders_async(Imap.AccountSession remote,
|
|
|
|
|
Gee.Map<FolderPath,Geary.Folder> existing_folders,
|
|
|
|
|
Gee.Map<FolderPath,Imap.Folder> remote_folders,
|
|
|
|
|
bool remote_folders_suspect,
|
|
|
|
|
Cancellable? cancellable) {
|
|
|
|
|
// update all remote folders properties in the local store and
|
|
|
|
|
// active in the system
|
2013-04-24 12:22:32 -07:00
|
|
|
Gee.HashSet<Geary.FolderPath> altered_paths = new Gee.HashSet<Geary.FolderPath>();
|
2013-05-31 17:56:48 -07:00
|
|
|
foreach (Imap.Folder remote_folder in remote_folders.values) {
|
2014-02-11 15:24:01 -08:00
|
|
|
MinimalFolder? minimal_folder = existing_folders.get(remote_folder.path)
|
|
|
|
|
as MinimalFolder;
|
|
|
|
|
if (minimal_folder == null)
|
2013-05-31 17:56:48 -07:00
|
|
|
continue;
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2013-02-25 20:18:37 -08:00
|
|
|
// only worry about alterations if the remote is openable
|
2013-04-25 14:47:20 -07:00
|
|
|
if (remote_folder.properties.is_openable.is_possible()) {
|
2014-02-11 15:24:01 -08:00
|
|
|
ImapDB.Folder local_folder = minimal_folder.local_folder;
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2013-08-09 15:17:06 -07:00
|
|
|
if (remote_folder.properties.have_contents_changed(local_folder.get_properties(),
|
2014-02-11 15:24:01 -08:00
|
|
|
minimal_folder.to_string())) {
|
2013-05-31 17:56:48 -07:00
|
|
|
altered_paths.add(remote_folder.path);
|
2013-08-09 15:17:06 -07:00
|
|
|
}
|
2013-02-25 20:18:37 -08:00
|
|
|
}
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2013-09-03 15:09:39 -07:00
|
|
|
// always update, openable or not; have the folder update the UID info the next time
|
|
|
|
|
// it's opened
|
2012-06-22 14:42:51 -07:00
|
|
|
try {
|
2018-01-18 17:33:49 +11:00
|
|
|
yield minimal_folder.local_folder.update_folder_status(
|
2019-04-04 20:50:48 +11:00
|
|
|
remote_folder.properties, false, cancellable
|
2018-01-18 17:33:49 +11:00
|
|
|
);
|
2012-06-22 14:42:51 -07:00
|
|
|
} catch (Error update_error) {
|
|
|
|
|
debug("Unable to update local folder %s with remote properties: %s",
|
2018-01-26 09:52:20 +10:30
|
|
|
remote_folder.path.to_string(), update_error.message);
|
2012-06-22 14:42:51 -07:00
|
|
|
}
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2018-09-26 23:28:43 +10:00
|
|
|
// set the engine folder's special type (but only promote,
|
|
|
|
|
// not demote, since getting the special folder type via
|
|
|
|
|
// its properties relies on the optional SPECIAL-USE or
|
|
|
|
|
// XLIST extensions) use this iteration to add discovered
|
|
|
|
|
// properties to map
|
2014-02-11 15:24:01 -08:00
|
|
|
if (minimal_folder.special_folder_type == SpecialFolderType.NONE)
|
|
|
|
|
minimal_folder.set_special_folder_type(remote_folder.properties.attrs.get_special_folder_type());
|
2012-05-10 20:28:42 -07:00
|
|
|
}
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2012-06-22 14:42:51 -07:00
|
|
|
// If path in remote but not local, need to add it
|
2013-12-12 12:42:02 -08:00
|
|
|
Gee.ArrayList<Imap.Folder> to_add = Geary.traverse<Imap.Folder>(remote_folders.values)
|
|
|
|
|
.filter(f => !existing_folders.has_key(f.path))
|
|
|
|
|
.to_array_list();
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2019-12-13 12:12:07 +11:00
|
|
|
// Remove if path in local but not remote
|
2013-12-12 12:42:02 -08:00
|
|
|
Gee.ArrayList<Geary.Folder> to_remove
|
2016-10-04 00:06:54 +11:00
|
|
|
= Geary.traverse<Gee.Map.Entry<FolderPath,Geary.Folder>>(existing_folders)
|
2019-12-13 12:12:07 +11:00
|
|
|
.filter(e => !remote_folders.has_key(e.key))
|
2013-12-12 12:42:02 -08:00
|
|
|
.map<Geary.Folder>(e => (Geary.Folder) e.value)
|
|
|
|
|
.to_array_list();
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2018-09-25 23:59:28 +10:00
|
|
|
// For folders to add, clone them and their properties
|
|
|
|
|
// locally, then add to the account
|
2018-01-26 09:52:20 +10:30
|
|
|
ImapDB.Account local = ((GenericAccount) this.account).local;
|
2017-11-23 16:47:19 +11:00
|
|
|
Gee.ArrayList<ImapDB.Folder> to_build = new Gee.ArrayList<ImapDB.Folder>();
|
2013-05-31 17:56:48 -07:00
|
|
|
foreach (Geary.Imap.Folder remote_folder in to_add) {
|
|
|
|
|
try {
|
2018-09-25 23:59:28 +10:00
|
|
|
to_build.add(
|
|
|
|
|
yield local.clone_folder_async(remote_folder, cancellable)
|
|
|
|
|
);
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
debug("Unable to clone folder %s in local store: %s",
|
|
|
|
|
remote_folder.path.to_string(),
|
|
|
|
|
err.message);
|
2011-06-16 16:27:08 -07:00
|
|
|
}
|
|
|
|
|
}
|
2017-12-19 10:49:05 +11:00
|
|
|
this.generic_account.add_folders(to_build, false);
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2014-03-11 12:12:37 -07:00
|
|
|
if (remote_folders_suspect) {
|
|
|
|
|
debug("Skipping removing folders due to prior errors");
|
|
|
|
|
} else {
|
2019-02-19 16:39:11 +11:00
|
|
|
Gee.BidirSortedSet<MinimalFolder> removed =
|
2017-12-19 10:49:05 +11:00
|
|
|
this.generic_account.remove_folders(to_remove);
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2019-02-19 16:39:11 +11:00
|
|
|
Gee.BidirIterator<MinimalFolder> removed_iterator =
|
|
|
|
|
removed.bidir_iterator();
|
2019-07-17 15:06:41 +10:00
|
|
|
bool has_prev = removed_iterator.last();
|
|
|
|
|
while (has_prev) {
|
2019-02-19 16:39:11 +11:00
|
|
|
MinimalFolder folder = removed_iterator.get();
|
|
|
|
|
|
2014-03-11 12:12:37 -07:00
|
|
|
try {
|
|
|
|
|
debug("Locally deleting removed folder %s", folder.to_string());
|
2019-01-14 11:01:03 +11:00
|
|
|
yield local.delete_folder_async(folder.path, cancellable);
|
2014-03-11 12:12:37 -07:00
|
|
|
} catch (Error e) {
|
|
|
|
|
debug("Unable to locally delete removed folder %s: %s", folder.to_string(), e.message);
|
|
|
|
|
}
|
2019-07-17 15:06:41 +10:00
|
|
|
|
|
|
|
|
has_prev = removed_iterator.previous();
|
2014-02-14 16:03:19 -08:00
|
|
|
}
|
2017-11-23 16:47:19 +11:00
|
|
|
|
|
|
|
|
// Let the remote know as well
|
2018-01-26 09:52:20 +10:30
|
|
|
remote.folders_removed(
|
2017-11-23 16:47:19 +11:00
|
|
|
Geary.traverse<Geary.Folder>(removed)
|
|
|
|
|
.map<FolderPath>(f => f.path).to_array_list()
|
|
|
|
|
);
|
2014-02-14 16:03:19 -08:00
|
|
|
}
|
2017-11-23 16:47:19 +11:00
|
|
|
|
2013-02-25 20:18:37 -08:00
|
|
|
// report all altered folders
|
|
|
|
|
if (altered_paths.size > 0) {
|
|
|
|
|
Gee.ArrayList<Geary.Folder> altered = new Gee.ArrayList<Geary.Folder>();
|
2013-05-31 17:56:48 -07:00
|
|
|
foreach (Geary.FolderPath altered_path in altered_paths) {
|
|
|
|
|
if (existing_folders.has_key(altered_path))
|
|
|
|
|
altered.add(existing_folders.get(altered_path));
|
2013-02-25 20:18:37 -08:00
|
|
|
else
|
2013-05-31 17:56:48 -07:00
|
|
|
debug("Unable to report %s altered: no local representation", altered_path.to_string());
|
2013-02-25 20:18:37 -08:00
|
|
|
}
|
2017-12-19 10:49:05 +11:00
|
|
|
this.generic_account.update_folders(altered);
|
2014-02-11 15:24:01 -08:00
|
|
|
}
|
2012-06-25 16:30:23 -07:00
|
|
|
|
2017-11-03 13:56:07 +11:00
|
|
|
// Ensure each of the important special folders we need already exist
|
2017-11-23 16:47:19 +11:00
|
|
|
foreach (Geary.SpecialFolderType special in this.specials) {
|
2017-11-03 13:56:07 +11:00
|
|
|
try {
|
2018-09-26 23:28:43 +10:00
|
|
|
yield this.generic_account.ensure_special_folder_async(
|
|
|
|
|
remote, special, cancellable
|
|
|
|
|
);
|
2017-11-03 13:56:07 +11:00
|
|
|
} catch (Error e) {
|
|
|
|
|
warning("Unable to ensure special folder %s: %s", special.to_string(), e.message);
|
|
|
|
|
}
|
2013-08-19 16:39:53 -07:00
|
|
|
}
|
2013-06-18 11:26:44 -07:00
|
|
|
}
|
2016-10-26 10:27:02 +02:00
|
|
|
|
2011-06-16 16:27:08 -07:00
|
|
|
}
|
2012-06-25 16:30:23 -07:00
|
|
|
|
2017-11-24 15:39:14 +11:00
|
|
|
/**
|
|
|
|
|
* Account operation that updates a folder's unseen message count.
|
|
|
|
|
*
|
|
|
|
|
* This performs a IMAP STATUS on the folder, but only if it is not
|
|
|
|
|
* open - if it is open it is already maintaining its unseen count.
|
|
|
|
|
*/
|
2017-12-19 10:49:05 +11:00
|
|
|
internal class Geary.ImapEngine.RefreshFolderUnseen : FolderOperation {
|
2017-11-24 15:39:14 +11:00
|
|
|
|
|
|
|
|
|
2017-11-28 10:12:41 +11:00
|
|
|
internal RefreshFolderUnseen(MinimalFolder folder,
|
2018-01-19 08:44:08 +11:00
|
|
|
GenericAccount account) {
|
2017-12-19 10:49:05 +11:00
|
|
|
base(account, folder);
|
2013-01-31 15:17:44 -08:00
|
|
|
}
|
2016-10-26 10:27:02 +02:00
|
|
|
|
2017-11-24 15:39:14 +11:00
|
|
|
public override async void execute(Cancellable cancellable) throws Error {
|
2018-02-27 01:03:21 +11:00
|
|
|
GenericAccount account = (GenericAccount) this.account;
|
2017-11-24 15:39:14 +11:00
|
|
|
if (this.folder.get_open_state() == Geary.Folder.OpenState.CLOSED) {
|
2018-02-27 01:03:21 +11:00
|
|
|
Imap.AccountSession? remote = yield account.claim_account_session(
|
2018-01-26 09:52:20 +10:30
|
|
|
cancellable
|
2017-01-12 01:20:13 +11:00
|
|
|
);
|
2016-10-26 10:27:02 +02:00
|
|
|
try {
|
2018-02-27 01:03:21 +11:00
|
|
|
Imap.Folder remote_folder = yield remote.fetch_folder_async(
|
|
|
|
|
folder.path,
|
|
|
|
|
cancellable
|
|
|
|
|
);
|
2018-01-18 17:33:49 +11:00
|
|
|
|
2018-02-27 01:03:21 +11:00
|
|
|
// Implementation-specific hack: Although this is called
|
|
|
|
|
// when the MinimalFolder is closed, we can safely use
|
|
|
|
|
// local_folder since we are only using its properties,
|
|
|
|
|
// and the properties were loaded when the folder was
|
|
|
|
|
// first instantiated.
|
|
|
|
|
ImapDB.Folder local_folder = ((MinimalFolder) this.folder).local_folder;
|
2018-01-18 17:33:49 +11:00
|
|
|
|
2018-02-27 01:03:21 +11:00
|
|
|
if (remote_folder.properties.have_contents_changed(
|
|
|
|
|
local_folder.get_properties(),
|
|
|
|
|
this.folder.to_string())) {
|
2018-01-18 17:33:49 +11:00
|
|
|
|
2018-02-27 01:03:21 +11:00
|
|
|
yield local_folder.update_folder_status(
|
2019-04-04 20:50:48 +11:00
|
|
|
remote_folder.properties, true, cancellable
|
2018-02-27 01:03:21 +11:00
|
|
|
);
|
2017-11-28 10:12:41 +11:00
|
|
|
|
2018-02-27 01:03:21 +11:00
|
|
|
((GenericAccount) this.account).update_folder(this.folder);
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
account.release_account_session(remote);
|
2016-10-26 10:27:02 +02:00
|
|
|
}
|
2013-01-31 15:17:44 -08:00
|
|
|
}
|
2011-12-22 12:31:06 -08:00
|
|
|
}
|
2011-06-16 16:27:08 -07:00
|
|
|
|
|
|
|
|
}
|