[geary: 1/2] Remove trailing whitespace
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary: 1/2] Remove trailing whitespace
- Date: Fri, 1 Mar 2019 07:07:54 +0000 (UTC)
commit 48c93655cfec47c003424b4fc11efa1e990070dc
Author: Kristian Klausen <kristian klausen dk>
Date: Thu Feb 28 09:38:56 2019 +0100
Remove trailing whitespace
find . -name '*.vala' -type f -exec sed -i 's/ *$//g' {} +
The following files was ignored:
test/client/composer/composer-web-view-test.vala
test/engine/util-html-test.vala
Fix #271
src/client/application/application-command.vala | 2 +-
src/client/application/geary-application.vala | 48 +-
src/client/application/geary-args.vala | 26 +-
src/client/application/geary-config.vala | 42 +-
src/client/application/geary-controller.vala | 150 ++---
src/client/components/count-badge.vala | 36 +-
src/client/components/icon-factory.vala | 44 +-
src/client/components/monitored-progress-bar.vala | 10 +-
src/client/components/monitored-spinner.vala | 8 +-
src/client/components/search-bar.vala | 34 +-
src/client/components/status-bar.vala | 28 +-
src/client/composer/composer-widget.vala | 20 +-
src/client/composer/contact-entry-completion.vala | 54 +-
src/client/composer/contact-list-store.vala | 16 +-
src/client/composer/email-entry.vala | 16 +-
.../conversation-list-cell-renderer.vala | 12 +-
.../conversation-list/conversation-list-store.vala | 144 ++---
.../conversation-list/conversation-list-view.vala | 96 +--
.../formatted-conversation-data.vala | 122 ++--
src/client/dialogs/alert-dialog.vala | 40 +-
src/client/dialogs/attachment-dialog.vala | 16 +-
src/client/dialogs/certificate-warning-dialog.vala | 38 +-
src/client/dialogs/password-dialog.vala | 22 +-
src/client/dialogs/upgrade-dialog.vala | 20 +-
.../folder-list-abstract-folder-entry.vala | 12 +-
.../folder-list/folder-list-account-branch.vala | 30 +-
.../folder-list/folder-list-folder-entry.vala | 54 +-
.../folder-list/folder-list-inboxes-branch.vala | 20 +-
.../folder-list/folder-list-search-branch.vala | 20 +-
.../folder-list/folder-list-special-grouping.vala | 4 +-
src/client/folder-list/folder-list-tree.vala | 72 +--
src/client/notification/libmessagingmenu.vala | 30 +-
src/client/notification/libnotify.vala | 52 +-
.../notification/new-messages-indicator.vala | 22 +-
src/client/notification/new-messages-monitor.vala | 100 ++--
src/client/notification/null-indicator.vala | 2 +-
src/client/notification/unity-launcher.vala | 18 +-
src/client/sidebar/sidebar-branch.vala | 218 +++----
src/client/sidebar/sidebar-common.vala | 26 +-
.../sidebar/sidebar-count-cell-renderer.vala | 20 +-
src/client/sidebar/sidebar-entry.vala | 24 +-
src/client/sidebar/sidebar-tree.vala | 500 ++++++++--------
src/client/util/util-date.vala | 48 +-
src/client/util/util-email.vala | 6 +-
src/engine/api/geary-abstract-local-folder.vala | 22 +-
src/engine/api/geary-account.vala | 50 +-
.../api/geary-aggregated-folder-properties.vala | 10 +-
src/engine/api/geary-composed-email.vala | 6 +-
src/engine/api/geary-contact-flags.vala | 18 +-
src/engine/api/geary-contact-importance.vala | 2 +-
src/engine/api/geary-contact-store.vala | 16 +-
src/engine/api/geary-contact.vala | 8 +-
src/engine/api/geary-email-flags.vala | 18 +-
src/engine/api/geary-email-identifier.vala | 28 +-
src/engine/api/geary-email-properties.vala | 6 +-
src/engine/api/geary-email.vala | 134 ++---
src/engine/api/geary-engine.vala | 4 +-
src/engine/api/geary-folder-properties.vala | 18 +-
src/engine/api/geary-folder.vala | 64 +-
src/engine/api/geary-named-flag.vala | 12 +-
src/engine/api/geary-named-flags.vala | 44 +-
src/engine/api/geary-progress-monitor.vala | 76 +--
src/engine/api/geary-revokable.vala | 50 +-
src/engine/api/geary-search-folder.vala | 20 +-
src/engine/api/geary-search-query.vala | 6 +-
src/engine/api/geary-special-folder-type.vala | 26 +-
src/engine/app/app-draft-manager.vala | 130 ++--
src/engine/app/app-email-store.vala | 48 +-
.../app-conversation-operation-queue.vala | 6 +-
.../app-local-search-operation.vala | 8 +-
.../app-terminate-operation.vala | 2 +-
.../email-store/app-async-folder-operation.vala | 2 +-
src/engine/app/email-store/app-copy-operation.vala | 8 +-
.../app/email-store/app-fetch-operation.vala | 8 +-
src/engine/app/email-store/app-list-operation.vala | 6 +-
src/engine/app/email-store/app-mark-operation.vala | 8 +-
src/engine/common/common-message-data.vala | 38 +-
src/engine/db/db-connection.vala | 108 ++--
src/engine/db/db-context.vala | 14 +-
src/engine/db/db-database.vala | 38 +-
src/engine/db/db-result.vala | 102 ++--
src/engine/db/db-statement.vala | 100 ++--
src/engine/db/db-synchronous-mode.vala | 12 +-
src/engine/db/db-transaction-async-job.vala | 22 +-
src/engine/db/db-transaction-outcome.vala | 12 +-
src/engine/db/db-transaction-type.vala | 16 +-
src/engine/db/db-versioned-database.vala | 4 +-
src/engine/db/db.vala | 22 +-
src/engine/imap-db/imap-db-account.vala | 362 ++++++------
src/engine/imap-db/imap-db-contact.vala | 16 +-
src/engine/imap-db/imap-db-database.vala | 28 +-
src/engine/imap-db/imap-db-email-identifier.vala | 28 +-
src/engine/imap-db/imap-db-folder.vala | 654 ++++++++++-----------
src/engine/imap-db/imap-db-gc.vala | 212 +++----
src/engine/imap-db/imap-db-message-addresses.vala | 32 +-
src/engine/imap-db/imap-db-message-row.vala | 118 ++--
.../search/imap-db-search-email-identifier.vala | 30 +-
.../search/imap-db-search-folder-properties.vala | 2 +-
.../imap-db/search/imap-db-search-folder.vala | 114 ++--
.../imap-db/search/imap-db-search-query.vala | 22 +-
src/engine/imap-db/search/imap-db-search-term.vala | 14 +-
.../gmail/imap-engine-gmail-folder.vala | 8 +-
.../imap-engine/imap-engine-contact-store.vala | 12 +-
.../imap-engine/imap-engine-email-prefetcher.vala | 40 +-
.../imap-engine/imap-engine-generic-account.vala | 26 +-
.../imap-engine/imap-engine-minimal-folder.vala | 186 +++---
.../imap-engine/imap-engine-replay-operation.vala | 22 +-
.../imap-engine/imap-engine-replay-queue.vala | 170 +++---
.../imap-engine-revokable-committed-move.vala | 2 +-
.../imap-engine/imap-engine-revokable-move.vala | 18 +-
.../imap-engine-abstract-list-email.vala | 56 +-
.../replay-ops/imap-engine-copy-email.vala | 10 +-
.../replay-ops/imap-engine-create-email.vala | 8 +-
.../replay-ops/imap-engine-empty-folder.vala | 16 +-
.../replay-ops/imap-engine-fetch-email.vala | 20 +-
.../replay-ops/imap-engine-list-email-by-id.vala | 6 +-
.../imap-engine-list-email-by-sparse-id.vala | 32 +-
.../replay-ops/imap-engine-mark-email.vala | 22 +-
.../replay-ops/imap-engine-move-email-commit.vala | 12 +-
.../replay-ops/imap-engine-move-email-prepare.vala | 16 +-
.../replay-ops/imap-engine-move-email-revoke.vala | 14 +-
.../replay-ops/imap-engine-remove-email.vala | 22 +-
.../replay-ops/imap-engine-replay-append.vala | 8 +-
.../replay-ops/imap-engine-replay-removal.vala | 10 +-
.../replay-ops/imap-engine-replay-update.vala | 2 +-
.../imap-engine-server-search-email.vala | 12 +-
src/engine/imap/api/imap-account-session.vala | 4 +-
src/engine/imap/api/imap-email-flags.vala | 48 +-
src/engine/imap/api/imap-email-properties.vala | 16 +-
src/engine/imap/api/imap-folder-properties.vala | 38 +-
src/engine/imap/api/imap-folder-session.vala | 174 +++---
.../imap/command/imap-capability-command.vala | 2 +-
src/engine/imap/command/imap-close-command.vala | 2 +-
src/engine/imap/command/imap-compress-command.vala | 4 +-
.../imap/command/imap-list-return-parameter.vala | 4 +-
src/engine/imap/command/imap-login-command.vala | 4 +-
src/engine/imap/command/imap-logout-command.vala | 2 +-
src/engine/imap/command/imap-message-set.vala | 134 ++---
src/engine/imap/command/imap-noop-command.vala | 2 +-
src/engine/imap/command/imap-search-criteria.vala | 16 +-
src/engine/imap/command/imap-search-criterion.vala | 60 +-
src/engine/imap/command/imap-starttls-command.vala | 2 +-
src/engine/imap/imap.vala | 2 +-
src/engine/imap/message/imap-data-format.vala | 10 +-
.../message/imap-fetch-body-data-specifier.vala | 122 ++--
.../imap/message/imap-fetch-data-specifier.vala | 76 +--
src/engine/imap/message/imap-flag.vala | 6 +-
src/engine/imap/message/imap-flags.vala | 28 +-
src/engine/imap/message/imap-internal-date.vala | 50 +-
.../imap/message/imap-mailbox-specifier.vala | 34 +-
src/engine/imap/message/imap-message-flag.vala | 52 +-
src/engine/imap/message/imap-message-flags.vala | 16 +-
src/engine/imap/message/imap-sequence-number.vala | 26 +-
src/engine/imap/message/imap-status-data-type.vala | 18 +-
src/engine/imap/message/imap-tag.vala | 46 +-
src/engine/imap/message/imap-uid-validity.vala | 12 +-
src/engine/imap/message/imap-uid.vala | 22 +-
src/engine/imap/parameter/imap-list-parameter.vala | 110 ++--
.../imap/parameter/imap-literal-parameter.vala | 10 +-
src/engine/imap/parameter/imap-nil-parameter.vala | 8 +-
.../imap/parameter/imap-number-parameter.vala | 34 +-
.../imap/parameter/imap-root-parameters.vala | 14 +-
.../imap/parameter/imap-string-parameter.vala | 48 +-
src/engine/imap/response/imap-capabilities.vala | 6 +-
.../imap/response/imap-continuation-response.vala | 8 +-
.../imap/response/imap-fetch-data-decoder.vala | 52 +-
src/engine/imap/response/imap-fetched-data.vala | 42 +-
.../imap/response/imap-mailbox-attribute.vala | 68 +--
.../imap/response/imap-mailbox-attributes.vala | 38 +-
.../imap/response/imap-mailbox-information.vala | 18 +-
.../imap/response/imap-response-code-type.vala | 22 +-
src/engine/imap/response/imap-response-code.vala | 30 +-
.../imap/response/imap-server-data-type.vala | 76 +--
src/engine/imap/response/imap-server-data.vala | 54 +-
src/engine/imap/response/imap-server-response.vala | 16 +-
src/engine/imap/response/imap-status-data.vala | 34 +-
src/engine/imap/response/imap-status-response.vala | 26 +-
src/engine/imap/response/imap-status.vala | 26 +-
.../imap/transport/imap-client-connection.vala | 36 +-
src/engine/imap/transport/imap-client-session.vala | 500 ++++++++--------
src/engine/imap/transport/imap-deserializer.vala | 188 +++---
src/engine/memory/memory-buffer.vala | 20 +-
src/engine/memory/memory-byte-buffer.vala | 22 +-
src/engine/memory/memory-empty-buffer.vala | 16 +-
src/engine/memory/memory-file-buffer.vala | 14 +-
src/engine/memory/memory-growable-buffer.vala | 90 +--
src/engine/memory/memory-offset-buffer.vala | 10 +-
src/engine/memory/memory-string-buffer.vala | 16 +-
.../memory/memory-unowned-string-buffer.vala | 4 +-
src/engine/mime/mime-content-disposition.vala | 22 +-
src/engine/mime/mime-content-parameters.vala | 16 +-
src/engine/mime/mime-content-type.vala | 40 +-
src/engine/mime/mime-data-format.vala | 8 +-
src/engine/mime/mime-disposition-type.vala | 26 +-
src/engine/mime/mime-multipart-subtype.vala | 14 +-
src/engine/nonblocking/nonblocking-batch.vala | 88 +--
src/engine/nonblocking/nonblocking-concurrent.vala | 40 +-
.../nonblocking-counting-semaphore.vala | 20 +-
.../nonblocking-reporting-semaphore.vala | 24 +-
.../rfc822/rfc822-gmime-filter-blockquotes.vala | 44 +-
src/engine/rfc822/rfc822-gmime-filter-plain.vala | 26 +-
src/engine/rfc822/rfc822-message-data.vala | 96 +--
src/engine/rfc822/rfc822-message.vala | 76 +--
src/engine/rfc822/rfc822-utils.vala | 42 +-
src/engine/smtp/smtp-authenticator.vala | 16 +-
src/engine/smtp/smtp-capabilities.vala | 8 +-
src/engine/smtp/smtp-client-connection.vala | 98 +--
src/engine/smtp/smtp-client-session.vala | 56 +-
src/engine/smtp/smtp-command.vala | 40 +-
src/engine/smtp/smtp-greeting.vala | 24 +-
src/engine/smtp/smtp-login-authenticator.vala | 8 +-
src/engine/smtp/smtp-plain-authenticator.vala | 10 +-
src/engine/smtp/smtp-request.vala | 20 +-
src/engine/smtp/smtp-response-code.vala | 48 +-
src/engine/smtp/smtp-response-line.vala | 20 +-
src/engine/smtp/smtp-response.vala | 12 +-
src/engine/state/state-machine-descriptor.vala | 10 +-
src/engine/state/state-machine.vala | 62 +-
src/engine/state/state-mapping.vala | 2 +-
src/engine/util/util-ascii.vala | 12 +-
src/engine/util/util-collection.vala | 30 +-
src/engine/util/util-files.vala | 16 +-
src/engine/util/util-generic-capabilities.vala | 44 +-
src/engine/util/util-imap-utf7.vala | 34 +-
src/engine/util/util-iterable.vala | 16 +-
src/engine/util/util-object.vala | 10 +-
src/engine/util/util-reference-semantics.vala | 28 +-
src/engine/util/util-scheduler.vala | 44 +-
src/engine/util/util-string.vala | 4 +-
src/engine/util/util-synchronization.vala | 38 +-
src/engine/util/util-time.vala | 2 +-
src/engine/util/util-trillian.vala | 34 +-
src/mailer/main.vala | 52 +-
test/engine/app/app-conversation-monitor-test.vala | 6 +-
234 files changed, 5065 insertions(+), 5065 deletions(-)
---
diff --git a/src/client/application/application-command.vala b/src/client/application/application-command.vala
index 1f120246..796b0102 100644
--- a/src/client/application/application-command.vala
+++ b/src/client/application/application-command.vala
@@ -80,7 +80,7 @@ public abstract class Application.Command : GLib.Object {
*/
public abstract async void undo(GLib.Cancellable? cancellable)
throws GLib.Error;
-
+
/**
* Called by {@link CommandStack} to redo the executed command.
*
diff --git a/src/client/application/geary-application.vala b/src/client/application/geary-application.vala
index 671285f0..76b41def 100644
--- a/src/client/application/geary-application.vala
+++ b/src/client/application/geary-application.vala
@@ -152,13 +152,13 @@ public class GearyApplication : Gtk.Application {
public override bool local_command_line(ref unowned string[] args, out int exit_status) {
bin = args[0];
exec_dir = (File.new_for_path(Posix.realpath(Environment.find_program_in_path(bin)))).get_parent();
-
+
try {
register();
} catch (Error e) {
error("Error registering GearyApplication: %s", e.message);
}
-
+
if (!Args.parse(args)) {
exit_status = 1;
return true;
@@ -186,10 +186,10 @@ public class GearyApplication : Gtk.Application {
exit_status = 0;
return true;
}
-
+
public override void startup() {
Configuration.init(is_installed(), GSETTINGS_DIR);
-
+
Environment.set_application_name(NAME);
Environment.set_prgname(PRGNAME);
International.init(GETTEXT_PACKAGE, bin);
@@ -208,10 +208,10 @@ public class GearyApplication : Gtk.Application {
add_action_entries(action_entries, this);
}
-
+
public override void activate() {
base.activate();
-
+
if (!present())
create_async.begin();
}
@@ -238,7 +238,7 @@ public class GearyApplication : Gtk.Application {
// Without this, the main loop will exit as soon as we hit the yield
// below, before we create the main window.
hold();
-
+
// do *after* parsing args, as they dicate where logging is sent to, if anywhere, and only
// after activate (which means this is only logged for the one user-visible instance, not
// the other instances called when sending commands to the app via the command-line)
@@ -265,15 +265,15 @@ public class GearyApplication : Gtk.Application {
release();
}
-
+
private async void destroy_async() {
// see create_async() for reasoning hold/release is used
hold();
-
+
yield controller.close_async();
-
+
release();
-
+
is_destroyed = true;
}
@@ -309,7 +309,7 @@ public class GearyApplication : Gtk.Application {
public File get_user_config_directory() {
return File.new_for_path(Environment.get_user_config_dir()).get_child("geary");
}
-
+
/**
* Returns the base directory that the application's various resource files are stored. If the
* application is running from its installed directory, this will point to
@@ -346,25 +346,25 @@ public class GearyApplication : Gtk.Application {
File desktop_file = (install_dir != null)
? install_dir.get_child("share").get_child("applications").get_child("org.gnome.Geary.desktop")
:
File.new_for_path(SOURCE_ROOT_DIR).get_child("build").get_child("desktop").get_child("org.gnome.Geary.desktop");
-
+
return desktop_file.query_exists() ? desktop_file : null;
}
-
+
public bool is_installed() {
return exec_dir.has_prefix(get_install_prefix_dir());
}
-
+
// Returns the configure installation prefix directory, which does not imply Geary is installed
// or that it's running from this directory.
public File get_install_prefix_dir() {
return File.new_for_path(INSTALL_PREFIX);
}
-
+
// Returns the installation directory, or null if we're running outside of the installation
// directory.
public File? get_install_dir() {
File prefix_dir = get_install_prefix_dir();
-
+
return exec_dir.has_prefix(prefix_dir) ? prefix_dir : null;
}
@@ -384,17 +384,17 @@ public class GearyApplication : Gtk.Application {
public void exit(int exitcode = 0) {
if (exiting_fired)
return;
-
+
this.exitcode = exitcode;
-
+
exiting_fired = true;
if (!exiting(false)) {
exiting_fired = false;
this.exitcode = 0;
-
+
return;
}
-
+
// Give asynchronous destroy_async() a chance to complete, but to avoid bug(s) where
// Geary hangs at exit, shut the whole thing down if destroy_async() takes too long to
// complete
@@ -402,7 +402,7 @@ public class GearyApplication : Gtk.Application {
destroy_async.begin();
while (!is_destroyed || Gtk.events_pending()) {
Gtk.main_iteration();
-
+
int64 delta_usec = get_monotonic_time() - start_usec;
if (delta_usec >= FORCE_SHUTDOWN_USEC) {
debug("Forcing shutdown of Geary, %ss passed...", (delta_usec / USEC_PER_SEC).to_string());
@@ -422,7 +422,7 @@ public class GearyApplication : Gtk.Application {
Signal.stop_emission_by_name(this, "exiting");
return false;
}
-
+
// This call will fire "exiting" only if it's not already been fired and halt the application
// in its tracks.
public void panic() {
@@ -430,7 +430,7 @@ public class GearyApplication : Gtk.Application {
exiting_fired = true;
exiting(true);
}
-
+
Posix.exit(1);
}
diff --git a/src/client/application/geary-args.vala b/src/client/application/geary-args.vala
index cde707e4..1cb4858b 100644
--- a/src/client/application/geary-args.vala
+++ b/src/client/application/geary-args.vala
@@ -55,7 +55,7 @@ public bool parse(string[] args) {
GearyApplication.COPYRIGHT_2,
_("Please report comments, suggestions and bugs to:"),
GearyApplication.BUGREPORT));
-
+
try {
context.parse(ref args);
} catch (OptionError error) {
@@ -64,46 +64,46 @@ public bool parse(string[] args) {
stdout.printf("\n%s", context.get_help(true, null));
return false;
}
-
+
// other than the OptionEntry command-line arguments, the only acceptable arguments are
// mailto:'s
for (int ctr = 1; ctr < args.length; ctr++) {
string arg = args[ctr];
-
+
if (!arg.has_prefix(Geary.ComposedEmail.MAILTO_SCHEME)) {
stdout.printf(_("Unrecognized command line option ā%sā\n").printf(arg));
stdout.printf("\n%s", context.get_help(true, null));
-
+
return false;
}
}
-
+
if (version) {
stdout.printf("%s %s\n", GearyApplication.PRGNAME, GearyApplication.VERSION);
Process.exit(0);
}
-
+
if (log_network)
Geary.Logging.enable_flags(Geary.Logging.Flag.NETWORK);
-
+
if (log_serializer)
Geary.Logging.enable_flags(Geary.Logging.Flag.SERIALIZER);
-
+
if (log_replay_queue)
Geary.Logging.enable_flags(Geary.Logging.Flag.REPLAY);
-
+
if (log_conversations)
Geary.Logging.enable_flags(Geary.Logging.Flag.CONVERSATIONS);
-
+
if (log_periodic)
Geary.Logging.enable_flags(Geary.Logging.Flag.PERIODIC);
-
+
if (log_sql)
Geary.Logging.enable_flags(Geary.Logging.Flag.SQL);
-
+
if (log_folder_normalization)
Geary.Logging.enable_flags(Geary.Logging.Flag.FOLDER_NORMALIZATION);
-
+
if (log_deserializer)
Geary.Logging.enable_flags(Geary.Logging.Flag.DESERIALIZER);
diff --git a/src/client/application/geary-config.vala b/src/client/application/geary-config.vala
index cea2961a..f186b37e 100644
--- a/src/client/application/geary-config.vala
+++ b/src/client/application/geary-config.vala
@@ -67,41 +67,41 @@ public class Configuration {
public int window_width {
get { return settings.get_int(WINDOW_WIDTH_KEY); }
}
-
+
public int window_height {
get { return settings.get_int(WINDOW_HEIGHT_KEY); }
}
-
+
public bool window_maximize {
get { return settings.get_boolean(WINDOW_MAXIMIZE_KEY); }
}
-
+
public int folder_list_pane_position_old {
get { return settings.get_int(FOLDER_LIST_PANE_POSITION_KEY); }
}
-
+
public int folder_list_pane_position_horizontal {
get { return settings.get_int(FOLDER_LIST_PANE_POSITION_HORIZONTAL_KEY); }
set { settings.set_int(FOLDER_LIST_PANE_POSITION_HORIZONTAL_KEY, value); }
}
-
+
public int folder_list_pane_position_vertical {
get { return settings.get_int(FOLDER_LIST_PANE_POSITION_VERTICAL_KEY); }
}
-
+
public bool folder_list_pane_horizontal {
get { return settings.get_boolean(FOLDER_LIST_PANE_HORIZONTAL_KEY); }
}
-
+
public int messages_pane_position {
get { return settings.get_int(MESSAGES_PANE_POSITION_KEY); }
set { settings.set_int(MESSAGES_PANE_POSITION_KEY, value); }
}
-
+
public bool autoselect {
get { return settings.get_boolean(AUTOSELECT_KEY); }
}
-
+
public bool display_preview {
get { return settings.get_boolean(DISPLAY_PREVIEW_KEY); }
}
@@ -136,7 +136,7 @@ public class Configuration {
get { return settings.get_boolean(STARTUP_NOTIFICATIONS_KEY); }
set { set_boolean(STARTUP_NOTIFICATIONS_KEY, value); }
}
-
+
private const string CLOCK_FORMAT_KEY = "clock-format";
public Date.ClockFormat clock_format {
get {
@@ -146,12 +146,12 @@ public class Configuration {
return Date.ClockFormat.TWENTY_FOUR_HOURS;
}
}
-
+
public bool ask_open_attachment {
get { return settings.get_boolean(ASK_OPEN_ATTACHMENT_KEY); }
set { set_boolean(ASK_OPEN_ATTACHMENT_KEY, value); }
}
-
+
public bool compose_as_html {
get { return settings.get_boolean(COMPOSE_AS_HTML_KEY); }
set { set_boolean(COMPOSE_AS_HTML_KEY, value); }
@@ -195,43 +195,43 @@ public class Configuration {
SettingsBindFlags flags = GLib.SettingsBindFlags.DEFAULT) {
settings.bind(key, object, property, flags);
}
-
+
private void set_boolean(string name, bool value) {
if (!settings.set_boolean(name, value))
message("Unable to set configuration value %s = %s", name, value.to_string());
}
-
+
public Geary.SearchQuery.Strategy get_search_strategy() {
switch (settings.get_string(SEARCH_STRATEGY_KEY).down()) {
case "exact":
return Geary.SearchQuery.Strategy.EXACT;
-
+
case "aggressive":
return Geary.SearchQuery.Strategy.AGGRESSIVE;
-
+
case "horizon":
return Geary.SearchQuery.Strategy.HORIZON;
-
+
case "conservative":
default:
return Geary.SearchQuery.Strategy.CONSERVATIVE;
}
}
-
+
public void set_search_strategy(Geary.SearchQuery.Strategy strategy) {
switch (strategy) {
case Geary.SearchQuery.Strategy.EXACT:
settings.set_string(SEARCH_STRATEGY_KEY, "exact");
break;
-
+
case Geary.SearchQuery.Strategy.AGGRESSIVE:
settings.set_string(SEARCH_STRATEGY_KEY, "aggressive");
break;
-
+
case Geary.SearchQuery.Strategy.HORIZON:
settings.set_string(SEARCH_STRATEGY_KEY, "horizon");
break;
-
+
case Geary.SearchQuery.Strategy.CONSERVATIVE:
default:
settings.set_string(SEARCH_STRATEGY_KEY, "conservative");
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index b55a15e4..ef98ef68 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -192,17 +192,17 @@ public class GearyController : Geary.BaseObject {
* Fired when the currently selected account has changed.
*/
public signal void account_selected(Geary.Account? account);
-
+
/**
* Fired when the currently selected folder has changed.
*/
public signal void folder_selected(Geary.Folder? folder);
-
+
/**
* Fired when the number of conversations changes.
*/
public signal void conversation_count_changed(int count);
-
+
/**
* Fired when the search text is changed according to the controller. This accounts
* for a brief typmatic delay.
@@ -866,11 +866,11 @@ public class GearyController : Geary.BaseObject {
libnotify.clear_error_notification();
}
}
-
+
private void on_sending_started() {
main_window.status_bar.activate_message(StatusBar.Message.OUTBOX_SENDING);
}
-
+
private void on_sending_finished() {
main_window.status_bar.deactivate_message(StatusBar.Message.OUTBOX_SENDING);
}
@@ -924,7 +924,7 @@ public class GearyController : Geary.BaseObject {
// Returns true if the caller should try opening the account again
private async bool account_database_error_async(Geary.Account account) {
bool retry = true;
-
+
// give the user two options: reset the Account local store, or exit Geary. A third
// could be done to leave the Account in an unopened state, but we don't currently
// have provisions for that.
@@ -944,11 +944,11 @@ public class GearyController : Geary.BaseObject {
_("Unable to rebuild database for ā%sā").printf(account.information.id),
_("Error during rebuild:\n\n%s").printf(err.message));
errdialog.run();
-
+
retry = false;
}
break;
-
+
default:
retry = false;
break;
@@ -1010,10 +1010,10 @@ public class GearyController : Geary.BaseObject {
} catch(Error e) {
error("Could not open accounts: %s", e.message);
}
-
+
return true;
}
-
+
/**
* Displays the main window if we're ready. Otherwise does nothing.
*/
@@ -1024,7 +1024,7 @@ public class GearyController : Geary.BaseObject {
!Args.hidden_startup)
main_window.show();
}
-
+
/**
* Returns the number of accounts that exist in Geary. Note that not all accounts may be
* open. Zero is returned on an error.
@@ -1035,7 +1035,7 @@ public class GearyController : Geary.BaseObject {
} catch (Error e) {
debug("Error getting number of accounts: %s", e.message);
}
-
+
return 0; // on error
}
@@ -1110,9 +1110,9 @@ public class GearyController : Geary.BaseObject {
private async void do_select_folder(Geary.Folder folder) throws Error {
debug("Switching to %s...", folder.to_string());
-
+
closed_folder();
-
+
// This function is not reentrant. It should be, because it can be
// called reentrant-ly if you select folders quickly enough. This
// mutex lock is a bandaid solution to make the function safe to
@@ -1121,13 +1121,13 @@ public class GearyController : Geary.BaseObject {
// clear Revokable, as Undo is only available while a folder is selected
save_revokable(null, null);
-
+
// stop monitoring for conversations and close the folder
if (current_conversations != null) {
yield current_conversations.stop_monitoring_async(null);
current_conversations = null;
}
-
+
// re-enable copy/move to the last selected folder
if (current_folder != null) {
main_window.main_toolbar.copy_folder_menu.enable_disable_folder(current_folder, true);
@@ -1144,7 +1144,7 @@ public class GearyController : Geary.BaseObject {
if (pending_mailtos.size > 0) {
foreach(string mailto in pending_mailtos)
compose_mailto(mailto);
-
+
pending_mailtos.clear();
}
@@ -1155,16 +1155,16 @@ public class GearyController : Geary.BaseObject {
main_window.main_toolbar.move_folder_menu.add_folder(f);
}
}
-
+
folder_selected(current_folder);
-
+
if (!(current_folder is Geary.SearchFolder))
previous_non_search_folder = current_folder;
-
+
// disable copy/move to the new folder
main_window.main_toolbar.copy_folder_menu.enable_disable_folder(current_folder, false);
main_window.main_toolbar.move_folder_menu.enable_disable_folder(current_folder, false);
-
+
update_ui();
current_conversations = new Geary.App.ConversationMonitor(
@@ -1193,7 +1193,7 @@ public class GearyController : Geary.BaseObject {
);
select_folder_mutex.release(ref mutex_token);
-
+
debug("Switched to %s", folder.to_string());
}
@@ -1229,10 +1229,10 @@ public class GearyController : Geary.BaseObject {
private void on_libnotify_invoked(Geary.Folder? folder, Geary.Email? email) {
new_messages_monitor.clear_all_new_messages();
-
+
if (folder == null || email == null || !can_switch_conversation_view())
return;
-
+
main_window.folder_list.select_folder(folder);
Geary.App.Conversation? conversation = current_conversations.get_by_email_identifier(email.id);
if (conversation != null)
@@ -1252,12 +1252,12 @@ public class GearyController : Geary.BaseObject {
on_indicator_activated_application(timestamp);
main_window.folder_list.select_folder(folder);
}
-
+
private void on_load_more() {
debug("on_load_more");
current_conversations.min_window_count += MIN_CONVERSATION_COUNT;
}
-
+
private void on_select_folder_completed(Object? source, AsyncResult result) {
try {
do_select_folder.end(result);
@@ -1474,10 +1474,10 @@ public class GearyController : Geary.BaseObject {
private void cancel_folder() {
Cancellable old_cancellable = cancellable_folder;
cancellable_folder = new Cancellable();
-
+
old_cancellable.cancel();
}
-
+
// Like cancel_folder() but doesn't cancel outstanding operations, allowing them to complete
// in the background
private void closed_folder() {
@@ -1494,16 +1494,16 @@ public class GearyController : Geary.BaseObject {
private void cancel_context_dependent_buttons() {
Cancellable old_cancellable = cancellable_context_dependent_buttons;
cancellable_context_dependent_buttons = new Cancellable();
-
+
old_cancellable.cancel();
}
-
+
// We need to include the second parameter, or valac doesn't recognize the function as matching
// GearyApplication.exiting's signature.
private bool on_application_exiting(GearyApplication sender, bool panicked) {
if (close_composition_windows())
return true;
-
+
return sender.cancel_exit();
}
@@ -1602,7 +1602,7 @@ public class GearyController : Geary.BaseObject {
foreach (Geary.App.Conversation conversation in selected_conversations) {
if (conversation.is_unread())
unread_selected = true;
-
+
// Only check the messages that "Mark as Unread" would mark, so we
// don't add the menu option and have it not do anything.
//
@@ -1635,7 +1635,7 @@ public class GearyController : Geary.BaseObject {
private void on_visible_conversations_changed(Gee.Set<Geary.App.Conversation> visible) {
clear_new_messages("on_visible_conversations_changed", visible);
}
-
+
private bool should_notify_new_messages(Geary.Folder folder) {
// A monitored folder must be selected to squelch notifications;
// if conversation list is at top of display, don't display
@@ -1644,22 +1644,22 @@ public class GearyController : Geary.BaseObject {
|| main_window.conversation_list_view.vadjustment.value != 0.0
|| !main_window.has_toplevel_focus;
}
-
+
// Clears messages if conditions are true: anything in should_notify_new_messages() is
// false and the supplied visible messages are visible in the conversation list view
private void clear_new_messages(string caller, Gee.Set<Geary.App.Conversation>? supplied) {
if (current_folder == null || !new_messages_monitor.get_folders().contains(current_folder)
|| should_notify_new_messages(current_folder))
return;
-
+
Gee.Set<Geary.App.Conversation> visible =
supplied ?? main_window.conversation_list_view.get_visible_conversations();
-
+
foreach (Geary.App.Conversation conversation in visible) {
if (new_messages_monitor.are_any_new_messages(current_folder, conversation.get_email_ids())) {
debug("Clearing new messages: %s", caller);
new_messages_monitor.clear_new_messages(current_folder);
-
+
break;
}
}
@@ -1769,12 +1769,12 @@ public class GearyController : Geary.BaseObject {
private void on_copy_conversation(Geary.Folder destination) {
copy_email(get_selected_email_ids(false), destination.path);
}
-
+
private void on_move_conversation(Geary.Folder destination) {
// Nothing to do if nothing selected.
if (selected_conversations == null || selected_conversations.size == 0)
return;
-
+
Gee.Collection<Geary.EmailIdentifier> ids = get_selected_email_ids(false);
if (ids.size == 0)
return;
@@ -2052,21 +2052,21 @@ public class GearyController : Geary.BaseObject {
// Safely destroy windows.
foreach(ComposerWidget cw in composers_to_destroy)
((ComposerContainer) cw.parent).close_container();
-
+
// If we cancelled the quit we can bail here.
if (quit_cancelled) {
waiting_to_close.clear();
-
+
return false;
}
-
+
// If there's still windows saving, we can't exit just yet. Hide the main window and wait.
if (waiting_to_close.size > 0) {
main_window.hide();
-
+
return false;
}
-
+
// If we deleted all composer windows without the user cancelling, we can exit.
return true;
}
@@ -2114,7 +2114,7 @@ public class GearyController : Geary.BaseObject {
bool is_draft = false) {
if (current_account == null)
return;
-
+
bool inline;
if (!should_create_new_composer(compose_type, referred, quote, is_draft, out inline))
return;
@@ -2175,7 +2175,7 @@ public class GearyController : Geary.BaseObject {
private bool should_create_new_composer(ComposerWidget.ComposeType? compose_type,
Geary.Email? referred, string? quote, bool is_draft, out bool inline) {
inline = true;
-
+
// In we're replying, see whether we already have a reply for that message.
if (compose_type != null && compose_type != ComposerWidget.ComposeType.NEW_MESSAGE) {
foreach (ComposerWidget cw in composer_widgets) {
@@ -2189,17 +2189,17 @@ public class GearyController : Geary.BaseObject {
inline = !any_inline_composers();
return true;
}
-
+
// If there are no inline composers, go ahead!
if (!any_inline_composers())
return true;
-
+
// If we're resuming a draft with open composers, open in a new window.
if (is_draft) {
inline = false;
return true;
}
-
+
// If we're creating a new message, and there's already a new message open, focus on
// it if it hasn't been modified; otherwise open a new composer in a new window.
if (compose_type == ComposerWidget.ComposeType.NEW_MESSAGE) {
@@ -2215,7 +2215,7 @@ public class GearyController : Geary.BaseObject {
}
}
}
-
+
// Find out what to do with the inline composers.
// TODO: Remove this in favor of automatically saving drafts
this.application.present();
@@ -2239,24 +2239,24 @@ public class GearyController : Geary.BaseObject {
}
return false;
}
-
+
public bool can_switch_conversation_view() {
bool inline;
return should_create_new_composer(null, null, null, false, out inline);
}
-
+
public bool any_inline_composers() {
foreach (ComposerWidget cw in composer_widgets)
if (cw.state != ComposerWidget.ComposerState.DETACHED)
return true;
return false;
}
-
+
private void on_composer_widget_destroy(Gtk.Widget sender) {
composer_widgets.remove((ComposerWidget) sender);
debug(@"Destroying composer of type $(((ComposerWidget) sender).compose_type); "
+ @"$(composer_widgets.size) composers remaining");
-
+
if (waiting_to_close.remove((ComposerWidget) sender)) {
// If we just removed the last window in the waiting to close list, it's time to exit!
if (waiting_to_close.size == 0)
@@ -2328,28 +2328,28 @@ public class GearyController : Geary.BaseObject {
// must support Empty in order for this command to proceed
if (current_account == null)
return;
-
+
Geary.Folder? folder = null;
try {
folder = current_account.get_special_folder(special_folder_type);
} catch (Error err) {
debug("%s: Unable to get special folder %s: %s", current_account.to_string(),
special_folder_type.to_string(), err.message);
-
+
// fall through
}
-
+
if (folder == null)
return;
-
+
Geary.FolderSupport.Empty? emptyable = folder as Geary.FolderSupport.Empty;
if (emptyable == null) {
debug("%s: Special folder %s (%s) does not support emptying", current_account.to_string(),
folder.path.to_string(), special_folder_type.to_string());
-
+
return;
}
-
+
ConfirmationDialog dialog = new ConfirmationDialog(main_window,
_("Empty all email from your %s folder?").printf(special_folder_type.get_display_name()),
_("This removes the email from Geary and your email server.")
@@ -2357,11 +2357,11 @@ public class GearyController : Geary.BaseObject {
_("Empty %s").printf(special_folder_type.get_display_name()), "destructive-action");
dialog.use_secondary_markup(true);
dialog.set_focus_response(Gtk.ResponseType.CANCEL);
-
+
if (dialog.run() == Gtk.ResponseType.OK)
empty_folder_async.begin(emptyable, cancellable_folder);
}
-
+
private async void empty_folder_async(Geary.FolderSupport.Empty emptyable, Cancellable? cancellable) {
try {
yield do_empty_folder_async(emptyable, cancellable);
@@ -2369,7 +2369,7 @@ public class GearyController : Geary.BaseObject {
// don't report to user if cancelled
if (err is IOError.CANCELLED)
return;
-
+
ErrorDialog dialog = new ErrorDialog(main_window,
_("Error emptying %s").printf(emptyable.get_display_name()), err.message);
dialog.run();
@@ -2463,7 +2463,7 @@ public class GearyController : Geary.BaseObject {
Gee.Collection<Geary.EmailIdentifier> ids = get_selected_email_ids(false);
if (archive) {
debug("Archiving selected messages");
-
+
Geary.FolderSupport.Archive? supports_archive = current_folder as Geary.FolderSupport.Archive;
if (supports_archive == null) {
debug("Folder %s doesn't support archive", current_folder.to_string());
@@ -2471,10 +2471,10 @@ public class GearyController : Geary.BaseObject {
save_revokable(yield supports_archive.archive_email_async(ids, cancellable),
_("Undo archive (Ctrl+Z)"));
}
-
+
return;
}
-
+
if (trash) {
yield trash_messages_async(ids, cancellable);
} else {
@@ -2497,13 +2497,13 @@ public class GearyController : Geary.BaseObject {
revokable.notify[Geary.Revokable.PROP_VALID].disconnect(on_revokable_valid_changed);
revokable.notify[Geary.Revokable.PROP_IN_PROCESS].disconnect(update_revokable_action);
revokable.committed.disconnect(on_revokable_committed);
-
+
revokable.commit_async.begin();
}
-
+
// store new revokable
revokable = new_revokable;
-
+
// connect to new revokable
if (revokable != null) {
revokable.notify[Geary.Revokable.PROP_VALID].connect(on_revokable_valid_changed);
@@ -2532,7 +2532,7 @@ public class GearyController : Geary.BaseObject {
if (revokable != null && !revokable.valid)
save_revokable(null, null);
}
-
+
private void on_revokable_committed(Geary.Revokable? committed_revokable) {
if (committed_revokable == null)
return;
@@ -2545,14 +2545,14 @@ public class GearyController : Geary.BaseObject {
if (revokable != null && revokable.valid)
revokable.revoke_async.begin(null, on_revoke_completed);
}
-
+
private void on_revoke_completed(Object? object, AsyncResult result) {
// Don't use the "revokable" instance because it might have gone null before this callback
// was reached
Geary.Revokable? origin = object as Geary.Revokable;
if (origin == null)
return;
-
+
try {
origin.revoke_async.end(result);
} catch (Error err) {
@@ -2742,11 +2742,11 @@ public class GearyController : Geary.BaseObject {
debug("Error checking for what operations are supported in the selected conversations: %s",
e.message);
}
-
+
// Exit here if the user has cancelled.
if (cancellable != null && cancellable.is_cancelled())
return;
-
+
Gee.HashSet<Type> supported_operations = new Gee.HashSet<Type>();
if (selected_operations != null)
supported_operations.add_all(selected_operations.get_values());
@@ -2760,7 +2760,7 @@ public class GearyController : Geary.BaseObject {
Gee.LinkedList<ComposerWidget> ret = Geary.traverse<ComposerWidget>(composer_widgets)
.filter(w => w.account.information == account)
.to_linked_list();
-
+
return ret.size >= 1 ? ret : null;
}
diff --git a/src/client/components/count-badge.vala b/src/client/components/count-badge.vala
index d7693cb9..a21d5246 100644
--- a/src/client/components/count-badge.vala
+++ b/src/client/components/count-badge.vala
@@ -9,13 +9,13 @@
*/
public class CountBadge : Geary.BaseObject {
public const string UNREAD_BG_COLOR = "#888888";
-
+
private const int FONT_SIZE_MESSAGE_COUNT = 8;
-
+
public int count { get; set; default = 0; }
-
+
private int min = 0;
-
+
/**
* Creates a count badge.
* @param min Minimum count to draw.
@@ -23,38 +23,38 @@ public class CountBadge : Geary.BaseObject {
public CountBadge(int min) {
this.min = min;
}
-
+
public int get_width(Gtk.Widget widget) {
int width = 0;
render_internal(widget, null, 0, 0, false, out width, null);
-
+
return width;
}
-
+
public int get_height(Gtk.Widget widget) {
int height = 0;
render_internal(widget, null, 0, 0, false, null, out height);
-
+
return height;
}
-
+
public void render(Gtk.Widget widget, Cairo.Context? ctx, int x, int y, bool selected) {
render_internal(widget, ctx, x, y, selected, null, null);
}
-
+
private void render_internal(Gtk.Widget widget, Cairo.Context? ctx, int x, int y, bool selected,
out int? width, out int? height) {
if (count < min) {
width = 0;
height = 0;
-
+
return;
}
-
- string mails =
+
+ string mails =
"<span foreground='white' font='%d' weight='bold'> %d </span>"
.printf(FONT_SIZE_MESSAGE_COUNT, count);
-
+
Pango.Layout layout_num = widget.create_pango_layout(null);
layout_num.set_markup(mails, -1);
layout_num.set_alignment(Pango.Alignment.RIGHT);
@@ -67,7 +67,7 @@ public class CountBadge : Geary.BaseObject {
double bg_height = logical_rect.height;
double radius = bg_height / 2.0;
double degrees = Math.PI / 180.0;
-
+
// Create rounded rect.
ctx.new_sub_path();
ctx.arc(x + bg_width - radius, y + radius, radius, -90 * degrees, 0 * degrees);
@@ -75,18 +75,18 @@ public class CountBadge : Geary.BaseObject {
ctx.arc(x + radius, y + bg_height - radius, radius, 90 * degrees, 180 * degrees);
ctx.arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
ctx.close_path();
-
+
// Colorize our shape.
GtkUtil.set_source_color_from_string(ctx, UNREAD_BG_COLOR);
ctx.fill_preserve();
ctx.set_line_width(2.0);
ctx.stroke();
-
+
// Center the text.
ctx.move_to(x + (bg_width / 2) - logical_rect.width / 2, y);
Pango.cairo_show_layout(ctx, layout_num);
}
-
+
width = logical_rect.width + FormattedConversationData.LINE_SPACING;
height = logical_rect.height;
}
diff --git a/src/client/components/icon-factory.vala b/src/client/components/icon-factory.vala
index 3fd6eab1..c7950bf3 100644
--- a/src/client/components/icon-factory.vala
+++ b/src/client/components/icon-factory.vala
@@ -8,26 +8,26 @@
public class IconFactory {
public const Gtk.IconSize ICON_TOOLBAR = Gtk.IconSize.LARGE_TOOLBAR;
public const Gtk.IconSize ICON_SIDEBAR = Gtk.IconSize.MENU;
-
+
private static IconFactory? _instance = null;
public static IconFactory instance {
get {
if (_instance == null)
_instance = new IconFactory();
-
+
return _instance;
}
-
+
private set { _instance = value; }
}
public const int UNREAD_ICON_SIZE = 16;
public const int STAR_ICON_SIZE = 16;
-
+
private Gtk.IconTheme icon_theme { get; private set; }
-
+
private File icons_dir;
-
+
// Creates the icon factory.
private IconFactory() {
icons_dir = GearyApplication.instance.get_resource_directory().get_child("icons");
@@ -39,33 +39,33 @@ public class IconFactory {
// perform any additional initialization here; at this time, everything is done in the
// constructor
}
-
+
private int icon_size_to_pixels(Gtk.IconSize icon_size) {
switch (icon_size) {
case ICON_SIDEBAR:
return 16;
-
+
case ICON_TOOLBAR:
default:
return 24;
}
}
-
+
public Icon get_theme_icon(string name) {
return new ThemedIcon(name);
}
-
+
public Icon get_custom_icon(string name, Gtk.IconSize size) {
int pixels = icon_size_to_pixels(size);
-
+
// Try sized icon first.
File icon_file = icons_dir.get_child("%dx%d".printf(pixels, pixels)).get_child(
"%s.svg".printf(name));
-
+
// If that wasn't found, try a non-sized icon.
if (!icon_file.query_exists())
icon_file = icons_dir.get_child("%s.svg".printf(name));
-
+
return new FileIcon(icon_file);
}
@@ -76,7 +76,7 @@ public class IconFactory {
} catch (Error err) {
warning("Couldn't load image-missing icon: %s", err.message);
}
-
+
// If that fails... well they're out of luck.
return null;
}
@@ -94,7 +94,7 @@ public class IconFactory {
private Gdk.Pixbuf aspect_scale_down_pixbuf(Gdk.Pixbuf pixbuf, int size) {
if (pixbuf.width <= size && pixbuf.height <= size)
return pixbuf;
-
+
int scaled_width, scaled_height;
if (pixbuf.width >= pixbuf.height) {
double aspect = (double) size / (double) pixbuf.width;
@@ -105,14 +105,14 @@ public class IconFactory {
scaled_width = (int) Math.round((double) pixbuf.width * aspect);
scaled_height = size;
}
-
+
return pixbuf.scale_simple(scaled_width, scaled_height, Gdk.InterpType.BILINEAR);
}
-
+
public Gdk.Pixbuf? load_symbolic(string icon_name, int size, Gtk.StyleContext style,
Gtk.IconLookupFlags flags = 0) {
Gtk.IconInfo? icon_info = icon_theme.lookup_icon(icon_name, size, flags);
-
+
// Attempt to load as a symbolic icon.
if (icon_info != null) {
try {
@@ -121,11 +121,11 @@ public class IconFactory {
message("Couldn't load icon: %s", e.message);
}
}
-
+
// Default: missing image icon.
return get_missing_icon(size, flags);
}
-
+
/**
* Loads a symbolic icon into a pixbuf, where the color-key has been switched to the provided
* color.
@@ -133,7 +133,7 @@ public class IconFactory {
public Gdk.Pixbuf? load_symbolic_colored(string icon_name, int size, Gdk.RGBA color,
Gtk.IconLookupFlags flags = 0) {
Gtk.IconInfo? icon_info = icon_theme.lookup_icon(icon_name, size, flags);
-
+
// Attempt to load as a symbolic icon.
if (icon_info != null) {
try {
@@ -145,6 +145,6 @@ public class IconFactory {
// Default: missing image icon.
return get_missing_icon(size, flags);
}
-
+
}
diff --git a/src/client/components/monitored-progress-bar.vala
b/src/client/components/monitored-progress-bar.vala
index 927e64f5..52afa599 100644
--- a/src/client/components/monitored-progress-bar.vala
+++ b/src/client/components/monitored-progress-bar.vala
@@ -9,24 +9,24 @@
*/
public class MonitoredProgressBar : Gtk.ProgressBar {
private Geary.ProgressMonitor? monitor = null;
-
+
public void set_progress_monitor(Geary.ProgressMonitor monitor) {
this.monitor = monitor;
monitor.start.connect(on_start);
monitor.finish.connect(on_finish);
monitor.update.connect(on_update);
-
+
fraction = monitor.progress;
}
-
+
private void on_start() {
fraction = 0.0;
}
-
+
private void on_update(double total_progress, double change, Geary.ProgressMonitor monitor) {
fraction = total_progress;
}
-
+
private void on_finish() {
fraction = 1.0;
}
diff --git a/src/client/components/monitored-spinner.vala b/src/client/components/monitored-spinner.vala
index 3f87975b..1031c08e 100644
--- a/src/client/components/monitored-spinner.vala
+++ b/src/client/components/monitored-spinner.vala
@@ -9,7 +9,7 @@
*/
public class MonitoredSpinner : Gtk.Spinner {
private Geary.ProgressMonitor? monitor = null;
-
+
public void set_progress_monitor(Geary.ProgressMonitor? monitor) {
if (monitor != null) {
this.monitor = monitor;
@@ -21,17 +21,17 @@ public class MonitoredSpinner : Gtk.Spinner {
hide();
}
}
-
+
public override void show() {
if (monitor != null && monitor.is_in_progress)
base.show();
}
-
+
private void on_start() {
start();
show();
}
-
+
private void on_stop() {
stop();
hide();
diff --git a/src/client/components/search-bar.vala b/src/client/components/search-bar.vala
index 1cb7c1be..40bb02f7 100644
--- a/src/client/components/search-bar.vala
+++ b/src/client/components/search-bar.vala
@@ -6,17 +6,17 @@
public class SearchBar : Gtk.SearchBar {
private const string DEFAULT_SEARCH_TEXT = _("Search");
-
+
public string search_text { get { return search_entry.text; } }
public bool search_entry_has_focus { get { return search_entry.has_focus; } }
-
+
private Gtk.SearchEntry search_entry = new Gtk.SearchEntry();
private Geary.ProgressMonitor? search_upgrade_progress_monitor = null;
private MonitoredProgressBar search_upgrade_progress_bar = new MonitoredProgressBar();
private Geary.Account? current_account = null;
-
+
public signal void search_text_changed(string search_text);
-
+
public SearchBar() {
// Search entry.
search_entry.width_chars = 28;
@@ -28,52 +28,52 @@ public class SearchBar : Gtk.SearchBar {
search_text_changed(search_entry.text);
});
search_entry.has_focus = true;
-
+
// Search upgrade progress bar.
search_upgrade_progress_bar.show_text = true;
search_upgrade_progress_bar.visible = false;
search_upgrade_progress_bar.no_show_all = true;
-
+
add(search_upgrade_progress_bar);
add(search_entry);
-
+
set_search_placeholder_text(DEFAULT_SEARCH_TEXT);
-
+
GearyApplication.instance.controller.account_selected.connect(on_account_changed);
}
-
+
public void set_search_text(string text) {
search_entry.text = text;
}
-
+
public void give_search_focus() {
set_search_mode(true);
search_entry.grab_focus();
}
-
+
public void set_search_placeholder_text(string placeholder) {
search_entry.placeholder_text = placeholder;
}
-
+
private void on_search_upgrade_start() {
// Set the progress bar's width to match the search entry's width.
int minimum_width = 0;
int natural_width = 0;
search_entry.get_preferred_width(out minimum_width, out natural_width);
search_upgrade_progress_bar.width_request = minimum_width;
-
+
search_entry.hide();
search_upgrade_progress_bar.show();
}
-
+
private void on_search_upgrade_finished() {
search_entry.show();
search_upgrade_progress_bar.hide();
}
-
+
private void on_account_changed(Geary.Account? account) {
on_search_upgrade_finished(); // Reset search box.
-
+
if (search_upgrade_progress_monitor != null) {
search_upgrade_progress_monitor.start.disconnect(on_search_upgrade_start);
search_upgrade_progress_monitor.finish.disconnect(on_search_upgrade_finished);
@@ -88,7 +88,7 @@ public class SearchBar : Gtk.SearchBar {
if (account != null) {
search_upgrade_progress_monitor = account.search_upgrade_monitor;
search_upgrade_progress_bar.set_progress_monitor(search_upgrade_progress_monitor);
-
+
search_upgrade_progress_monitor.start.connect(on_search_upgrade_start);
search_upgrade_progress_monitor.finish.connect(on_search_upgrade_finished);
if (search_upgrade_progress_monitor.is_in_progress)
diff --git a/src/client/components/status-bar.vala b/src/client/components/status-bar.vala
index 8ee204a3..6b71a911 100644
--- a/src/client/components/status-bar.vala
+++ b/src/client/components/status-bar.vala
@@ -18,7 +18,7 @@ public class StatusBar : Gtk.Statusbar {
OUTBOX_SENDING,
OUTBOX_SEND_FAILURE,
OUTBOX_SAVE_SENT_MAIL_FAILED;
-
+
internal string get_text() {
switch (this) {
case Message.OUTBOX_SENDING:
@@ -35,7 +35,7 @@ public class StatusBar : Gtk.Statusbar {
assert_not_reached();
}
}
-
+
internal Context get_context() {
switch (this) {
case Message.OUTBOX_SENDING:
@@ -49,36 +49,36 @@ public class StatusBar : Gtk.Statusbar {
}
}
}
-
+
internal enum Context {
OUTBOX,
}
-
+
private Gee.HashMap<Context, uint> context_ids = new Gee.HashMap<Context, uint>();
private Gee.HashMap<Message, uint> message_ids = new Gee.HashMap<Message, uint>();
private Gee.HashMap<Message, int> message_counts = new Gee.HashMap<Message, int>();
-
+
public StatusBar() {
set_context_id(Context.OUTBOX);
}
-
+
private void set_context_id(Context context) {
context_ids.set(context, get_context_id(context.to_string()));
}
-
+
private int get_count(Message message) {
return (message_counts.has_key(message) ? message_counts.get(message) : 0);
}
-
+
private void push_message(Message message) {
message_ids.set(message, push(context_ids.get(message.get_context()), message.get_text()));
}
-
+
private void remove_message(Message message) {
remove(context_ids.get(message.get_context()), message_ids.get(message));
message_ids.unset(message);
}
-
+
/**
* Return whether the message has been activated more times than it has
* been deactivated.
@@ -86,19 +86,19 @@ public class StatusBar : Gtk.Statusbar {
public bool is_message_active(Message message) {
return message_ids.has_key(message);
}
-
+
public void activate_message(Message message) {
if (is_message_active(message))
remove_message(message);
-
+
push_message(message);
message_counts.set(message, get_count(message) + 1);
}
-
+
public void deactivate_message(Message message) {
if (!is_message_active(message))
return;
-
+
int count = get_count(message);
if (count == 1)
remove_message(message);
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index 3582c517..cb6d9506 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -22,7 +22,7 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
REPLY_ALL,
FORWARD
}
-
+
public enum CloseStatus {
DO_CLOSE,
PENDING_CLOSE,
@@ -551,7 +551,7 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
ContactListStoreCache contact_list_store_cache, string mailto, Configuration config) {
this(account, contact_list_store_cache, ComposeType.NEW_MESSAGE, config);
-
+
Gee.HashMultiMap<string, string> headers = new Gee.HashMultiMap<string, string>();
if (mailto.length > Geary.ComposedEmail.MAILTO_SCHEME.length) {
// Parse the mailto link.
@@ -920,11 +920,11 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
[GtkCallback]
private void on_drag_data_received(Gtk.Widget sender, Gdk.DragContext context, int x, int y,
Gtk.SelectionData selection_data, uint info, uint time_) {
-
+
bool dnd_success = false;
if (selection_data.get_length() >= 0) {
dnd_success = true;
-
+
string uri_list = (string) selection_data.get_data();
string[] uris = uri_list.strip().split("\n");
foreach (string uri in uris) {
@@ -939,7 +939,7 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
}
}
}
-
+
Gtk.drag_finish(context, dnd_success, false, time_);
}
@@ -947,7 +947,7 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
private bool on_drag_drop(Gtk.Widget sender, Gdk.DragContext context, int x, int y, uint time_) {
if (context.list_targets() == null)
return false;
-
+
uint length = context.list_targets().length();
Gdk.Atom? target_type = null;
for (uint i = 0; i < length; i++) {
@@ -955,10 +955,10 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
if (target.name() == URI_LIST_MIME_TYPE)
target_type = target;
}
-
+
if (target_type == null)
return false;
-
+
Gtk.drag_get_data(sender, context, target_type, time_);
return true;
}
@@ -1093,7 +1093,7 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
if (!modify_headers)
return;
-
+
bool recipients_modified = this.to_entry.modified || this.cc_entry.modified ||
this.bcc_entry.modified;
if (!recipients_modified) {
if (type == ComposeType.REPLY || type == ComposeType.REPLY_ALL)
@@ -1472,7 +1472,7 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
} catch (Error err) {
GLib.message("Unable to discard draft: %s", err.message);
}
-
+
return null;
}
diff --git a/src/client/composer/contact-entry-completion.vala
b/src/client/composer/contact-entry-completion.vala
index f999b7b1..a68704b8 100644
--- a/src/client/composer/contact-entry-completion.vala
+++ b/src/client/composer/contact-entry-completion.vala
@@ -70,7 +70,7 @@ public class ContactEntryCompletion : Gtk.EntryCompletion {
Gtk.Entry? entry = sender.get_entry() as Gtk.Entry;
if (entry == null)
return false;
-
+
int current_address_index;
string current_address_remainder;
Gee.List<string> addresses = get_addresses(sender, out current_address_index, null,
@@ -80,32 +80,32 @@ public class ContactEntryCompletion : Gtk.EntryCompletion {
addresses.insert(current_address_index + 1, current_address_remainder);
string delimiter = ", ";
entry.text = concat_strings(addresses, delimiter);
-
+
int characters_seen_so_far = 0;
for (int i = 0; i <= current_address_index; i++)
characters_seen_so_far += addresses[i].char_count() + delimiter.char_count();
-
+
entry.set_position(characters_seen_so_far);
-
+
return true;
}
-
+
private bool on_cursor_on_match(Gtk.EntryCompletion sender, Gtk.TreeModel model, Gtk.TreeIter iter) {
last_iter = iter;
return true;
}
-
+
public void trigger_selection() {
if (last_iter != null) {
on_match_selected(this, model, last_iter);
last_iter = null;
}
}
-
+
public void reset_selection() {
last_iter = null;
}
-
+
private Gee.List<string> get_addresses(Gtk.EntryCompletion completion,
out int current_address_index = null, out string current_address_key = null,
out string current_address_remainder = null) {
@@ -117,25 +117,25 @@ public class ContactEntryCompletion : Gtk.EntryCompletion {
empty_addresses.add("");
if (entry == null)
return empty_addresses;
-
+
string? original_text = entry.get_text();
if (original_text == null)
return empty_addresses;
-
+
int cursor_position = entry.cursor_position;
int cursor_offset = original_text.index_of_nth_char(cursor_position);
if (cursor_offset < 0)
return empty_addresses;
-
+
Gee.List<string> addresses = new Gee.ArrayList<string>();
string delimiter = ",";
string[] addresses_array = original_text.split(delimiter);
foreach (string address in addresses_array)
addresses.add(address);
-
+
if (addresses.size < 1)
return empty_addresses;
-
+
int bytes_seen_so_far = 0;
current_address_index = addresses.size - 1;
for (int i = 0; i < addresses.size; i++) {
@@ -145,7 +145,7 @@ public class ContactEntryCompletion : Gtk.EntryCompletion {
current_address_key = addresses[i]
.substring(0, cursor_offset - bytes_seen_so_far)
.strip().normalize().casefold();
-
+
current_address_remainder = addresses[i]
.substring(cursor_offset - bytes_seen_so_far).strip();
break;
@@ -155,10 +155,10 @@ public class ContactEntryCompletion : Gtk.EntryCompletion {
for (int i = 0; i < addresses.size; i++) {
addresses[i] = addresses[i].strip();
}
-
+
return addresses;
}
-
+
// We could only add the delimiter *between* each string (i.e., don't add it after the last
// string). But it's easier for the user if they don't have to manually type a comma after
// adding each address. So we add the delimiter after every string.
@@ -168,36 +168,36 @@ public class ContactEntryCompletion : Gtk.EntryCompletion {
builder.append(strings[i]);
builder.append(delimiter);
}
-
+
return builder.str;
}
-
+
private bool match_prefix_contact(string needle, Geary.Contact contact,
out string highlighted_result = null) {
string email_result;
bool email_match = match_prefix_string(needle, contact.email, out email_result);
-
+
string real_name_result;
bool real_name_match = match_prefix_string(needle, contact.real_name, out real_name_result);
-
+
// email_result and real_name_result were already escaped, then <b></b> tags were added to
// highlight matches. We don't want to escape them again.
highlighted_result = contact.real_name == null ? email_result :
real_name_result + Markup.escape_text(" <") + email_result + Markup.escape_text(">");
-
+
return email_match || real_name_match;
}
private bool match_prefix_string(string needle, string? haystack = null,
out string highlighted_result = null) {
highlighted_result = "";
-
+
if (Geary.String.is_empty(haystack) || Geary.String.is_empty(needle))
return false;
-
+
// Default result if there is no match or we encounter an error.
highlighted_result = haystack;
-
+
bool matched = false;
try {
string escaped_needle = Regex.escape_string(needle.normalize());
@@ -210,10 +210,10 @@ public class ContactEntryCompletion : Gtk.EntryCompletion {
} catch (RegexError err) {
debug("Error matching regex: %s", err.message);
}
-
+
highlighted_result = Markup.escape_text(highlighted_result)
.replace("‘", "<b>").replace("’", "</b>");
-
+
return matched;
}
@@ -223,7 +223,7 @@ public class ContactEntryCompletion : Gtk.EntryCompletion {
result.append("\xc2\x91%s\xc2\x92".printf(match));
// This is UTF-8 encoding of U+0091 and U+0092
}
-
+
return false;
}
}
diff --git a/src/client/composer/contact-list-store.vala b/src/client/composer/contact-list-store.vala
index 5959eb8d..3af3cf11 100644
--- a/src/client/composer/contact-list-store.vala
+++ b/src/client/composer/contact-list-store.vala
@@ -55,7 +55,7 @@ public class ContactListStore : Gtk.ListStore, Geary.BaseInterface {
public enum Column {
CONTACT_OBJECT,
PRIOR_KEYS;
-
+
public static Type[] get_types() {
return {
typeof (Geary.Contact), // CONTACT_OBJECT
@@ -63,7 +63,7 @@ public class ContactListStore : Gtk.ListStore, Geary.BaseInterface {
};
}
}
-
+
public Geary.ContactStore contact_store { get; private set; }
public ContactListStore(Geary.ContactStore contact_store) {
@@ -103,7 +103,7 @@ public class ContactListStore : Gtk.ListStore, Geary.BaseInterface {
public Geary.Contact get_contact(Gtk.TreeIter iter) {
GLib.Value contact_value;
get_value(iter, Column.CONTACT_OBJECT, out contact_value);
-
+
return (Geary.Contact) contact_value.get_object();
}
@@ -125,24 +125,24 @@ public class ContactListStore : Gtk.ListStore, Geary.BaseInterface {
Gtk.TreeIter iter;
if (!get_iter_first(out iter))
return;
-
+
do {
if (get_contact(iter) != updated_contact)
continue;
-
+
Gtk.TreePath? path = get_path(iter);
if (path != null)
row_changed(path, iter);
-
+
return;
} while (iter_next(ref iter));
}
-
+
private void on_contacts_added(Gee.Collection<Geary.Contact> contacts) {
foreach (Geary.Contact contact in contacts)
add_contact(contact);
}
-
+
private void on_contacts_updated(Gee.Collection<Geary.Contact> contacts) {
foreach (Geary.Contact contact in contacts)
update_contact(contact);
diff --git a/src/client/composer/email-entry.vala b/src/client/composer/email-entry.vala
index 94b8a5b6..c0632ac6 100644
--- a/src/client/composer/email-entry.vala
+++ b/src/client/composer/email-entry.vala
@@ -15,21 +15,21 @@ public class EmailEntry : Gtk.Entry {
// null or valid addresses
public Geary.RFC822.MailboxAddresses? addresses { get; set; default = null; }
-
+
private weak ComposerWidget composer;
-
+
private bool updating = false;
public EmailEntry(ComposerWidget composer) {
changed.connect(on_changed);
key_press_event.connect(on_key_press);
this.composer = composer;
-
+
notify["addresses"].connect(() => {
validate_addresses();
if (updating)
return;
-
+
updating = true;
modified = true;
text = (addresses == null) ? "" : addresses.to_full_display();
@@ -62,7 +62,7 @@ public class EmailEntry : Gtk.Entry {
addresses = new Geary.RFC822.MailboxAddresses.from_rfc822_string(text);
updating = false;
}
-
+
private void validate_addresses() {
if (addresses == null || addresses.size == 0) {
valid = false;
@@ -70,7 +70,7 @@ public class EmailEntry : Gtk.Entry {
return;
}
empty = false;
-
+
foreach (Geary.RFC822.MailboxAddress address in addresses) {
if (!address.is_valid()) {
valid = false;
@@ -79,14 +79,14 @@ public class EmailEntry : Gtk.Entry {
}
valid = true;
}
-
+
private bool on_key_press(Gtk.Widget widget, Gdk.EventKey event) {
if (event.keyval == Gdk.Key.Tab) {
((ContactEntryCompletion) get_completion()).trigger_selection();
composer.child_focus(Gtk.DirectionType.TAB_FORWARD);
return true;
}
-
+
return false;
}
}
diff --git a/src/client/conversation-list/conversation-list-cell-renderer.vala
b/src/client/conversation-list/conversation-list-cell-renderer.vala
index 94ab01a2..d8cccfb2 100644
--- a/src/client/conversation-list/conversation-list-cell-renderer.vala
+++ b/src/client/conversation-list/conversation-list-cell-renderer.vala
@@ -7,7 +7,7 @@
public class ConversationListCellRenderer : Gtk.CellRenderer {
private static FormattedConversationData? example_data = null;
private static bool hover_selected = false;
-
+
// Mail message data.
public FormattedConversationData data { get; set; }
@@ -37,29 +37,29 @@ public class ConversationListCellRenderer : Gtk.CellRenderer {
minimum_size = natural_size = 1;
}
- public override void render(Cairo.Context ctx, Gtk.Widget widget, Gdk.Rectangle background_area,
+ public override void render(Cairo.Context ctx, Gtk.Widget widget, Gdk.Rectangle background_area,
Gdk.Rectangle cell_area, Gtk.CellRendererState flags) {
if (data != null)
data.render(ctx, widget, background_area, cell_area, flags, hover_selected);
}
-
+
// Recalculates size when the style changed.
// Note: this must be called by the parent TreeView.
public static void style_changed(Gtk.Widget widget) {
if (example_data == null) {
example_data = new FormattedConversationData.create_example();
}
-
+
example_data.calculate_sizes(widget);
}
-
+
// Shows hover effect on all selected cells.
public static void set_hover_selected(bool hover) {
hover_selected = hover;
}
// This is implemented because it's required; ignore it and look at get_preferred_height() instead.
- public override void get_size(Gtk.Widget widget, Gdk.Rectangle? cell_area, out int x_offset,
+ public override void get_size(Gtk.Widget widget, Gdk.Rectangle? cell_area, out int x_offset,
out int y_offset, out int width, out int height) {
// Set values to avoid compiler warning.
x_offset = 0;
diff --git a/src/client/conversation-list/conversation-list-store.vala
b/src/client/conversation-list/conversation-list-store.vala
index c8f37c04..a63a6a22 100644
--- a/src/client/conversation-list/conversation-list-store.vala
+++ b/src/client/conversation-list/conversation-list-store.vala
@@ -32,7 +32,7 @@ public class ConversationListStore : Gtk.ListStore {
CONVERSATION_DATA,
CONVERSATION_OBJECT,
ROW_WRAPPER;
-
+
public static Type[] get_types() {
return {
typeof (FormattedConversationData), // CONVERSATION_DATA
@@ -40,37 +40,37 @@ public class ConversationListStore : Gtk.ListStore {
typeof (RowWrapper) // ROW_WRAPPER
};
}
-
+
public string to_string() {
switch (this) {
case CONVERSATION_DATA:
return "data";
-
+
case CONVERSATION_OBJECT:
return "envelope";
-
+
case ROW_WRAPPER:
return "wrapper";
-
+
default:
assert_not_reached();
}
}
}
-
+
private class RowWrapper : Geary.BaseObject {
public Geary.App.Conversation conversation;
public Gtk.TreeRowReference row;
-
+
public RowWrapper(Gtk.TreeModel model, Geary.App.Conversation conversation, Gtk.TreePath path) {
this.conversation = conversation;
this.row = new Gtk.TreeRowReference(model, path);
}
-
+
public Gtk.TreePath get_path() {
return row.get_path();
}
-
+
public bool get_iter(out Gtk.TreeIter iter) {
return row.get_model().get_iter(out iter, get_path());
}
@@ -145,10 +145,10 @@ public class ConversationListStore : Gtk.ListStore {
Gtk.TreeIter iter;
if (!get_iter(out iter, path))
return null;
-
+
return get_conversation_at_iter(iter);
}
-
+
private async void refresh_previews_async(Geary.App.ConversationMonitor conversation_monitor) {
// Use a mutex because it's possible for the conversation monitor to fire multiple
// "scan-started" signals as messages come in fast and furious, but only want to process
@@ -159,30 +159,30 @@ public class ConversationListStore : Gtk.ListStore {
token = yield refresh_mutex.claim_async(this.cancellable);
} catch (Error err) {
debug("Unable to claim refresh mutex: %s", err.message);
-
+
return;
}
-
+
preview_monitor.notify_start();
-
+
yield do_refresh_previews_async(conversation_monitor);
-
+
preview_monitor.notify_finish();
-
+
try {
refresh_mutex.release(ref token);
} catch (Error err) {
debug("Unable to release refresh mutex: %s", err.message);
}
}
-
+
// should only be called by refresh_previews_async()
private async void do_refresh_previews_async(Geary.App.ConversationMonitor conversation_monitor) {
if (conversation_monitor == null || !GearyApplication.instance.config.display_preview)
return;
-
+
Gee.Set<Geary.EmailIdentifier> needing_previews = get_emails_needing_previews();
-
+
Gee.ArrayList<Geary.Email> emails = new Gee.ArrayList<Geary.Email>();
if (needing_previews.size > 0)
emails.add_all(yield do_get_previews_async(needing_previews));
@@ -219,10 +219,10 @@ public class ConversationListStore : Gtk.ListStore {
return emails ?? new Gee.ArrayList<Geary.Email>();
}
-
+
private Gee.Set<Geary.EmailIdentifier> get_emails_needing_previews() {
Gee.Set<Geary.EmailIdentifier> needing = new Gee.HashSet<Geary.EmailIdentifier>();
-
+
// sort the conversations so the previews are fetched from the newest to the oldest, matching
// the user experience
Gee.TreeSet<Geary.App.Conversation> sorted_conversations = new Gee.TreeSet<Geary.App.Conversation>(
@@ -234,11 +234,11 @@ public class ConversationListStore : Gtk.ListStore {
foreach (Geary.Email email in
conversation.get_emails(Geary.App.Conversation.Ordering.RECV_DATE_ASCENDING)) {
if (email.email_flags.is_unread()) {
need_preview = email;
-
+
break;
}
}
-
+
// if all are read, use newest in-folder message, then newest out-of-folder if not
// present
if (need_preview == null) {
@@ -246,34 +246,34 @@ public class ConversationListStore : Gtk.ListStore {
if (need_preview == null)
continue;
}
-
+
Geary.Email? current_preview = get_preview_for_conversation(conversation);
-
+
// if all preview fields present and it's the same email, don't need to refresh
if (current_preview != null
&& need_preview.id.equal_to(current_preview.id)
&& current_preview.fields.is_all_set(ConversationListStore.WITH_PREVIEW_FIELDS)) {
continue;
}
-
+
needing.add(need_preview.id);
}
-
+
return needing;
}
-
+
private Geary.Email? get_preview_for_conversation(Geary.App.Conversation conversation) {
Gtk.TreeIter iter;
if (!get_iter_for_conversation(conversation, out iter)) {
debug("Unable to find preview for conversation");
-
+
return null;
}
-
+
FormattedConversationData? message_data = get_message_data_at_iter(iter);
return message_data == null ? null : message_data.preview;
}
-
+
private void set_preview_for_conversation(Geary.App.Conversation conversation, Geary.Email preview) {
Gtk.TreeIter iter;
if (get_iter_for_conversation(conversation, out iter))
@@ -293,16 +293,16 @@ public class ConversationListStore : Gtk.ListStore {
Gtk.TreePath? path = get_path(iter);
assert(path != null);
RowWrapper wrapper = new RowWrapper(this, conversation, path);
-
+
set(iter,
Column.CONVERSATION_DATA, conversation_data,
Column.CONVERSATION_OBJECT, conversation,
Column.ROW_WRAPPER, wrapper
);
-
+
row_map.set(conversation, wrapper);
}
-
+
private void refresh_conversation(Geary.App.Conversation conversation) {
Gtk.TreeIter iter;
if (!get_iter_for_conversation(conversation, out iter)) {
@@ -310,11 +310,11 @@ public class ConversationListStore : Gtk.ListStore {
add_conversation(conversation);
return;
}
-
+
Geary.Email? last_email =
conversation.get_latest_recv_email(Geary.App.Conversation.Location.ANYWHERE);
if (last_email == null) {
debug("Cannot refresh conversation: last email is null");
-
+
#if VALA_0_36
remove(ref iter);
#else
@@ -322,16 +322,16 @@ public class ConversationListStore : Gtk.ListStore {
#endif
return;
}
-
+
set_row(iter, conversation, last_email);
-
+
Gtk.TreePath? path = get_path(iter);
if (path != null)
row_changed(path, iter);
else
debug("Cannot refresh conversation: no path for iterator");
}
-
+
private void refresh_flags(Geary.App.Conversation conversation) {
Gtk.TreeIter iter;
if (!get_iter_for_conversation(conversation, out iter)) {
@@ -339,55 +339,55 @@ public class ConversationListStore : Gtk.ListStore {
add_conversation(conversation);
return;
}
-
+
FormattedConversationData? existing_message_data = get_message_data_at_iter(iter);
if (existing_message_data == null)
return;
-
+
existing_message_data.is_unread = conversation.is_unread();
existing_message_data.is_flagged = conversation.is_flagged();
-
+
Gtk.TreePath? path = get_path(iter);
if (path != null)
row_changed(path, iter);
}
-
+
public Gtk.TreePath? get_path_for_conversation(Geary.App.Conversation conversation) {
RowWrapper? wrapper = row_map.get(conversation);
-
+
return (wrapper != null) ? wrapper.get_path() : null;
}
-
+
private bool get_iter_for_conversation(Geary.App.Conversation conversation, out Gtk.TreeIter iter) {
RowWrapper? wrapper = row_map.get(conversation);
if (wrapper != null)
return wrapper.get_iter(out iter);
-
+
// use get_iter_first() because boxing Gtk.TreeIter with a nullable is problematic with
// current bindings
get_iter_first(out iter);
-
+
return false;
}
-
+
private bool has_conversation(Geary.App.Conversation conversation) {
return row_map.has_key(conversation);
}
-
+
private Geary.App.Conversation? get_conversation_at_iter(Gtk.TreeIter iter) {
Geary.App.Conversation? conversation;
get(iter, Column.CONVERSATION_OBJECT, out conversation);
-
+
return conversation;
}
-
+
private FormattedConversationData? get_message_data_at_iter(Gtk.TreeIter iter) {
FormattedConversationData? message_data;
get(iter, Column.CONVERSATION_DATA, out message_data);
-
+
return message_data;
}
-
+
private void remove_conversation(Geary.App.Conversation conversation) {
Gtk.TreeIter iter;
if (get_iter_for_conversation(conversation, out iter))
@@ -396,44 +396,44 @@ public class ConversationListStore : Gtk.ListStore {
#else
remove(iter);
#endif
-
+
row_map.unset(conversation);
}
-
+
private bool add_conversation(Geary.App.Conversation conversation) {
Geary.Email? last_email =
conversation.get_latest_recv_email(Geary.App.Conversation.Location.ANYWHERE);
if (last_email == null) {
debug("Cannot add conversation: last email is null");
-
+
return false;
}
-
+
if (has_conversation(conversation)) {
debug("Conversation already present; not adding");
-
+
return false;
}
-
+
Gtk.TreeIter iter;
append(out iter);
set_row(iter, conversation, last_email);
-
+
return true;
}
-
+
private void on_scan_completed(Geary.App.ConversationMonitor sender) {
refresh_previews_async.begin(sender);
loading_local_only = false;
}
-
+
private void on_conversations_added(Gee.Collection<Geary.App.Conversation> conversations) {
// this handler is used to initialize the display, so it's possible for an empty list to
// be passed in (the ConversationMonitor signal should never do this)
if (conversations.size == 0)
return;
-
+
conversations_added(true);
-
+
debug("Adding %d conversations.", conversations.size);
int added = 0;
foreach (Geary.App.Conversation conversation in conversations) {
@@ -441,10 +441,10 @@ public class ConversationListStore : Gtk.ListStore {
added++;
}
debug("Added %d/%d conversations.", added, conversations.size);
-
+
conversations_added(false);
}
-
+
private void on_conversations_removed(Gee.Collection<Geary.App.Conversation> conversations) {
conversations_removed(true);
foreach (Geary.App.Conversation removed in conversations)
@@ -459,18 +459,18 @@ public class ConversationListStore : Gtk.ListStore {
debug("Unable to append conversation; conversation not present in list store");
}
}
-
+
private void on_conversation_trimmed(Geary.App.Conversation conversation) {
refresh_conversation(conversation);
}
-
+
private void on_display_preview_changed() {
refresh_previews_async.begin(this.conversations);
}
-
+
private void on_email_flags_changed(Geary.App.Conversation conversation) {
refresh_flags(conversation);
-
+
// refresh previews because the oldest unread message is displayed as the preview, and if
// that's changed, need to change the preview
// TODO: need support code to load preview for single conversation, not scan all
@@ -485,10 +485,10 @@ public class ConversationListStore : Gtk.ListStore {
private bool update_date_string(Gtk.TreeModel model, Gtk.TreePath path, Gtk.TreeIter iter) {
FormattedConversationData? message_data;
model.get(iter, Column.CONVERSATION_DATA, out message_data);
-
+
if (message_data != null && message_data.update_date_string())
row_changed(path, iter);
-
+
// Continue iterating, don't stop
return false;
}
diff --git a/src/client/conversation-list/conversation-list-view.vala
b/src/client/conversation-list/conversation-list-view.vala
index 06d83fd5..8cc8160d 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -24,17 +24,17 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
private bool suppress_selection = false;
public signal void conversations_selected(Gee.Set<Geary.App.Conversation> selected);
-
+
// Signal for when a conversation has been double-clicked, or selected and enter is pressed.
public signal void conversation_activated(Geary.App.Conversation activated);
-
+
public virtual signal void load_more() {
enable_load_more = false;
}
-
+
public signal void mark_conversations(Gee.Collection<Geary.App.Conversation> conversations,
Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove, bool only_mark_preview);
-
+
public signal void visible_conversations_changed(Gee.Set<Geary.App.Conversation> visible);
@@ -59,16 +59,16 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
// Set up drag and drop.
Gtk.drag_source_set(this, Gdk.ModifierType.BUTTON1_MASK, FolderList.Tree.TARGET_ENTRY_LIST,
Gdk.DragAction.COPY | Gdk.DragAction.MOVE);
-
+
GearyApplication.instance.config.settings.changed[Configuration.DISPLAY_PREVIEW_KEY].connect(
on_display_preview_changed);
GearyApplication.instance.controller.notify[GearyController.PROP_CURRENT_CONVERSATION].
connect(on_conversation_monitor_changed);
-
+
// Watch for mouse events.
motion_notify_event.connect(on_motion_notify_event);
leave_notify_event.connect(on_leave_notify_event);
-
+
// GtkTreeView binds Ctrl+N to "move cursor to next". Not so interested in that, so we'll
// remove it.
unowned Gtk.BindingSet? binding_set = Gtk.BindingSet.find("GtkTreeView");
@@ -207,7 +207,7 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
debug("Parent was not scrolled window");
return null;
}
-
+
return parent.get_vadjustment();
}
@@ -217,16 +217,16 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
int cell_y;
Gtk.TreePath? path;
get_path_at_pos((int) event.x, (int) event.y, out path, null, out cell_x, out cell_y);
-
+
// If the user clicked in an empty area, do nothing.
if (path == null)
return false;
-
+
// Handle clicks to toggle read and starred status.
if ((event.state & Gdk.ModifierType.SHIFT_MASK) == 0 &&
(event.state & Gdk.ModifierType.CONTROL_MASK) == 0 &&
event.type == Gdk.EventType.BUTTON_PRESS) {
-
+
// Click positions depend on whether the preview is enabled.
bool read_clicked = false;
bool star_clicked = false;
@@ -237,7 +237,7 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
read_clicked = cell_x < 25 && cell_y >= 8 && cell_y <= 22;
star_clicked = cell_x < 25 && cell_y >= 28 && cell_y <= 43;
}
-
+
// Get the current conversation. If it's selected, we'll apply the mark operation to
// all selected conversations; otherwise, it just applies to this one.
Geary.App.Conversation conversation = get_model().get_conversation_at_path(path);
@@ -246,36 +246,36 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
to_mark = GearyApplication.instance.controller.get_selected_conversations();
else
to_mark = Geary.iterate<Geary.App.Conversation>(conversation).to_array_list();
-
+
if (read_clicked) {
// Read/unread.
Geary.EmailFlags flags = new Geary.EmailFlags();
flags.add(Geary.EmailFlags.UNREAD);
-
+
if (conversation.is_unread())
mark_conversations(to_mark, null, flags, false);
else
mark_conversations(to_mark, flags, null, true);
-
+
return true;
} else if (star_clicked) {
// Starred/unstarred.
Geary.EmailFlags flags = new Geary.EmailFlags();
flags.add(Geary.EmailFlags.FLAGGED);
-
+
if (conversation.is_flagged())
mark_conversations(to_mark, null, flags, false);
else
mark_conversations(to_mark, flags, null, true);
-
+
return true;
}
}
-
+
if (!get_selection().path_is_selected(path) &&
!GearyApplication.instance.controller.can_switch_conversation_view())
return true;
-
+
if (event.button == 3 && event.type == Gdk.EventType.BUTTON_PRESS) {
Geary.App.Conversation conversation = get_model().get_conversation_at_path(path);
@@ -313,19 +313,19 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
private void on_style_changed() {
// Recalculate dimensions of child cells.
ConversationListCellRenderer.style_changed(this);
-
+
schedule_visible_conversations_changed();
}
-
+
private void on_show() {
// Wait until we're visible to set this signal up.
((Gtk.Scrollable) this).get_vadjustment().value_changed.connect(on_value_changed);
}
-
+
private void on_value_changed() {
if (!enable_load_more)
return;
-
+
// Check if we're at the very bottom of the list. If we are, it's time to
// issue a load_more signal.
Gtk.Adjustment adjustment = ((Gtk.Scrollable) this).get_vadjustment();
@@ -335,24 +335,24 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
load_more();
last_upper = upper;
}
-
+
schedule_visible_conversations_changed();
}
-
+
private static Gtk.TreeViewColumn create_column(ConversationListStore.Column column,
Gtk.CellRenderer renderer, string attr, int width = 0) {
Gtk.TreeViewColumn view_column = new Gtk.TreeViewColumn.with_attributes(column.to_string(),
renderer, attr, column);
view_column.set_resizable(true);
-
+
if (width != 0) {
view_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED);
view_column.set_fixed_width(width);
}
-
+
return view_column;
}
-
+
private List<Gtk.TreePath> get_all_selected_paths() {
Gtk.TreeModel model;
return get_selection().get_selected_rows(out model);
@@ -399,35 +399,35 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
public Gee.Set<Geary.App.Conversation> get_visible_conversations() {
Gee.HashSet<Geary.App.Conversation> visible_conversations = new
Gee.HashSet<Geary.App.Conversation>();
-
+
Gtk.TreePath start_path;
Gtk.TreePath end_path;
if (!get_visible_range(out start_path, out end_path))
return visible_conversations;
-
+
while (start_path.compare(end_path) <= 0) {
Geary.App.Conversation? conversation = get_model().get_conversation_at_path(start_path);
if (conversation != null)
visible_conversations.add(conversation);
-
+
start_path.next();
}
-
+
return visible_conversations;
}
-
+
public Gee.Set<Geary.App.Conversation> get_selected_conversations() {
Gee.HashSet<Geary.App.Conversation> selected_conversations = new
Gee.HashSet<Geary.App.Conversation>();
-
+
foreach (Gtk.TreePath path in get_all_selected_paths()) {
Geary.App.Conversation? conversation = get_model().get_conversation_at_path(path);
if (path != null)
selected_conversations.add(conversation);
}
-
+
return selected_conversations;
}
-
+
// Always returns false, so it can be used as a one-time SourceFunc
private bool update_visible_conversations() {
Gee.Set<Geary.App.Conversation> visible_conversations = get_visible_conversations();
@@ -436,14 +436,14 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
current_visible_conversations, visible_conversations)) {
return false;
}
-
+
current_visible_conversations = visible_conversations;
-
+
visible_conversations_changed(current_visible_conversations.read_only_view);
-
+
return false;
}
-
+
private void schedule_visible_conversations_changed() {
scheduled_update_visible_conversations = Geary.Scheduler.on_idle(update_visible_conversations);
}
@@ -453,7 +453,7 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
if (path != null)
set_cursor(path, null, false);
}
-
+
public void select_conversations(Gee.Set<Geary.App.Conversation> conversations) {
Gtk.TreeSelection selection = get_selection();
foreach (Geary.App.Conversation conversation in conversations) {
@@ -462,36 +462,36 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
selection.select_path(path);
}
}
-
+
private void on_row_deleted(Gtk.TreePath path) {
// if one or more rows are deleted in the model, reset the last upper limit so scrolling to
// the bottom will always activate a reload (this is particularly important if the model
// is cleared)
last_upper = -1.0;
}
-
+
private void on_rows_changed() {
schedule_visible_conversations_changed();
}
-
+
private void on_display_preview_changed() {
style_updated();
model.foreach(refresh_path);
-
+
schedule_visible_conversations_changed();
}
-
+
private bool refresh_path(Gtk.TreeModel model, Gtk.TreePath path, Gtk.TreeIter iter) {
model.row_changed(path, iter);
return false;
}
-
+
private void on_row_activated(Gtk.TreePath path) {
Geary.App.Conversation? c = get_model().get_conversation_at_path(path);
if (c != null)
conversation_activated(c);
}
-
+
// Enable/disable hover effect on all selected cells.
private void set_hover_selected(bool hover) {
ConversationListCellRenderer.set_hover_selected(hover);
diff --git a/src/client/conversation-list/formatted-conversation-data.vala
b/src/client/conversation-list/formatted-conversation-data.vala
index f05b4845..5af42625 100644
--- a/src/client/conversation-list/formatted-conversation-data.vala
+++ b/src/client/conversation-list/formatted-conversation-data.vala
@@ -7,22 +7,22 @@
// Stores formatted data for a message.
public class FormattedConversationData : Geary.BaseObject {
public const int LINE_SPACING = 6;
-
+
private const string ME = _("Me");
private const string STYLE_EXAMPLE = "Gg"; // Use both upper and lower case to get max height.
private const int TEXT_LEFT = LINE_SPACING * 2 + IconFactory.UNREAD_ICON_SIZE;
private const double DIM_TEXT_AMOUNT = 0.05;
private const double DIM_PREVIEW_TEXT_AMOUNT = 0.25;
-
+
private const int FONT_SIZE_DATE = 10;
private const int FONT_SIZE_SUBJECT = 9;
private const int FONT_SIZE_FROM = 11;
private const int FONT_SIZE_PREVIEW = 8;
-
+
private class ParticipantDisplay : Geary.BaseObject, Gee.Hashable<ParticipantDisplay> {
public Geary.RFC822.MailboxAddress address;
public bool is_unread;
-
+
public ParticipantDisplay(Geary.RFC822.MailboxAddress address, bool is_unread) {
this.address = address;
this.is_unread = is_unread;
@@ -49,16 +49,16 @@ public class FormattedConversationData : Geary.BaseObject {
if (Geary.String.is_empty(short_address))
return get_full_markup(account_mailboxes);
}
-
+
// use first name as delimited by a space
string[] tokens = short_address.split(" ", 2);
if (tokens.length < 1)
return get_full_markup(account_mailboxes);
-
+
string first_name = tokens[0].strip();
if (Geary.String.is_empty_or_whitespace(first_name))
return get_full_markup(account_mailboxes);
-
+
return get_as_markup(first_name);
}
@@ -79,15 +79,15 @@ public class FormattedConversationData : Geary.BaseObject {
public bool equal_to(ParticipantDisplay other) {
return address.equal_to(other.address);
}
-
+
public uint hash() {
return address.hash();
}
}
-
+
private static int cell_height = -1;
private static int preview_height = -1;
-
+
public bool is_unread { get; set; }
public bool is_flagged { get; set; }
public string date { get; private set; }
@@ -95,12 +95,12 @@ public class FormattedConversationData : Geary.BaseObject {
public string? body { get; private set; default = null; } // optional
public int num_emails { get; set; }
public Geary.Email? preview { get; private set; default = null; }
-
+
private Geary.App.Conversation? conversation = null;
private Gee.List<Geary.RFC822.MailboxAddress>? account_owner_emails = null;
private bool use_to = true;
private CountBadge count_badge = new CountBadge(2);
-
+
// Creates a formatted message data from an e-mail.
public FormattedConversationData(Geary.App.Conversation conversation, Geary.Email preview,
Geary.Folder folder, Gee.List<Geary.RFC822.MailboxAddress> account_owner_emails) {
@@ -108,37 +108,37 @@ public class FormattedConversationData : Geary.BaseObject {
this.conversation = conversation;
this.account_owner_emails = account_owner_emails;
use_to = (folder != null) && folder.special_folder_type.is_outgoing();
-
+
// Load preview-related data.
update_date_string();
this.subject = EmailUtil.strip_subject_prefixes(preview);
this.body = Geary.String.reduce_whitespace(preview.get_preview_as_string());
this.preview = preview;
-
+
// Load conversation-related data.
this.is_unread = conversation.is_unread();
this.is_flagged = conversation.is_flagged();
this.num_emails = conversation.get_count();
}
-
+
public bool update_date_string() {
// get latest email *in folder* for the conversation's date, fall back on out-of-folder
Geary.Email? latest =
conversation.get_latest_recv_email(Geary.App.Conversation.Location.IN_FOLDER_OUT_OF_FOLDER);
if (latest == null || latest.properties == null)
return false;
-
+
// conversation list store sorts by date-received, so display that instead of sender's
// Date:
string new_date = Date.pretty_print(latest.properties.date_received,
GearyApplication.instance.config.clock_format);
if (new_date == date)
return false;
-
+
date = new_date;
-
+
return true;
}
-
+
// Creates an example message (used interally for styling calculations.)
public FormattedConversationData.create_example() {
this.is_unread = false;
@@ -148,20 +148,20 @@ public class FormattedConversationData : Geary.BaseObject {
this.body = STYLE_EXAMPLE + "\n" + STYLE_EXAMPLE;
this.num_emails = 1;
}
-
+
private uint8 gdk_to_rgb(double gdk) {
return (uint8) (gdk.clamp(0.0, 1.0) * 255.0);
}
-
+
private Gdk.RGBA dim_rgba(Gdk.RGBA rgba, double amount) {
amount = amount.clamp(0.0, 1.0);
-
+
// can't use ternary in struct initializer due to this bug:
// https://bugzilla.gnome.org/show_bug.cgi?id=684742
double dim_red = (rgba.red >= 0.5) ? -amount : amount;
double dim_green = (rgba.green >= 0.5) ? -amount : amount;
double dim_blue = (rgba.blue >= 0.5) ? -amount : amount;
-
+
return Gdk.RGBA() {
red = (rgba.red + dim_red).clamp(0.0, 1.0),
green = (rgba.green + dim_green).clamp(0.0, 1.0),
@@ -169,20 +169,20 @@ public class FormattedConversationData : Geary.BaseObject {
alpha = rgba.alpha
};
}
-
+
private string rgba_to_markup(Gdk.RGBA rgba) {
return "#%02x%02x%02x".printf(
gdk_to_rgb(rgba.red), gdk_to_rgb(rgba.green), gdk_to_rgb(rgba.blue));
}
-
+
private Gdk.RGBA get_foreground_rgba(Gtk.Widget widget, bool selected) {
return widget.get_style_context().get_color(selected ? Gtk.StateFlags.SELECTED :
Gtk.StateFlags.NORMAL);
}
-
+
private string get_participants_markup(Gtk.Widget widget, bool selected) {
if (conversation == null || account_owner_emails == null || account_owner_emails.size == 0)
return "";
-
+
// Build chronological list of AuthorDisplay records, setting to unread if any message by
// that author is unread
Gee.ArrayList<ParticipantDisplay> list = new Gee.ArrayList<ParticipantDisplay>();
@@ -191,7 +191,7 @@ public class FormattedConversationData : Geary.BaseObject {
Geary.RFC822.MailboxAddresses? addresses = use_to ? message.to : message.from;
if (addresses == null || addresses.size < 1)
continue;
-
+
foreach (Geary.RFC822.MailboxAddress address in addresses) {
ParticipantDisplay participant_display = new ParticipantDisplay(address,
message.email_flags.is_unread());
@@ -203,14 +203,14 @@ public class FormattedConversationData : Geary.BaseObject {
continue;
}
-
+
// if present and this message is unread but the prior were read,
// this author is now unread
if (message.email_flags.is_unread() && !list[existing_index].is_unread)
list[existing_index].is_unread = true;
}
}
-
+
StringBuilder builder = new StringBuilder("<span foreground='%s'>".printf(
rgba_to_markup(get_foreground_rgba(widget, selected))));
if (list.size == 1) {
@@ -221,42 +221,42 @@ public class FormattedConversationData : Geary.BaseObject {
foreach (ParticipantDisplay participant in list) {
if (!first)
builder.append(", ");
-
+
builder.append(participant.get_short_markup(account_owner_emails));
first = false;
}
}
builder.append("</span>");
-
+
return builder.str;
}
-
- public void render(Cairo.Context ctx, Gtk.Widget widget, Gdk.Rectangle background_area,
+
+ public void render(Cairo.Context ctx, Gtk.Widget widget, Gdk.Rectangle background_area,
Gdk.Rectangle cell_area, Gtk.CellRendererState flags, bool hover_select) {
render_internal(widget, cell_area, ctx, flags, false, hover_select);
}
-
+
// Call this on style changes.
public void calculate_sizes(Gtk.Widget widget) {
render_internal(widget, null, null, 0, true, false);
}
-
+
// Must call calculate_sizes() first.
public int get_height() {
assert(cell_height != -1); // ensures calculate_sizes() was called.
return cell_height;
}
-
+
// Can be used for rendering or calculating height.
- private void render_internal(Gtk.Widget widget, Gdk.Rectangle? cell_area,
+ private void render_internal(Gtk.Widget widget, Gdk.Rectangle? cell_area,
Cairo.Context? ctx, Gtk.CellRendererState flags, bool recalc_dims,
bool hover_select) {
bool display_preview = GearyApplication.instance.config.display_preview;
int y = LINE_SPACING + (cell_area != null ? cell_area.y : 0);
-
+
bool selected = (flags & Gtk.CellRendererState.SELECTED) != 0;
bool hover = (flags & Gtk.CellRendererState.PRELIT) != 0 || (selected && hover_select);
-
+
// Date field.
Pango.Rectangle ink_rect = render_date(widget, cell_area, ctx, y, selected);
@@ -267,33 +267,33 @@ public class FormattedConversationData : Geary.BaseObject {
// If we are displaying a preview then the message counter goes on the same line as the
// preview, otherwise it is with the subject.
int preview_height = 0;
-
+
// Setup counter badge.
count_badge.count = num_emails;
int counter_width = count_badge.get_width(widget) + LINE_SPACING;
int counter_x = cell_area != null ? cell_area.width - cell_area.x - counter_width +
(LINE_SPACING / 2) : 0;
-
+
if (display_preview) {
// Subject field.
render_subject(widget, cell_area, ctx, y, selected);
y += ink_rect.height + ink_rect.y + LINE_SPACING;
-
+
// Number of e-mails field.
count_badge.render(widget, ctx, counter_x, y, selected);
-
+
// Body preview.
ink_rect = render_preview(widget, cell_area, ctx, y, selected, counter_width);
preview_height = ink_rect.height + ink_rect.y + LINE_SPACING;
} else {
// Number of e-mails field.
count_badge.render(widget, ctx, counter_x, y, selected);
-
+
// Subject field.
render_subject(widget, cell_area, ctx, y, selected, counter_width);
y += ink_rect.height + ink_rect.y + LINE_SPACING;
}
-
+
// Draw separator line.
if (ctx != null && cell_area != null) {
ctx.set_line_width(1.0);
@@ -302,14 +302,14 @@ public class FormattedConversationData : Geary.BaseObject {
ctx.line_to(cell_area.x + cell_area.width + 1, cell_area.y + cell_area.height);
ctx.stroke();
}
-
+
if (recalc_dims) {
FormattedConversationData.preview_height = preview_height;
FormattedConversationData.cell_height = y + preview_height;
} else {
int unread_y = display_preview ? cell_area.y + LINE_SPACING * 2 : cell_area.y +
LINE_SPACING;
-
+
// Unread indicator.
if (is_unread || hover) {
Gdk.Pixbuf read_icon = IconFactory.instance.load_symbolic(
@@ -318,7 +318,7 @@ public class FormattedConversationData : Geary.BaseObject {
Gdk.cairo_set_source_pixbuf(ctx, read_icon, cell_area.x + LINE_SPACING, unread_y);
ctx.paint();
}
-
+
// Starred indicator.
if (is_flagged || hover) {
int star_y = cell_area.y + (cell_area.height / 2) + (display_preview ? LINE_SPACING : 0);
@@ -330,13 +330,13 @@ public class FormattedConversationData : Geary.BaseObject {
}
}
}
-
+
private Pango.Rectangle render_date(Gtk.Widget widget, Gdk.Rectangle? cell_area,
Cairo.Context? ctx, int y, bool selected) {
string date_markup = "<span foreground='%s'>%s</span>".printf(
rgba_to_markup(dim_rgba(get_foreground_rgba(widget, selected), DIM_TEXT_AMOUNT)),
Geary.HTML.escape_markup(date));
-
+
Pango.Rectangle? ink_rect;
Pango.Rectangle? logical_rect;
Pango.FontDescription font_date = new Pango.FontDescription();
@@ -352,11 +352,11 @@ public class FormattedConversationData : Geary.BaseObject {
}
return ink_rect;
}
-
+
private Pango.Rectangle render_from(Gtk.Widget widget, Gdk.Rectangle? cell_area,
Cairo.Context? ctx, int y, bool selected, Pango.Rectangle ink_rect) {
string from_markup = (conversation != null) ? get_participants_markup(widget, selected) :
STYLE_EXAMPLE;
-
+
Pango.FontDescription font_from = new Pango.FontDescription();
font_from.set_size(FONT_SIZE_FROM * Pango.SCALE);
Pango.Layout layout_from = widget.create_pango_layout(null);
@@ -372,13 +372,13 @@ public class FormattedConversationData : Geary.BaseObject {
}
return ink_rect;
}
-
+
private void render_subject(Gtk.Widget widget, Gdk.Rectangle? cell_area, Cairo.Context? ctx,
int y, bool selected, int counter_width = 0) {
string subject_markup = "<span foreground='%s'>%s</span>".printf(
rgba_to_markup(dim_rgba(get_foreground_rgba(widget, selected), DIM_TEXT_AMOUNT)),
Geary.HTML.escape_markup(subject));
-
+
Pango.FontDescription font_subject = new Pango.FontDescription();
font_subject.set_size(FONT_SIZE_SUBJECT * Pango.SCALE);
if (is_unread)
@@ -394,27 +394,27 @@ public class FormattedConversationData : Geary.BaseObject {
Pango.cairo_show_layout(ctx, layout_subject);
}
}
-
+
private Pango.Rectangle render_preview(Gtk.Widget widget, Gdk.Rectangle? cell_area,
Cairo.Context? ctx, int y, bool selected, int counter_width = 0) {
double dim = selected ? DIM_TEXT_AMOUNT : DIM_PREVIEW_TEXT_AMOUNT;
string preview_markup = "<span foreground='%s'>%s</span>".printf(
rgba_to_markup(dim_rgba(get_foreground_rgba(widget, selected), dim)),
Geary.String.is_empty(body) ? "" : Geary.HTML.escape_markup(body));
-
+
Pango.FontDescription font_preview = new Pango.FontDescription();
font_preview.set_size(FONT_SIZE_PREVIEW * Pango.SCALE);
-
+
Pango.Layout layout_preview = widget.create_pango_layout(null);
layout_preview.set_font_description(font_preview);
-
+
layout_preview.set_markup(preview_markup, -1);
layout_preview.set_wrap(Pango.WrapMode.WORD);
layout_preview.set_ellipsize(Pango.EllipsizeMode.END);
if (ctx != null && cell_area != null) {
layout_preview.set_width((cell_area.width - TEXT_LEFT - counter_width - LINE_SPACING) *
Pango.SCALE);
layout_preview.set_height(preview_height * Pango.SCALE);
-
+
ctx.move_to(cell_area.x + TEXT_LEFT, y);
Pango.cairo_show_layout(ctx, layout_preview);
} else {
@@ -427,6 +427,6 @@ public class FormattedConversationData : Geary.BaseObject {
layout_preview.get_pixel_extents(out ink_rect, out logical_rect);
return ink_rect;
}
-
+
}
diff --git a/src/client/dialogs/alert-dialog.vala b/src/client/dialogs/alert-dialog.vala
index 6acaa44a..ea3c4d57 100644
--- a/src/client/dialogs/alert-dialog.vala
+++ b/src/client/dialogs/alert-dialog.vala
@@ -6,22 +6,22 @@
class AlertDialog : Object {
private Gtk.MessageDialog dialog;
-
+
public AlertDialog(Gtk.Window? parent, Gtk.MessageType message_type, string title, string? description,
string? ok_button, string? cancel_button, string? tertiary_button,
Gtk.ResponseType tertiary_response_type, string? ok_action_type) {
dialog = new Gtk.MessageDialog(parent, Gtk.DialogFlags.DESTROY_WITH_PARENT, message_type,
Gtk.ButtonsType.NONE, "");
-
+
dialog.text = title;
dialog.secondary_text = description;
-
+
if (!Geary.String.is_empty_or_whitespace(tertiary_button))
dialog.add_button(tertiary_button, tertiary_response_type);
-
+
if (!Geary.String.is_empty_or_whitespace(cancel_button))
dialog.add_button(cancel_button, Gtk.ResponseType.CANCEL);
-
+
if (!Geary.String.is_empty_or_whitespace(ok_button)) {
Gtk.Widget? button = dialog.add_button(ok_button, Gtk.ResponseType.OK);
if (!Geary.String.is_empty_or_whitespace(ok_action_type)) {
@@ -29,11 +29,11 @@ class AlertDialog : Object {
}
}
}
-
+
public void use_secondary_markup(bool markup) {
dialog.secondary_use_markup = markup;
}
-
+
public Gtk.Box get_message_area() {
return (Gtk.Box) dialog.get_message_area();
}
@@ -43,19 +43,19 @@ class AlertDialog : Object {
if (to_focus != null)
to_focus.grab_focus();
}
-
+
// Runs dialog, destroys it, and returns selected response
public Gtk.ResponseType run() {
Gtk.ResponseType response = (Gtk.ResponseType) dialog.run();
-
+
dialog.destroy();
-
+
return response;
}
}
class ConfirmationDialog : AlertDialog {
- public ConfirmationDialog(Gtk.Window? parent, string title, string? description,
+ public ConfirmationDialog(Gtk.Window? parent, string title, string? description,
string? ok_button, string? ok_action_type = "") {
base (parent, Gtk.MessageType.WARNING, title, description, ok_button, Stock._CANCEL,
null, Gtk.ResponseType.NONE, ok_action_type);
@@ -79,35 +79,35 @@ class ErrorDialog : AlertDialog {
class QuestionDialog : AlertDialog {
public bool is_checked { get; private set; default = false; }
-
+
private Gtk.CheckButton? checkbutton = null;
-
- public QuestionDialog(Gtk.Window? parent, string title, string? description,
+
+ public QuestionDialog(Gtk.Window? parent, string title, string? description,
string yes_button, string no_button) {
base (parent, Gtk.MessageType.QUESTION, title, description, yes_button, no_button, null,
Gtk.ResponseType.NONE, "suggested-action");
}
-
+
public QuestionDialog.with_checkbox(Gtk.Window? parent, string title, string? description,
string yes_button, string no_button, string checkbox_label, bool checkbox_default) {
this (parent, title, description, yes_button, no_button);
-
+
checkbutton = new Gtk.CheckButton.with_mnemonic(checkbox_label);
checkbutton.active = checkbox_default;
checkbutton.halign = Gtk.Align.END;
checkbutton.toggled.connect(on_checkbox_toggled);
-
+
get_message_area().pack_start(checkbutton);
-
+
// this must be done once all the packing is completed
get_message_area().show_all();
// the check box may have grabbed keyboard focus, so we put it back to the button
set_focus_response(Gtk.ResponseType.OK);
-
+
is_checked = checkbox_default;
}
-
+
private void on_checkbox_toggled() {
is_checked = checkbutton.active;
}
diff --git a/src/client/dialogs/attachment-dialog.vala b/src/client/dialogs/attachment-dialog.vala
index ca7880f8..cb5e4af7 100644
--- a/src/client/dialogs/attachment-dialog.vala
+++ b/src/client/dialogs/attachment-dialog.vala
@@ -61,17 +61,17 @@ public class AttachmentDialog : Object {
chooser.set_preview_widget_active(false);
return;
}
-
+
// read the image format data first
int width = 0;
int height = 0;
Gdk.PixbufFormat? format = Gdk.Pixbuf.get_file_info(filename, out width, out height);
-
+
if (format == null) {
chooser.set_preview_widget_active(false);
return;
}
-
+
// if the image is too big, resize it
Gdk.Pixbuf pixbuf;
try {
@@ -80,23 +80,23 @@ public class AttachmentDialog : Object {
chooser.set_preview_widget_active(false);
return;
}
-
+
if (pixbuf == null) {
chooser.set_preview_widget_active(false);
return;
}
-
+
pixbuf = pixbuf.apply_embedded_orientation();
-
+
// distribute the extra space around the image
int extra_space = PREVIEW_SIZE - pixbuf.width;
int smaller_half = extra_space/2;
int larger_half = extra_space - smaller_half;
-
+
// pad the image manually (avoids rounding errors)
preview_image.set_margin_start(PREVIEW_PADDING + smaller_half);
preview_image.set_margin_end(PREVIEW_PADDING + larger_half);
-
+
// show the preview
preview_image.set_from_pixbuf(pixbuf);
chooser.set_preview_widget_active(true);
diff --git a/src/client/dialogs/certificate-warning-dialog.vala
b/src/client/dialogs/certificate-warning-dialog.vala
index e29ec9a8..030c3101 100644
--- a/src/client/dialogs/certificate-warning-dialog.vala
+++ b/src/client/dialogs/certificate-warning-dialog.vala
@@ -10,11 +10,11 @@ public class CertificateWarningDialog {
TRUST,
ALWAYS_TRUST
}
-
+
private const string BULLET = "• ";
-
+
private Gtk.Dialog dialog;
-
+
public CertificateWarningDialog(Gtk.Window? parent,
Geary.AccountInformation account,
Geary.ServiceInformation service,
@@ -25,7 +25,7 @@ public class CertificateWarningDialog {
dialog = (Gtk.Dialog) builder.get_object("CertificateWarningDialog");
dialog.transient_for = parent;
dialog.modal = true;
-
+
Gtk.Label title_label = (Gtk.Label) builder.get_object("untrusted_connection_label");
Gtk.Label top_label = (Gtk.Label) builder.get_object("top_label");
Gtk.Label warnings_label = (Gtk.Label) builder.get_object("warnings_label");
@@ -48,7 +48,7 @@ public class CertificateWarningDialog {
+_("Selecting āTrust This Serverā or āAlways Trust This Serverā may cause your username and
password to be transmitted insecurely.")
+ "</b>";
trust_label.use_markup = true;
-
+
if (is_validation) {
// could be a new or existing account
dont_trust_label.label =
@@ -64,51 +64,51 @@ public class CertificateWarningDialog {
+ "</b> ";
}
dont_trust_label.use_markup = true;
-
+
contact_label.label =
_("Contact your system administrator or email service provider if you have any question about
these issues.");
}
-
+
private static string generate_warning_list(TlsCertificateFlags warnings) {
StringBuilder builder = new StringBuilder();
-
+
if ((warnings & TlsCertificateFlags.UNKNOWN_CA) != 0)
builder.append(BULLET + _("The serverās certificate is not signed by a known authority") + "\n");
-
+
if ((warnings & TlsCertificateFlags.BAD_IDENTITY) != 0)
builder.append(BULLET + _("The serverās identity does not match the identity in the
certificate") + "\n");
-
+
if ((warnings & TlsCertificateFlags.EXPIRED) != 0)
builder.append(BULLET + _("The serverās certificate has expired") + "\n");
-
+
if ((warnings & TlsCertificateFlags.NOT_ACTIVATED) != 0)
builder.append(BULLET + _("The serverās certificate has not been activated") + "\n");
-
+
if ((warnings & TlsCertificateFlags.REVOKED) != 0)
builder.append(BULLET + _("The serverās certificate has been revoked and is now invalid") +
"\n");
-
+
if ((warnings & TlsCertificateFlags.INSECURE) != 0)
builder.append(BULLET + _("The serverās certificate is considered insecure") + "\n");
-
+
if ((warnings & TlsCertificateFlags.GENERIC_ERROR) != 0)
builder.append(BULLET + _("An error has occurred processing the serverās certificate") + "\n");
-
+
return builder.str;
}
-
+
public Result run() {
dialog.show_all();
int response = dialog.run();
dialog.destroy();
-
+
// these values are defined in the Glade file
switch (response) {
case 1:
return Result.TRUST;
-
+
case 2:
return Result.ALWAYS_TRUST;
-
+
default:
return Result.DONT_TRUST;
}
diff --git a/src/client/dialogs/password-dialog.vala b/src/client/dialogs/password-dialog.vala
index e8985cfc..a9fb8437 100644
--- a/src/client/dialogs/password-dialog.vala
+++ b/src/client/dialogs/password-dialog.vala
@@ -3,7 +3,7 @@
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
-
+
/**
* Displays a dialog for collecting the user's password, without allowing them to change their
* other data.
@@ -14,7 +14,7 @@ public class PasswordDialog {
// details: https://bugzilla.gnome.org/show_bug.cgi?id=679006
private const string PRIMARY_TEXT_MARKUP = "<span weight=\"bold\" size=\"larger\">%s</span>";
private const string PRIMARY_TEXT_FIRST_TRY = _("Geary requires your email password to continue");
-
+
private Gtk.Dialog dialog;
private Gtk.Entry entry_password;
private Gtk.CheckButton check_remember_password;
@@ -27,18 +27,18 @@ public class PasswordDialog {
Geary.AccountInformation account,
Geary.ServiceInformation service) {
Gtk.Builder builder = GioUtil.create_builder("password-dialog.glade");
-
+
dialog = (Gtk.Dialog) builder.get_object("PasswordDialog");
dialog.transient_for = parent;
dialog.set_type_hint(Gdk.WindowTypeHint.DIALOG);
dialog.set_default_response(Gtk.ResponseType.OK);
-
+
entry_password = (Gtk.Entry) builder.get_object("entry: password");
check_remember_password = (Gtk.CheckButton) builder.get_object("check: remember_password");
-
+
Gtk.Label label_username = (Gtk.Label) builder.get_object("label: username");
Gtk.Label label_smtp = (Gtk.Label) builder.get_object("label: smtp");
-
+
// Load translated text for labels with markup unsupported by glade.
Gtk.Label primary_text_label = (Gtk.Label) builder.get_object("primary_text_label");
primary_text_label.set_markup(PRIMARY_TEXT_MARKUP.printf(PRIMARY_TEXT_FIRST_TRY));
@@ -59,15 +59,15 @@ public class PasswordDialog {
}
ok_button = (Gtk.Button) builder.get_object("authenticate_button");
-
+
refresh_ok_button_sensitivity();
entry_password.changed.connect(refresh_ok_button_sensitivity);
}
-
+
private void refresh_ok_button_sensitivity() {
ok_button.sensitive = !Geary.String.is_empty_or_whitespace(entry_password.get_text());
}
-
+
public bool run() {
dialog.show();
@@ -76,9 +76,9 @@ public class PasswordDialog {
password = entry_password.get_text();
remember_password = check_remember_password.active;
}
-
+
dialog.destroy();
-
+
return (response == Gtk.ResponseType.OK);
}
}
diff --git a/src/client/dialogs/upgrade-dialog.vala b/src/client/dialogs/upgrade-dialog.vala
index efcd4e7a..55cf81ca 100644
--- a/src/client/dialogs/upgrade-dialog.vala
+++ b/src/client/dialogs/upgrade-dialog.vala
@@ -6,17 +6,17 @@
public class UpgradeDialog : Object {
public const string PROP_VISIBLE_NAME = "visible";
-
+
// Progress monitor associated with the upgrade.
public Geary.AggregateProgressMonitor monitor { public get; private set;
default = new Geary.AggregateProgressMonitor(); }
-
+
// Whether or not this dialog is visible.
public bool visible { get; set; }
-
+
private Gtk.Dialog dialog;
private Gee.HashSet<Cancellable> cancellables = new Gee.HashSet<Cancellable>();
-
+
/**
* Creates and loads the upgrade progress dialog.
*/
@@ -29,32 +29,32 @@ public class UpgradeDialog : Object {
monitor.start.connect(on_start);
monitor.finish.connect(on_close);
dialog.delete_event.connect(on_delete_event);
-
+
// Bind visibility flag.
dialog.bind_property(PROP_VISIBLE_NAME, this, PROP_VISIBLE_NAME, BindingFlags.BIDIRECTIONAL |
BindingFlags.SYNC_CREATE);
}
-
+
private void on_start() {
dialog.show();
}
-
+
private bool on_delete_event() {
// Don't allow window to close until we're finished.
return !monitor.is_in_progress;
}
-
+
private void on_close() {
// If the user quit the dialog before the upgrade completed, cancel everything.
if (monitor.is_in_progress) {
foreach(Cancellable c in cancellables)
c.cancel();
}
-
+
if (dialog.visible)
dialog.hide();
}
-
+
/**
* Add accounts before opening them.
*/
diff --git a/src/client/folder-list/folder-list-abstract-folder-entry.vala
b/src/client/folder-list/folder-list-abstract-folder-entry.vala
index f53dfcd0..24825296 100644
--- a/src/client/folder-list/folder-list-abstract-folder-entry.vala
+++ b/src/client/folder-list/folder-list-abstract-folder-entry.vala
@@ -11,19 +11,19 @@
*/
public abstract class FolderList.AbstractFolderEntry : Geary.BaseObject, Sidebar.Entry,
Sidebar.SelectableEntry {
public Geary.Folder folder { get; private set; }
-
+
public AbstractFolderEntry(Geary.Folder folder) {
this.folder = folder;
}
-
+
public abstract string get_sidebar_name();
-
+
public abstract string? get_sidebar_tooltip();
-
+
public abstract string? get_sidebar_icon();
-
+
public abstract int get_count();
-
+
public virtual string to_string() {
return "AbstractFolderEntry: " + get_sidebar_name();
}
diff --git a/src/client/folder-list/folder-list-account-branch.vala
b/src/client/folder-list/folder-list-account-branch.vala
index 70587d9e..a22f62ae 100644
--- a/src/client/folder-list/folder-list-account-branch.vala
+++ b/src/client/folder-list/folder-list-account-branch.vala
@@ -43,43 +43,43 @@ public class FolderList.AccountBranch : Sidebar.Branch {
private static int special_grouping_comparator(Sidebar.Entry a, Sidebar.Entry b) {
SpecialGrouping? grouping_a = a as SpecialGrouping;
SpecialGrouping? grouping_b = b as SpecialGrouping;
-
+
assert(grouping_a != null || grouping_b != null);
-
+
int position_a = (grouping_a != null ? grouping_a.position : 0);
int position_b = (grouping_b != null ? grouping_b.position : 0);
-
+
return position_a - position_b;
}
-
+
private static int special_folder_comparator(Sidebar.Entry a, Sidebar.Entry b) {
if (a is Sidebar.Grouping || b is Sidebar.Grouping)
return special_grouping_comparator(a, b);
-
+
assert(a is FolderEntry);
assert(b is FolderEntry);
-
+
FolderEntry entry_a = (FolderEntry) a;
FolderEntry entry_b = (FolderEntry) b;
Geary.SpecialFolderType type_a = entry_a.folder.special_folder_type;
Geary.SpecialFolderType type_b = entry_b.folder.special_folder_type;
-
+
assert(type_a != Geary.SpecialFolderType.NONE);
assert(type_b != Geary.SpecialFolderType.NONE);
-
+
// Special folders are ordered by their enum value.
return (int) type_a - (int) type_b;
}
-
+
private static int normal_folder_comparator(Sidebar.Entry a, Sidebar.Entry b) {
// Non-special folders are compared based on name.
return a.get_sidebar_name().collate(b.get_sidebar_name());
}
-
+
public FolderEntry? get_entry_for_path(Geary.FolderPath folder_path) {
return folder_entries.get(folder_path);
}
-
+
public void add_folder(Geary.Folder folder) {
Sidebar.Entry? graft_point = null;
FolderEntry folder_entry = new FolderEntry(folder);
@@ -87,7 +87,7 @@ public class FolderList.AccountBranch : Sidebar.Branch {
if (special_folder_type != Geary.SpecialFolderType.NONE) {
if (special_folder_type == Geary.SpecialFolderType.SEARCH)
return; // Don't show search folder under the account.
-
+
// Special folders go in the root of the account.
graft_point = get_root();
} else if (folder.path.is_top_level) {
@@ -123,18 +123,18 @@ public class FolderList.AccountBranch : Sidebar.Branch {
special_folder_type.to_string());
}
}
-
+
public void remove_folder(Geary.Folder folder) {
Sidebar.Entry? entry = folder_entries.get(folder.path);
if(entry == null) {
debug("Could not remove folder %s", folder.to_string());
return;
}
-
+
prune(entry);
folder_entries.unset(folder.path);
}
-
+
private void on_entry_removed(Sidebar.Entry entry) {
FolderEntry? folder_entry = entry as FolderEntry;
if (folder_entry != null && folder_entries.has_key(folder_entry.folder.path))
diff --git a/src/client/folder-list/folder-list-folder-entry.vala
b/src/client/folder-list/folder-list-folder-entry.vala
index 5a5396fe..3eda0554 100644
--- a/src/client/folder-list/folder-list-folder-entry.vala
+++ b/src/client/folder-list/folder-list-folder-entry.vala
@@ -8,7 +8,7 @@
public class FolderList.FolderEntry : FolderList.AbstractFolderEntry, Sidebar.InternalDropTargetEntry,
Sidebar.EmphasizableEntry {
private bool has_new;
-
+
public FolderEntry(Geary.Folder folder) {
base(folder);
has_new = false;
@@ -16,89 +16,89 @@ public class FolderList.FolderEntry : FolderList.AbstractFolderEntry, Sidebar.In
folder.properties.notify[Geary.FolderProperties.PROP_NAME_EMAIL_UNREAD].connect(on_counts_changed);
folder.display_name_changed.connect(on_display_name_changed);
}
-
+
~FolderEntry() {
folder.properties.notify[Geary.FolderProperties.PROP_NAME_EMAIL_TOTAL].disconnect(on_counts_changed);
folder.properties.notify[Geary.FolderProperties.PROP_NAME_EMAIL_UNREAD].disconnect(on_counts_changed);
folder.display_name_changed.disconnect(on_display_name_changed);
}
-
+
public override string get_sidebar_name() {
return folder.get_display_name();
}
-
+
public override string? get_sidebar_tooltip() {
// Label displaying total number of email messages in a folder
string total_msg = ngettext("%d message", "%d messages", folder.properties.email_total).
printf(folder.properties.email_total);
-
+
if (folder.properties.email_unread == 0)
return total_msg;
-
+
/// Label displaying number of unread email messages in a folder
string unread_msg = ngettext("%d unread", "%d unread", folder.properties.email_unread).
printf(folder.properties.email_unread);
-
+
/// This string represents the divider between two messages: "n messages" and "n unread",
/// shown in the folder list as a tooltip. Please use your languages conventions for
/// combining the two, i.e. a comma (",") for English; "6 messages, 3 unread"
return _("%s, %s").printf(total_msg, unread_msg);
}
-
+
public override string? get_sidebar_icon() {
switch (folder.special_folder_type) {
case Geary.SpecialFolderType.NONE:
return "tag-symbolic";
-
+
case Geary.SpecialFolderType.INBOX:
return "mail-inbox-symbolic";
-
+
case Geary.SpecialFolderType.DRAFTS:
return "mail-drafts-symbolic";
case Geary.SpecialFolderType.SENT:
return "mail-sent-symbolic";
-
+
case Geary.SpecialFolderType.FLAGGED:
return "starred-symbolic";
-
+
case Geary.SpecialFolderType.IMPORTANT:
return "task-due-symbolic";
-
+
case Geary.SpecialFolderType.ALL_MAIL:
case Geary.SpecialFolderType.ARCHIVE:
return "mail-archive-symbolic";
-
+
case Geary.SpecialFolderType.SPAM:
return "dialog-warning-symbolic";
-
+
case Geary.SpecialFolderType.TRASH:
return "user-trash-symbolic";
-
+
case Geary.SpecialFolderType.OUTBOX:
return "mail-outbox-symbolic";
-
+
default:
assert_not_reached();
}
}
-
+
public override string to_string() {
return "FolderEntry: " + get_sidebar_name();
}
-
+
public bool is_emphasized() {
return has_new;
}
-
+
public void set_has_new(bool has_new) {
if (this.has_new == has_new)
return;
-
+
this.has_new = has_new;
is_emphasized_changed(has_new);
}
-
+
public bool internal_drop_received(Gdk.DragContext context, Gtk.SelectionData data) {
// Copy or move?
Gdk.ModifierType mask;
@@ -113,29 +113,29 @@ public class FolderList.FolderEntry : FolderList.AbstractFolderEntry, Sidebar.In
return true;
}
-
+
private void on_counts_changed() {
sidebar_count_changed(get_count());
sidebar_tooltip_changed(get_sidebar_tooltip());
}
-
+
private void on_display_name_changed() {
sidebar_name_changed(folder.get_display_name());
}
-
+
public override int get_count() {
switch (folder.special_folder_type) {
// for Drafts and Outbox, interested in showing total count, not unread count
case Geary.SpecialFolderType.DRAFTS:
case Geary.SpecialFolderType.OUTBOX:
return folder.properties.email_total;
-
+
// only show counts for Inbox, Spam, and user folders
case Geary.SpecialFolderType.INBOX:
case Geary.SpecialFolderType.SPAM:
case Geary.SpecialFolderType.NONE:
return folder.properties.email_unread;
-
+
// otherwise, to avoid clutter, no counts displayed (but are available in tooltip)
default:
return 0;
diff --git a/src/client/folder-list/folder-list-inboxes-branch.vala
b/src/client/folder-list/folder-list-inboxes-branch.vala
index 96ce3542..9b4ab2ca 100644
--- a/src/client/folder-list/folder-list-inboxes-branch.vala
+++ b/src/client/folder-list/folder-list-inboxes-branch.vala
@@ -9,48 +9,48 @@
public class FolderList.InboxesBranch : Sidebar.Branch {
public Gee.HashMap<Geary.Account, InboxFolderEntry> folder_entries {
get; private set; default = new Gee.HashMap<Geary.Account, InboxFolderEntry>(); }
-
+
public InboxesBranch() {
base(new Sidebar.Header(_("Inboxes")),
Sidebar.Branch.Options.NONE, inbox_comparator);
}
-
+
private static int inbox_comparator(Sidebar.Entry a, Sidebar.Entry b) {
assert(a is InboxFolderEntry);
assert(b is InboxFolderEntry);
-
+
InboxFolderEntry entry_a = (InboxFolderEntry) a;
InboxFolderEntry entry_b = (InboxFolderEntry) b;
return Geary.AccountInformation.compare_ascending(entry_a.get_account_information(),
entry_b.get_account_information());
}
-
+
public InboxFolderEntry? get_entry_for_account(Geary.Account account) {
return folder_entries.get(account);
}
-
+
public void add_inbox(Geary.Folder inbox) {
assert(inbox.special_folder_type == Geary.SpecialFolderType.INBOX);
-
+
InboxFolderEntry folder_entry = new InboxFolderEntry(inbox);
graft(get_root(), folder_entry);
-
+
folder_entries.set(inbox.account, folder_entry);
inbox.account.information.notify["ordinal"].connect(on_ordinal_changed);
}
-
+
public void remove_inbox(Geary.Account account) {
Sidebar.Entry? entry = folder_entries.get(account);
if(entry == null) {
debug("Could not remove inbox for %s", account.to_string());
return;
}
-
+
account.information.notify["ordinal"].disconnect(on_ordinal_changed);
prune(entry);
folder_entries.unset(account);
}
-
+
private void on_ordinal_changed() {
reorder_all();
}
diff --git a/src/client/folder-list/folder-list-search-branch.vala
b/src/client/folder-list/folder-list-search-branch.vala
index ba6c64de..dc3c9ad4 100644
--- a/src/client/folder-list/folder-list-search-branch.vala
+++ b/src/client/folder-list/folder-list-search-branch.vala
@@ -11,7 +11,7 @@ public class FolderList.SearchBranch : Sidebar.RootOnlyBranch {
public SearchBranch(Geary.SearchFolder folder) {
base(new SearchEntry(folder));
}
-
+
public Geary.SearchFolder get_search_folder() {
return (Geary.SearchFolder) ((SearchEntry) get_root()).folder;
}
@@ -20,47 +20,47 @@ public class FolderList.SearchBranch : Sidebar.RootOnlyBranch {
public class FolderList.SearchEntry : FolderList.AbstractFolderEntry {
public SearchEntry(Geary.SearchFolder folder) {
base(folder);
-
+
Geary.Engine.instance.account_available.connect(on_accounts_changed);
Geary.Engine.instance.account_unavailable.connect(on_accounts_changed);
folder.properties.notify[Geary.FolderProperties.PROP_NAME_EMAIL_TOTAL].connect(
on_email_total_changed);
}
-
+
~SearchEntry() {
Geary.Engine.instance.account_available.disconnect(on_accounts_changed);
Geary.Engine.instance.account_unavailable.disconnect(on_accounts_changed);
folder.properties.notify[Geary.FolderProperties.PROP_NAME_EMAIL_TOTAL].disconnect(
on_email_total_changed);
}
-
+
public override string get_sidebar_name() {
return GearyApplication.instance.controller.get_num_accounts() == 1 ? _("Search") :
_("Search %s account").printf(folder.account.information.display_name);
}
-
+
public override string? get_sidebar_tooltip() {
int total = folder.properties.email_total;
return ngettext("%d result", "%d results", total).printf(total);
}
-
+
public override string? get_sidebar_icon() {
return "edit-find-symbolic";
}
-
+
public override string to_string() {
return "SearchEntry: " + folder.to_string();
}
-
+
private void on_accounts_changed() {
sidebar_name_changed(get_sidebar_name());
sidebar_tooltip_changed(get_sidebar_tooltip());
}
-
+
private void on_email_total_changed() {
sidebar_tooltip_changed(get_sidebar_tooltip());
}
-
+
public override int get_count() {
return 0;
}
diff --git a/src/client/folder-list/folder-list-special-grouping.vala
b/src/client/folder-list/folder-list-special-grouping.vala
index c9a2d953..f4a2d90d 100644
--- a/src/client/folder-list/folder-list-special-grouping.vala
+++ b/src/client/folder-list/folder-list-special-grouping.vala
@@ -10,11 +10,11 @@ public class FolderList.SpecialGrouping : Sidebar.Grouping {
// Must be != 0 and unique among SpecialGroupings. Bigger comes later
// in the list. If < 0, it comes before non-SpecialGroupings.
public int position { get; private set; }
-
+
public SpecialGrouping(int position, string name, string? icon,
string? tooltip = null) {
base(name, icon, tooltip);
-
+
this.position = position;
}
}
diff --git a/src/client/folder-list/folder-list-tree.vala b/src/client/folder-list/folder-list-tree.vala
index 0ad1969b..8fabcc0b 100644
--- a/src/client/folder-list/folder-list-tree.vala
+++ b/src/client/folder-list/folder-list-tree.vala
@@ -8,14 +8,14 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
public const Gtk.TargetEntry[] TARGET_ENTRY_LIST = {
{ "application/x-geary-mail", Gtk.TargetFlags.SAME_APP, 0 }
};
-
+
private const int INBOX_ORDINAL = -2; // First account branch is zero
private const int SEARCH_ORDINAL = -1;
-
+
public signal void folder_selected(Geary.Folder? folder);
public signal void copy_conversation(Geary.Folder folder);
public signal void move_conversation(Geary.Folder folder);
-
+
private Gee.HashMap<Geary.Account, AccountBranch> account_branches
= new Gee.HashMap<Geary.Account, AccountBranch>();
private InboxesBranch inboxes_branch = new InboxesBranch();
@@ -30,7 +30,7 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
// Set self as a drag destination.
Gtk.drag_dest_set(this, Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT,
TARGET_ENTRY_LIST, Gdk.DragAction.COPY | Gdk.DragAction.MOVE);
-
+
// GtkTreeView binds Ctrl+N to "move cursor to next". Not so interested in that, so we'll
// remove it.
unowned Gtk.BindingSet? binding_set = Gtk.BindingSet.find("GtkTreeView");
@@ -62,17 +62,17 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
private void drop_handler(Gdk.DragContext context, Sidebar.Entry? entry,
Gtk.SelectionData data, uint info, uint time) {
}
-
+
private FolderEntry? get_folder_entry(Geary.Folder folder) {
AccountBranch? account_branch = account_branches.get(folder.account);
return (account_branch == null ? null :
account_branch.get_entry_for_path(folder.path));
}
-
+
public override bool accept_cursor_changed() {
return GearyApplication.instance.controller.can_switch_conversation_view();
}
-
+
private void on_entry_selected(Sidebar.SelectableEntry selectable) {
AbstractFolderEntry? abstract_folder_entry = selectable as AbstractFolderEntry;
if (abstract_folder_entry != null)
@@ -83,45 +83,45 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
FolderEntry? entry = get_folder_entry(folder);
if (entry != null)
entry.set_has_new(count > 0);
-
+
if (has_branch(inboxes_branch)) {
InboxFolderEntry? inbox_entry = inboxes_branch.get_entry_for_account(folder.account);
if (inbox_entry != null)
inbox_entry.set_has_new(count > 0);
}
}
-
+
public void set_new_messages_monitor(NewMessagesMonitor? monitor) {
if (this.monitor != null) {
this.monitor.new_messages_arrived.disconnect(on_new_messages_changed);
this.monitor.new_messages_retired.disconnect(on_new_messages_changed);
}
-
+
this.monitor = monitor;
if (this.monitor != null) {
this.monitor.new_messages_arrived.connect(on_new_messages_changed);
this.monitor.new_messages_retired.connect(on_new_messages_changed);
}
}
-
+
public void set_user_folders_root_name(Geary.Account account, string name) {
if (account_branches.has_key(account))
account_branches.get(account).user_folder_group.rename(name);
}
-
+
public void add_folder(Geary.Folder folder) {
if (!account_branches.has_key(folder.account))
account_branches.set(folder.account, new AccountBranch(folder.account));
-
+
AccountBranch account_branch = account_branches.get(folder.account);
if (!has_branch(account_branch))
graft(account_branch, folder.account.information.ordinal);
-
+
if (account_branches.size > 1 && !has_branch(inboxes_branch))
graft(inboxes_branch, INBOX_ORDINAL); // The Inboxes branch comes first.
if (folder.special_folder_type == Geary.SpecialFolderType.INBOX)
inboxes_branch.add_inbox(folder);
-
+
folder.account.information.notify["ordinal"].connect(on_ordinal_changed);
account_branch.add_folder(folder);
}
@@ -130,28 +130,28 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
AccountBranch? account_branch = account_branches.get(folder.account);
assert(account_branch != null);
assert(has_branch(account_branch));
-
+
// If this is the current folder, unselect it.
Sidebar.Entry? entry = account_branch.get_entry_for_path(folder.path);
-
+
// if not found or found but not selected, see if the folder is in the Inboxes branch
if (has_branch(inboxes_branch) && (entry == null || !is_selected(entry))) {
InboxFolderEntry? inbox_entry = inboxes_branch.get_entry_for_account(folder.account);
if (inbox_entry != null && inbox_entry.folder == folder)
entry = inbox_entry;
}
-
+
// if found and selected, report nothing is selected in preparation for its removal
if (entry != null && is_selected(entry))
folder_selected(null);
-
+
// if Inbox, remove from inboxes branch, selected or not
if (folder.special_folder_type == Geary.SpecialFolderType.INBOX)
inboxes_branch.remove_inbox(folder.account);
-
+
account_branch.remove_folder(folder);
}
-
+
public void remove_account(Geary.Account account) {
account.information.notify["ordinal"].disconnect(on_ordinal_changed);
AccountBranch? account_branch = account_branches.get(account);
@@ -163,40 +163,40 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
break;
}
}
-
+
if (has_branch(account_branch))
prune(account_branch);
account_branches.unset(account);
}
-
+
Sidebar.Entry? entry = inboxes_branch.get_entry_for_account(account);
if (entry != null && is_selected(entry))
folder_selected(null);
-
+
inboxes_branch.remove_inbox(account);
-
+
if (account_branches.size <= 1 && has_branch(inboxes_branch))
prune(inboxes_branch);
}
-
+
public void select_folder(Geary.Folder folder) {
FolderEntry? entry = get_folder_entry(folder);
if (entry != null)
place_cursor(entry, false);
}
-
+
public bool select_inbox(Geary.Account account) {
if (!has_branch(inboxes_branch))
return false;
-
+
InboxFolderEntry? entry = inboxes_branch.get_entry_for_account(account);
if (entry == null)
return false;
-
+
place_cursor(entry, false);
return true;
}
-
+
public override bool drag_motion(Gdk.DragContext context, int x, int y, uint time) {
// Run the base version first.
bool ret = base.drag_motion(context, x, y, time);
@@ -212,11 +212,11 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
}
return ret;
}
-
+
private void on_ordinal_changed() {
if (account_branches.size <= 1)
return;
-
+
// Remove branches where the ordinal doesn't match the graft position.
Gee.ArrayList<AccountBranch> branches_to_reorder = new Gee.ArrayList<AccountBranch>();
foreach (AccountBranch branch in account_branches.values) {
@@ -225,12 +225,12 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
branches_to_reorder.add(branch);
}
}
-
+
// Re-add branches with new positions.
foreach (AccountBranch branch in branches_to_reorder)
graft(branch, branch.account.information.ordinal);
}
-
+
public void set_search(Geary.SearchFolder search_folder) {
if (search_branch != null && has_branch(search_branch)) {
// We already have a search folder. If it's the same one, just
@@ -243,12 +243,12 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface {
remove_search();
}
}
-
+
search_branch = new SearchBranch(search_folder);
graft(search_branch, SEARCH_ORDINAL);
place_cursor(search_branch.get_root(), false);
}
-
+
public void remove_search() {
if (search_branch != null) {
prune(search_branch);
diff --git a/src/client/notification/libmessagingmenu.vala b/src/client/notification/libmessagingmenu.vala
index a2605513..1624fe80 100644
--- a/src/client/notification/libmessagingmenu.vala
+++ b/src/client/notification/libmessagingmenu.vala
@@ -7,29 +7,29 @@
public class Libmessagingmenu : NewMessagesIndicator {
#if HAVE_LIBMESSAGINGMENU
private MessagingMenu.App? app = null;
-
+
public Libmessagingmenu(NewMessagesMonitor monitor) {
base (monitor);
-
+
File? desktop_file = GearyApplication.instance.get_desktop_file();
if (desktop_file == null
|| !desktop_file.has_prefix(GearyApplication.instance.get_install_prefix_dir())) {
debug("Only an installed version of Geary with its .desktop file installed can use Messaging
Menu");
-
+
return;
}
-
+
app = new MessagingMenu.App("org.gnome.Geary.desktop");
app.register();
app.activate_source.connect(on_activate_source);
-
+
monitor.folder_removed.connect(on_folder_removed);
monitor.new_messages_arrived.connect(on_new_messages_changed);
monitor.new_messages_retired.connect(on_new_messages_changed);
-
+
debug("Registered messaging-menu indicator");
}
-
+
~Libmessagingmenu() {
if (app != null) {
monitor.folder_removed.disconnect(on_folder_removed);
@@ -50,36 +50,36 @@ public class Libmessagingmenu : NewMessagesIndicator {
}
}
}
-
+
private void on_new_messages_changed(Geary.Folder folder, int count) {
if (count > 0)
show_new_messages_count(folder, count);
else
remove_new_messages_count(folder);
}
-
+
private void on_folder_removed(Geary.Folder folder) {
remove_new_messages_count(folder);
}
-
+
private void show_new_messages_count(Geary.Folder folder, int count) {
if (!GearyApplication.instance.config.show_notifications ||
!monitor.should_notify_new_messages(folder))
return;
-
+
string source_id = get_source_id(folder);
-
+
if (app.has_source(source_id))
app.set_source_count(source_id, count);
else
app.append_source_with_count(source_id, null,
_("%s ā New Messages").printf(folder.account.information.display_name), count);
-
+
app.draw_attention(source_id);
}
-
+
private void remove_new_messages_count(Geary.Folder folder) {
string source_id = get_source_id(folder);
-
+
if (app.has_source(source_id)) {
app.remove_attention(source_id);
app.remove_source(source_id);
diff --git a/src/client/notification/libnotify.vala b/src/client/notification/libnotify.vala
index 059e1978..fd53be55 100644
--- a/src/client/notification/libnotify.vala
+++ b/src/client/notification/libnotify.vala
@@ -29,14 +29,14 @@ public class Libnotify : Geary.BaseObject {
this.avatars = avatars;
monitor.add_required_fields(REQUIRED_FIELDS);
-
+
if (!Notify.is_initted()) {
if (!Notify.init(GearyApplication.PRGNAME))
message("Failed to initialize libnotify.");
}
-
+
init_sound();
-
+
// This will return null if no notification server is present
this.caps = Notify.get_server_caps();
@@ -47,7 +47,7 @@ public class Libnotify : Geary.BaseObject {
if (sound_context == null)
Canberra.Context.create(out sound_context);
}
-
+
private void on_new_messages_arrived(Geary.Folder folder, int total, int added) {
if (added == 1 && monitor.last_new_message_folder != null &&
monitor.last_new_message != null) {
@@ -57,21 +57,21 @@ public class Libnotify : Geary.BaseObject {
notify_new_mail(folder, added);
}
}
-
+
private void on_default_action(Notify.Notification notification, string action) {
invoked(folder, email);
GearyApplication.instance.activate();
}
-
+
private void notify_new_mail(Geary.Folder folder, int added) {
// don't pass email if invoked
this.folder = null;
email = null;
-
+
if (!GearyApplication.instance.config.show_notifications ||
!monitor.should_notify_new_messages(folder))
return;
-
+
string body = ngettext("%d new message", "%d new messages", added).printf(added);
int total = monitor.get_new_message_count(folder);
if (total > added) {
@@ -85,23 +85,23 @@ public class Libnotify : Geary.BaseObject {
private async void notify_one_message_async(Geary.Folder folder, Geary.Email email,
GLib.Cancellable? cancellable) throws GLib.Error {
assert(email.fields.fulfills(REQUIRED_FIELDS));
-
+
// used if notification is invoked
this.folder = folder;
this.email = email;
-
+
if (!GearyApplication.instance.config.show_notifications ||
!monitor.should_notify_new_messages(folder))
return;
-
+
// possible to receive email with no originator
Geary.RFC822.MailboxAddress? primary = email.get_primary_originator();
if (primary == null) {
notify_new_mail(folder, 1);
-
+
return;
}
-
+
string body;
int count = monitor.get_new_message_count(folder);
if (count <= 1) {
@@ -126,14 +126,14 @@ public class Libnotify : Geary.BaseObject {
} catch (Error err) {
debug("Unable to close current libnotify notification: %s", err.message);
}
-
+
current_notification = null;
}
-
+
current_notification = issue_notification("email.arrived", summary, body, icon, "message-new_email");
-
+
}
-
+
private Notify.Notification? issue_notification(string category, string summary,
string body, Gdk.Pixbuf? icon, string? sound) {
if (this.caps == null)
@@ -147,38 +147,38 @@ public class Libnotify : Geary.BaseObject {
notification.set_hint_string("desktop-entry", "org.gnome.Geary");
if (caps.find_custom("actions", GLib.strcmp) != null)
notification.add_action("default", _("Open"), on_default_action);
-
+
notification.set_category(category);
notification.set("summary", summary);
notification.set("body", body);
-
+
if (icon != null)
notification.set_image_from_pixbuf(icon);
-
+
if (sound != null) {
if (caps.find("sound") != null)
notification.set_hint_string("sound-name", sound);
else
play_sound(sound);
}
-
+
try {
notification.show();
} catch (Error err) {
message("Unable to show notification: %s", err.message);
}
-
+
return notification;
}
-
+
public static void play_sound(string sound) {
if (!GearyApplication.instance.config.play_sounds)
return;
-
+
init_sound();
sound_context.play(0, Canberra.PROP_EVENT_ID, sound);
}
-
+
public void set_error_notification(string summary, string body) {
// Only one error at a time, guys. (This means subsequent errors will
// be dropped. Since this is only used for one thing now, that's ok,
@@ -196,7 +196,7 @@ public class Libnotify : Geary.BaseObject {
} catch (Error err) {
debug("Unable to close libnotify error notification: %s", err.message);
}
-
+
error_notification = null;
}
}
diff --git a/src/client/notification/new-messages-indicator.vala
b/src/client/notification/new-messages-indicator.vala
index 12128b4e..d9da3439 100644
--- a/src/client/notification/new-messages-indicator.vala
+++ b/src/client/notification/new-messages-indicator.vala
@@ -9,36 +9,36 @@
public abstract class NewMessagesIndicator : Geary.BaseObject {
protected NewMessagesMonitor monitor;
-
+
public signal void application_activated(uint32 timestamp);
-
+
public signal void inbox_activated(Geary.Folder folder, uint32 timestamp);
-
+
public signal void composer_activated(uint32 timestamp);
-
+
protected NewMessagesIndicator(NewMessagesMonitor monitor) {
this.monitor = monitor;
}
-
+
public static NewMessagesIndicator create(NewMessagesMonitor monitor) {
NewMessagesIndicator? indicator = null;
-
+
// Indicators are ordered from most to least prefered. If more than one is available,
// use the first.
-
+
#if HAVE_LIBMESSAGINGMENU
if (indicator == null)
indicator = new Libmessagingmenu(monitor);
#endif
-
+
if (indicator == null)
indicator = new NullIndicator(monitor);
-
+
assert(indicator != null);
-
+
return indicator;
}
-
+
// Returns time as a uint32 (suitable for signals if event doesn't supply it)
protected uint32 now() {
return (uint32) TimeVal().tv_sec;
diff --git a/src/client/notification/new-messages-monitor.vala
b/src/client/notification/new-messages-monitor.vala
index 7e78da6d..c18c2edc 100644
--- a/src/client/notification/new-messages-monitor.vala
+++ b/src/client/notification/new-messages-monitor.vala
@@ -13,44 +13,44 @@
public class NewMessagesMonitor : Geary.BaseObject {
public delegate bool ShouldNotifyNewMessages(Geary.Folder folder);
-
+
private class MonitorInformation : Geary.BaseObject {
public Geary.Folder folder;
public Cancellable? cancellable = null;
public int count = 0;
public Gee.HashSet<Geary.EmailIdentifier> new_ids
= new Gee.HashSet<Geary.EmailIdentifier>();
-
+
public MonitorInformation(Geary.Folder folder, Cancellable? cancellable) {
this.folder = folder;
this.cancellable = cancellable;
}
}
-
+
public Geary.Email.Field required_fields { get; private set; default = Geary.Email.Field.FLAGS; }
public int total_new_messages { get; private set; default = 0; }
public Geary.Folder? last_new_message_folder { get; private set; default = null; }
public Geary.Email? last_new_message { get; private set; default = null; }
-
+
private Gee.HashMap<Geary.Folder, MonitorInformation> folder_information
= new Gee.HashMap<Geary.Folder, MonitorInformation>();
private unowned ShouldNotifyNewMessages? _should_notify_new_messages;
-
+
public signal void folder_added(Geary.Folder folder);
-
+
public signal void folder_removed(Geary.Folder folder);
-
+
/**
* Fired when the monitor finds new messages on a folder.
*/
public signal void new_messages_arrived(Geary.Folder folder, int total, int added);
-
+
/**
* Fired when the monitor clears the "new" status of some messages in the
* folder.
*/
public signal void new_messages_retired(Geary.Folder folder, int total);
-
+
public NewMessagesMonitor(ShouldNotifyNewMessages? should_notify_new_messages) {
_should_notify_new_messages = should_notify_new_messages;
}
@@ -58,31 +58,31 @@ public class NewMessagesMonitor : Geary.BaseObject {
public bool should_notify_new_messages(Geary.Folder folder) {
return (_should_notify_new_messages == null ? true : _should_notify_new_messages(folder));
}
-
+
public void add_folder(Geary.Folder folder, Cancellable? cancellable = null) {
assert(!folder_information.has_key(folder));
-
+
folder.email_locally_appended.connect(on_email_locally_appended);
folder.email_flags_changed.connect(on_email_flags_changed);
folder.email_removed.connect(on_email_removed);
-
+
folder_information.set(folder, new MonitorInformation(folder, cancellable));
-
+
folder_added(folder);
}
-
+
public void remove_folder(Geary.Folder folder) {
if (!folder_information.has_key(folder))
return;
-
+
folder.email_locally_appended.disconnect(on_email_locally_appended);
folder.email_flags_changed.disconnect(on_email_flags_changed);
folder.email_removed.disconnect(on_email_removed);
-
+
total_new_messages -= folder_information.get(folder).count;
-
+
folder_information.unset(folder);
-
+
folder_removed(folder);
}
@@ -97,66 +97,66 @@ public class NewMessagesMonitor : Geary.BaseObject {
public Gee.Collection<Geary.Folder> get_folders() {
return folder_information.keys;
}
-
+
public int get_new_message_count(Geary.Folder folder) {
assert(folder_information.has_key(folder));
-
+
return folder_information.get(folder).count;
}
-
+
public void add_required_fields(Geary.Email.Field fields) {
required_fields |= fields;
}
-
+
public bool are_any_new_messages(Geary.Folder folder,
Gee.Collection<Geary.EmailIdentifier> ids) {
assert(folder_information.has_key(folder));
MonitorInformation info = folder_information.get(folder);
-
+
foreach (Geary.EmailIdentifier id in ids) {
if (info.new_ids.contains(id))
return true;
}
-
+
return false;
}
-
+
private void on_email_locally_appended(Geary.Folder folder,
Gee.Collection<Geary.EmailIdentifier> email_ids) {
do_process_new_email.begin(folder, email_ids);
}
-
+
private void on_email_flags_changed(Geary.Folder folder,
Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> ids) {
retire_new_messages(folder, ids.keys);
}
-
+
private void on_email_removed(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids) {
retire_new_messages(folder, ids);
}
-
+
private async void do_process_new_email(Geary.Folder folder,
Gee.Collection<Geary.EmailIdentifier> email_ids) {
MonitorInformation info = folder_information.get(folder);
-
+
try {
Gee.List<Geary.Email>? list = yield folder.list_email_by_sparse_id_async(email_ids,
required_fields, Geary.Folder.ListFlags.NONE, info.cancellable);
if (list == null || list.size == 0) {
debug("Warning: %d new emails, but none could be listed", email_ids.size);
-
+
return;
}
-
+
new_messages(info, list);
-
+
debug("do_process_new_email: %d messages listed, %d unread in folder %s",
list.size, info.count, folder.to_string());
} catch (Error err) {
debug("Unable to notify of new email: %s", err.message);
}
}
-
+
private void new_messages(MonitorInformation info, Gee.Collection<Geary.Email> emails) {
int appended_count = 0;
foreach (Geary.Email email in emails) {
@@ -164,69 +164,69 @@ public class NewMessagesMonitor : Geary.BaseObject {
debug("Warning: new message %s (%Xh) does not fulfill NewMessagesMonitor required fields of
%Xh",
email.id.to_string(), email.fields, required_fields);
}
-
+
if (info.new_ids.contains(email.id))
continue;
-
+
if (!email.email_flags.is_unread())
continue;
-
+
last_new_message_folder = info.folder;
last_new_message = email;
-
+
info.new_ids.add(email.id);
appended_count++;
}
-
+
update_count(info, true, appended_count);
}
-
+
private void retire_new_messages(Geary.Folder folder,
Gee.Collection<Geary.EmailIdentifier> email_ids) {
MonitorInformation info = folder_information.get(folder);
-
+
int removed_count = 0;
foreach (Geary.EmailIdentifier email_id in email_ids) {
if (last_new_message != null && last_new_message.id.equal_to(email_id)) {
last_new_message_folder = null;
last_new_message = null;
}
-
+
if (info.new_ids.remove(email_id))
removed_count++;
}
-
+
update_count(info, false, removed_count);
}
-
+
public void clear_new_messages(Geary.Folder folder) {
assert(folder_information.has_key(folder));
MonitorInformation info = folder_information.get(folder);
-
+
info.new_ids.clear();
last_new_message_folder = null;
last_new_message = null;
-
+
update_count(info, false, 0);
}
-
+
public void clear_all_new_messages() {
foreach(Geary.Folder folder in folder_information.keys)
clear_new_messages(folder);
}
-
+
private void update_count(MonitorInformation info, bool arrived, int delta) {
int new_size = info.new_ids.size;
-
+
// Documentation for "notify" signal seems to suggest that it's possible for the signal to
// fire even if the value of the property doesn't change. Since this signal can trigger
// big events, want to avoid firing it unless necessary
if (info.count == new_size)
return;
-
+
total_new_messages += new_size - info.count;
info.count = new_size;
-
+
if (arrived)
new_messages_arrived(info.folder, info.count, delta);
else
diff --git a/src/client/notification/null-indicator.vala b/src/client/notification/null-indicator.vala
index eac95faf..ca3950e8 100644
--- a/src/client/notification/null-indicator.vala
+++ b/src/client/notification/null-indicator.vala
@@ -9,7 +9,7 @@
public class NullIndicator : NewMessagesIndicator {
public NullIndicator(NewMessagesMonitor monitor) {
base (monitor);
-
+
debug("No messaging menu support in this build");
}
}
diff --git a/src/client/notification/unity-launcher.vala b/src/client/notification/unity-launcher.vala
index cbf53a36..8b6e7230 100644
--- a/src/client/notification/unity-launcher.vala
+++ b/src/client/notification/unity-launcher.vala
@@ -8,24 +8,24 @@ public class UnityLauncher : Geary.BaseObject {
#if HAVE_LIBUNITY
private NewMessagesMonitor? monitor = null;
private Unity.LauncherEntry? entry = null;
-
+
public UnityLauncher(NewMessagesMonitor monitor) {
this.monitor = monitor;
-
+
entry = Unity.LauncherEntry.get_for_desktop_id("org.gnome.Geary.desktop");
set_count(0);
-
+
monitor.folder_removed.connect(on_folder_removed);
monitor.new_messages_arrived.connect(on_new_messages_changed);
monitor.new_messages_retired.connect(on_new_messages_changed);
}
-
+
~UnityLauncher() {
monitor.folder_removed.disconnect(on_folder_removed);
monitor.new_messages_arrived.disconnect(on_new_messages_changed);
monitor.new_messages_retired.disconnect(on_new_messages_changed);
}
-
+
private void update_count() {
// This is the dead-simple approach. It could be optimized, but
// doesn't seem like it's worth too much effort.
@@ -34,20 +34,20 @@ public class UnityLauncher : Geary.BaseObject {
if (monitor.should_notify_new_messages(folder))
count += monitor.get_new_message_count(folder);
}
-
+
set_count(count);
}
-
+
private void set_count(int count) {
entry.count = count;
entry.count_visible = (count != 0);
debug("set unity launcher entry count to %s", entry.count.to_string());
}
-
+
private void on_new_messages_changed() {
update_count();
}
-
+
private void on_folder_removed() {
update_count();
}
diff --git a/src/client/sidebar/sidebar-branch.vala b/src/client/sidebar/sidebar-branch.vala
index 4caf0deb..d641a300 100644
--- a/src/client/sidebar/sidebar-branch.vala
+++ b/src/client/sidebar/sidebar-branch.vala
@@ -14,68 +14,68 @@ public class Sidebar.Branch : Geary.BaseObject {
AUTO_OPEN_ON_NEW_CHILD,
STARTUP_EXPAND_TO_FIRST_CHILD,
STARTUP_OPEN_GROUPING;
-
+
public bool is_hide_if_empty() {
return (this & HIDE_IF_EMPTY) != 0;
}
-
+
public bool is_auto_open_on_new_child() {
return (this & AUTO_OPEN_ON_NEW_CHILD) != 0;
}
-
+
public bool is_startup_expand_to_first_child() {
return (this & STARTUP_EXPAND_TO_FIRST_CHILD) != 0;
}
-
+
public bool is_startup_open_grouping() {
return (this & STARTUP_OPEN_GROUPING) != 0;
}
}
-
+
private class Node {
public delegate void PruneCallback(Node node);
-
+
public delegate void ChildrenReorderedCallback(Node node);
-
+
public Sidebar.Entry entry;
public weak Node? parent;
public CompareFunc<Sidebar.Entry> comparator;
public Gee.SortedSet<Node>? children = null;
-
+
public Node(Sidebar.Entry entry, Node? parent, CompareFunc<Sidebar.Entry> comparator) {
this.entry = entry;
this.parent = parent;
this.comparator = comparator;
}
-
+
private static int comparator_wrapper(Node anode, Node bnode) {
if (anode == bnode)
return 0;
-
+
assert(anode.parent == bnode.parent);
-
+
return anode.parent.comparator(anode.entry, bnode.entry);
}
-
+
public bool has_children() {
return (children != null && children.size > 0);
}
-
+
public void add_child(Node child) {
child.parent = this;
if (children == null)
children = new Gee.TreeSet<Node>(comparator_wrapper);
-
+
bool added = children.add(child);
assert(added);
}
-
+
public void remove_child(Node child) {
assert(children != null);
-
+
Gee.SortedSet<Node> new_children = new Gee.TreeSet<Node>(comparator_wrapper);
-
+
// For similar reasons as in reorder_child(), can't rely on TreeSet to locate this
// node because we need reference equality.
bool found = false;
@@ -85,58 +85,58 @@ public class Sidebar.Branch : Geary.BaseObject {
else
found = true;
}
-
+
assert(found);
-
+
if (new_children.size != 0)
children = new_children;
else
children = null;
-
+
child.parent = null;
}
-
+
public void prune_children(PruneCallback cb) {
if (children == null)
return;
-
+
foreach (Node child in children)
child.prune_children(cb);
-
+
Gee.SortedSet<Node> old_children = children;
children = null;
-
+
// Although this could've been done in the prior loop, it means notifying that
// a child has been removed prior to it being removed; this can cause problem
// if a signal handler calls back into the Tree to examine/add/remove nodes.
foreach (Node child in old_children)
cb(child);
}
-
+
// This returns the index of the Node purely by reference equality, making it useful if
// the criteria the Node is sorted upon has changed.
public int index_of_by_reference(Node child) {
if (children == null)
return -1;
-
+
int index = 0;
foreach (Node c in children) {
if (child == c)
return index;
-
+
index++;
}
-
+
return -1;
}
-
+
// Returns true if child moved when reordered.
public bool reorder_child(Node child) {
assert(children != null);
-
+
int old_index = index_of_by_reference(child);
assert(old_index >= 0);
-
+
// Because Gee.SortedSet uses the comparator for equality, if the Node's entry state
// has changed in such a way that the item is no longer sorted properly, the SortedSet's
// search and remove methods are useless. Makes no difference if children.remove() is
@@ -146,287 +146,287 @@ public class Sidebar.Branch : Geary.BaseObject {
Gee.SortedSet<Node> new_children = new Gee.TreeSet<Node>(comparator_wrapper);
bool added = new_children.add_all(children);
assert(added);
-
+
children = new_children;
-
+
int new_index = index_of_by_reference(child);
assert(new_index >= 0);
-
+
return (old_index != new_index);
}
-
+
public void reorder_children(bool recursive, ChildrenReorderedCallback cb) {
if (children == null)
return;
-
+
Gee.SortedSet<Node> reordered = new Gee.TreeSet<Node>(comparator_wrapper);
reordered.add_all(children);
children = reordered;
-
+
if (recursive) {
foreach (Node child in children)
child.reorder_children(true, cb);
}
-
+
cb(this);
}
-
+
public void change_comparator(CompareFunc<Sidebar.Entry> comparator, bool recursive,
ChildrenReorderedCallback cb) {
this.comparator = comparator;
-
+
// reorder children, but need to do manual recursion to set comparator
reorder_children(false, cb);
-
+
if (recursive) {
foreach (Node child in children)
child.change_comparator(comparator, true, cb);
}
}
}
-
+
private Node root;
private Options options;
private bool shown = true;
private CompareFunc<Sidebar.Entry> default_comparator;
private Gee.HashMap<Sidebar.Entry, Node> map = new Gee.HashMap<Sidebar.Entry, Node>();
-
+
public signal void entry_added(Sidebar.Entry entry);
-
+
public signal void entry_removed(Sidebar.Entry entry);
-
+
public signal void entry_moved(Sidebar.Entry entry);
-
+
public signal void entry_reparented(Sidebar.Entry entry, Sidebar.Entry old_parent);
-
+
public signal void children_reordered(Sidebar.Entry entry);
-
+
public signal void show_branch(bool show);
-
+
public Branch(Sidebar.Entry root, Options options, CompareFunc<Sidebar.Entry> default_comparator,
CompareFunc<Sidebar.Entry>? root_comparator = null) {
this.default_comparator = default_comparator;
this.root = new Node(root, null,
(root_comparator != null) ? root_comparator : default_comparator);
this.options = options;
-
+
map.set(root, this.root);
-
+
if (options.is_hide_if_empty())
set_show_branch(false);
}
-
+
public Sidebar.Entry get_root() {
return root.entry;
}
-
+
public void set_show_branch(bool shown) {
if (this.shown == shown)
return;
-
+
this.shown = shown;
show_branch(shown);
}
-
+
public bool get_show_branch() {
return shown;
}
-
+
public bool is_auto_open_on_new_child() {
return options.is_auto_open_on_new_child();
}
-
+
public bool is_startup_expand_to_first_child() {
return options.is_startup_expand_to_first_child();
}
-
+
public bool is_startup_open_grouping() {
return options.is_startup_open_grouping();
}
-
+
public void graft(Sidebar.Entry parent, Sidebar.Entry entry,
CompareFunc<Sidebar.Entry>? comparator = null) {
assert(map.has_key(parent));
assert(!map.has_key(entry));
-
+
if (options.is_hide_if_empty())
set_show_branch(true);
-
+
Node parent_node = map.get(parent);
Node entry_node = new Node(entry, parent_node,
(comparator != null) ? comparator : default_comparator);
-
+
parent_node.add_child(entry_node);
map.set(entry, entry_node);
-
+
entry_added(entry);
}
-
+
// Cannot prune the root. The Branch should simply be removed from the Tree.
public void prune(Sidebar.Entry entry) {
assert(entry != root.entry);
assert(map.has_key(entry));
-
+
Node entry_node = map.get(entry);
-
+
entry_node.prune_children(prune_callback);
-
+
assert(entry_node.parent != null);
entry_node.parent.remove_child(entry_node);
-
+
bool removed = map.unset(entry);
assert(removed);
-
+
entry_removed(entry);
-
+
if (options.is_hide_if_empty() && !root.has_children())
set_show_branch(false);
}
-
+
// Cannot reparent the root.
public void reparent(Sidebar.Entry new_parent, Sidebar.Entry entry) {
assert(entry != root.entry);
assert(map.has_key(entry));
assert(map.has_key(new_parent));
-
+
Node entry_node = map.get(entry);
Node new_parent_node = map.get(new_parent);
-
+
assert(entry_node.parent != null);
Sidebar.Entry old_parent = entry_node.parent.entry;
-
+
entry_node.parent.remove_child(entry_node);
new_parent_node.add_child(entry_node);
-
+
entry_reparented(entry, old_parent);
}
-
+
public bool has_entry(Sidebar.Entry entry) {
return (root.entry == entry || map.has_key(entry));
}
-
- // Call when a value related to the comparison of this entry has changed. The root cannot be
+
+ // Call when a value related to the comparison of this entry has changed. The root cannot be
// reordered.
public void reorder(Sidebar.Entry entry) {
assert(entry != root.entry);
-
+
Node? entry_node = map.get(entry);
assert(entry_node != null);
-
+
assert(entry_node.parent != null);
if (entry_node.parent.reorder_child(entry_node))
entry_moved(entry);
}
-
+
// Call when the entire tree needs to be reordered.
public void reorder_all() {
root.reorder_children(true, children_reordered_callback);
}
-
+
// Call when the children of the entry need to be reordered.
public void reorder_children(Sidebar.Entry entry, bool recursive) {
Node? entry_node = map.get(entry);
assert(entry_node != null);
-
+
entry_node.reorder_children(recursive, children_reordered_callback);
}
-
+
public void change_all_comparators(CompareFunc<Sidebar.Entry>? comparator) {
root.change_comparator(comparator, true, children_reordered_callback);
}
-
+
public void change_comparator(Sidebar.Entry entry, bool recursive,
CompareFunc<Sidebar.Entry>? comparator) {
Node? entry_node = map.get(entry);
assert(entry_node != null);
-
+
entry_node.change_comparator(comparator, recursive, children_reordered_callback);
}
-
+
public int get_child_count(Sidebar.Entry parent) {
Node? parent_node = map.get(parent);
assert(parent_node != null);
-
+
return (parent_node.children != null) ? parent_node.children.size : 0;
}
-
+
// Gets a snapshot of the children of the entry; this list will not be changed as the
// branch is updated.
public Gee.List<Sidebar.Entry>? get_children(Sidebar.Entry parent) {
assert(map.has_key(parent));
-
+
Node parent_node = map.get(parent);
if (parent_node.children == null)
return null;
-
+
Gee.List<Sidebar.Entry> child_entries = new Gee.ArrayList<Sidebar.Entry>();
foreach (Node child in parent_node.children)
child_entries.add(child.entry);
-
+
return child_entries;
}
-
+
public Sidebar.Entry? find_first_child(Sidebar.Entry parent, Locator<Sidebar.Entry> locator) {
Node? parent_node = map.get(parent);
assert(parent_node != null);
-
+
if (parent_node.children == null)
return null;
-
+
foreach (Node child in parent_node.children) {
if (locator(child.entry))
return child.entry;
}
-
+
return null;
}
-
+
// Returns null if entry is root;
public Sidebar.Entry? get_parent(Sidebar.Entry entry) {
if (entry == root.entry)
return null;
-
+
Node? entry_node = map.get(entry);
assert(entry_node != null);
assert(entry_node.parent != null);
-
+
return entry_node.parent.entry;
}
-
+
// Returns null if entry is root;
public Sidebar.Entry? get_previous_sibling(Sidebar.Entry entry) {
if (entry == root.entry)
return null;
-
+
Node? entry_node = map.get(entry);
assert(entry_node != null);
assert(entry_node.parent != null);
assert(entry_node.parent.children != null);
-
+
Node? sibling = entry_node.parent.children.lower(entry_node);
-
+
return (sibling != null) ? sibling.entry : null;
}
-
+
// Returns null if entry is root;
public Sidebar.Entry? get_next_sibling(Sidebar.Entry entry) {
if (entry == root.entry)
return null;
-
+
Node? entry_node = map.get(entry);
assert(entry_node != null);
assert(entry_node.parent != null);
assert(entry_node.parent.children != null);
-
+
Node? sibling = entry_node.parent.children.higher(entry_node);
-
+
return (sibling != null) ? sibling.entry : null;
}
-
+
private void prune_callback(Node node) {
entry_removed(node.entry);
}
-
+
private void children_reordered_callback(Node node) {
children_reordered(node.entry);
}
diff --git a/src/client/sidebar/sidebar-common.vala b/src/client/sidebar/sidebar-common.vala
index 921a6392..7e3c6e62 100644
--- a/src/client/sidebar/sidebar-common.vala
+++ b/src/client/sidebar/sidebar-common.vala
@@ -7,46 +7,46 @@
// A simple grouping Entry that is only expandable
public class Sidebar.Grouping : Geary.BaseObject, Sidebar.Entry, Sidebar.ExpandableEntry,
Sidebar.RenameableEntry {
-
+
private string name;
private string? tooltip;
private string? icon;
-
+
public Grouping(string name, string? icon, string? tooltip = null) {
this.name = name;
this.icon = icon;
this.tooltip = tooltip;
}
-
+
public void rename(string name) {
this.name = name;
sidebar_name_changed(name);
}
-
+
public bool is_user_renameable() {
return false;
}
-
+
public string get_sidebar_name() {
return name;
}
-
+
public string? get_sidebar_tooltip() {
return tooltip;
}
-
+
public string? get_sidebar_icon() {
return icon;
}
-
+
public int get_count() {
return -1;
}
-
+
public string to_string() {
return name;
}
-
+
public bool expand_on_select() {
return true;
}
@@ -57,7 +57,7 @@ public class Sidebar.RootOnlyBranch : Sidebar.Branch {
public RootOnlyBranch(Sidebar.Entry root) {
base (root, Sidebar.Branch.Options.NONE, null_comparator);
}
-
+
private static int null_comparator(Sidebar.Entry a, Sidebar.Entry b) {
return (a != b) ? -1 : 0;
}
@@ -72,12 +72,12 @@ public class Sidebar.RootOnlyBranch : Sidebar.Branch {
*/
public class Sidebar.Header : Sidebar.Grouping, Sidebar.EmphasizableEntry {
private bool emphasized;
-
+
public Header(string name, bool emphasized = true) {
base(name, null);
this.emphasized = emphasized;
}
-
+
public bool is_emphasized() {
return emphasized;
}
diff --git a/src/client/sidebar/sidebar-count-cell-renderer.vala
b/src/client/sidebar/sidebar-count-cell-renderer.vala
index 505709c4..847e49fc 100644
--- a/src/client/sidebar/sidebar-count-cell-renderer.vala
+++ b/src/client/sidebar/sidebar-count-cell-renderer.vala
@@ -9,36 +9,36 @@
*/
public class SidebarCountCellRenderer : Gtk.CellRenderer {
private const int HORIZONTAL_MARGIN = 4;
-
+
public int counter { get; set; }
-
+
private CountBadge unread_count = new CountBadge(1);
-
+
public SidebarCountCellRenderer() {
}
-
+
public override Gtk.SizeRequestMode get_request_mode() {
return Gtk.SizeRequestMode.WIDTH_FOR_HEIGHT;
}
-
+
public override void get_preferred_width(Gtk.Widget widget, out int minimum_size, out int natural_size) {
unread_count.count = counter;
minimum_size = unread_count.get_width(widget) + FormattedConversationData.LINE_SPACING;
natural_size = minimum_size;
}
-
- public override void render(Cairo.Context ctx, Gtk.Widget widget, Gdk.Rectangle background_area,
+
+ public override void render(Cairo.Context ctx, Gtk.Widget widget, Gdk.Rectangle background_area,
Gdk.Rectangle cell_area, Gtk.CellRendererState flags) {
unread_count.count = counter;
-
+
// Compute x and y locations to right-align and vertically center the count.
int x = cell_area.x + (cell_area.width - unread_count.get_width(widget)) - HORIZONTAL_MARGIN;
int y = cell_area.y + ((cell_area.height - unread_count.get_height(widget)) / 2);
unread_count.render(widget, ctx, x, y, false);
}
-
+
// This is implemented because it's required; ignore it and look at get_preferred_width() instead.
- public override void get_size(Gtk.Widget widget, Gdk.Rectangle? cell_area, out int x_offset,
+ public override void get_size(Gtk.Widget widget, Gdk.Rectangle? cell_area, out int x_offset,
out int y_offset, out int width, out int height) {
// Set values to avoid compiler warning.
x_offset = 0;
diff --git a/src/client/sidebar/sidebar-entry.vala b/src/client/sidebar/sidebar-entry.vala
index d2d0db4e..fa1baac5 100644
--- a/src/client/sidebar/sidebar-entry.vala
+++ b/src/client/sidebar/sidebar-entry.vala
@@ -6,24 +6,24 @@
public interface Sidebar.Entry : Object {
public signal void sidebar_name_changed(string name);
-
+
public signal void sidebar_tooltip_changed(string? tooltip);
-
+
public signal void sidebar_count_changed(int count);
-
+
public abstract string get_sidebar_name();
-
+
public abstract string? get_sidebar_tooltip();
-
+
public abstract string? get_sidebar_icon();
-
+
public abstract int get_count();
-
+
public abstract string to_string();
-
+
internal virtual void grafted(Sidebar.Tree tree) {
}
-
+
internal virtual void pruned(Sidebar.Tree tree) {
}
}
@@ -37,16 +37,16 @@ public interface Sidebar.SelectableEntry : Sidebar.Entry {
public interface Sidebar.RenameableEntry : Sidebar.Entry {
public signal void sidebar_name_changed(string name);
-
+
public abstract void rename(string new_name);
-
+
// Return true to allow the user to rename the sidebar entry in the UI.
public abstract bool is_user_renameable();
}
public interface Sidebar.EmphasizableEntry : Sidebar.Entry {
public signal void is_emphasized_changed(bool emphasized);
-
+
public abstract bool is_emphasized();
}
diff --git a/src/client/sidebar/sidebar-tree.vala b/src/client/sidebar/sidebar-tree.vala
index 180c98fa..6a61b3c1 100644
--- a/src/client/sidebar/sidebar-tree.vala
+++ b/src/client/sidebar/sidebar-tree.vala
@@ -6,44 +6,44 @@
public class Sidebar.Tree : Gtk.TreeView {
public const int ICON_SIZE = 16;
-
+
// Only one ExternalDropHandler can be registered with the Tree; it's responsible for completing
// the "drag-data-received" signal properly.
public delegate void ExternalDropHandler(Gdk.DragContext context, Sidebar.Entry? entry,
Gtk.SelectionData data, uint info, uint time);
-
+
private class EntryWrapper : Object {
public Sidebar.Entry entry;
public Gtk.TreeRowReference row;
-
+
public EntryWrapper(Gtk.TreeModel model, Sidebar.Entry entry, Gtk.TreePath path) {
this.entry = entry;
this.row = new Gtk.TreeRowReference(model, path);
}
-
+
public Gtk.TreePath get_path() {
return row.get_path();
}
-
+
public Gtk.TreeIter get_iter() {
Gtk.TreeIter iter;
bool valid = row.get_model().get_iter(out iter, get_path());
assert(valid);
-
+
return iter;
}
}
-
+
private class RootWrapper : EntryWrapper {
public int root_position;
-
+
public RootWrapper(Gtk.TreeModel model, Sidebar.Entry entry, Gtk.TreePath path, int root_position) {
base (model, entry, path);
-
+
this.root_position = root_position;
}
}
-
+
private enum Columns {
NAME,
TOOLTIP,
@@ -52,7 +52,7 @@ public class Sidebar.Tree : Gtk.TreeView {
COUNTER,
N_COLUMNS
}
-
+
private Gtk.TreeStore store = new Gtk.TreeStore(Columns.N_COLUMNS,
typeof (string), // NAME
typeof (string?), // TOOLTIP
@@ -60,7 +60,7 @@ public class Sidebar.Tree : Gtk.TreeView {
typeof (string?), // ICON
typeof (int) // COUNTER
);
-
+
private Gtk.IconTheme? icon_theme;
private Gtk.CellRendererText text_renderer;
private unowned ExternalDropHandler drop_handler;
@@ -77,23 +77,23 @@ public class Sidebar.Tree : Gtk.TreeView {
private bool is_internal_drag_in_progress = false;
private Sidebar.Entry? internal_drag_source_entry = null;
private Gtk.TreeRowReference? old_path_ref = null;
-
+
public signal void entry_selected(Sidebar.SelectableEntry selectable);
-
+
public signal void selected_entry_removed(Sidebar.SelectableEntry removed);
-
+
public signal void branch_added(Sidebar.Branch branch);
-
+
public signal void branch_removed(Sidebar.Branch branch);
-
+
public signal void branch_shown(Sidebar.Branch branch, bool shown);
-
+
public Tree(Gtk.TargetEntry[] target_entries, Gdk.DragAction actions,
ExternalDropHandler drop_handler, Gtk.IconTheme? theme = null) {
set_model(store);
icon_theme = theme;
get_style_context().add_class("sidebar");
-
+
Gtk.TreeViewColumn text_column = new Gtk.TreeViewColumn();
text_column.set_expand(true);
Gtk.CellRendererPixbuf icon_renderer = new Gtk.CellRendererPixbuf();
@@ -107,14 +107,14 @@ public class Sidebar.Tree : Gtk.TreeView {
text_column.pack_start(text_renderer, true);
text_column.add_attribute(text_renderer, "markup", Columns.NAME);
append_column(text_column);
-
+
// Count column.
Gtk.TreeViewColumn count_column = new Gtk.TreeViewColumn();
SidebarCountCellRenderer unread_renderer = new SidebarCountCellRenderer();
count_column.pack_start(unread_renderer, false);
count_column.add_attribute(unread_renderer, "counter", Columns.COUNTER);
append_column(count_column);
-
+
set_headers_visible(false);
set_enable_search(false);
set_search_column(-1);
@@ -123,19 +123,19 @@ public class Sidebar.Tree : Gtk.TreeView {
set_enable_tree_lines(false);
set_grid_lines(Gtk.TreeViewGridLines.NONE);
set_tooltip_column(Columns.TOOLTIP);
-
+
Gtk.TreeSelection selection = get_selection();
selection.set_mode(Gtk.SelectionMode.BROWSE);
selection.set_select_function(on_selection);
-
+
test_expand_row.connect(on_toggle_row);
test_collapse_row.connect(on_toggle_row);
-
- // It Would Be Nice if the target entries and actions were gleaned by querying each
+
+ // It Would Be Nice if the target entries and actions were gleaned by querying each
// Sidebar.Entry as it was added, but that's a tad too complicated for our needs
// currently
enable_model_drag_dest(target_entries, actions);
-
+
// Drag source removed as per http://redmine.yorba.org/issues/4701
//
// Reason: this isn't working correctly (Sidebar.InternalDragSourceEntry should be
@@ -143,21 +143,21 @@ public class Sidebar.Tree : Gtk.TreeView {
// the entire mechanism shifted somehow under GTK 3; see Gtk.TreeDragSource and
// Gtk.TreeDragDest for more information on how Sidebar should implement this
// properly
-
+
this.drop_handler = drop_handler;
-
+
popup_menu.connect(on_context_menu_keypress);
-
+
drag_begin.connect(on_drag_begin);
drag_end.connect(on_drag_end);
drag_motion.connect(on_drag_motion);
}
-
+
~Tree() {
text_renderer.editing_canceled.disconnect(on_editing_canceled);
text_renderer.editing_started.disconnect(on_editing_started);
}
-
+
public void icon_renderer_function(Gtk.CellLayout layout, Gtk.CellRenderer renderer, Gtk.TreeModel
model, Gtk.TreeIter iter) {
EntryWrapper? wrapper = get_wrapper_at_iter(iter);
if (wrapper == null) {
@@ -165,7 +165,7 @@ public class Sidebar.Tree : Gtk.TreeView {
}
renderer.visible = !(wrapper.entry is Sidebar.Header);
}
-
+
public void counter_renderer_function(Gtk.CellLayout layout, Gtk.CellRenderer renderer, Gtk.TreeModel
model, Gtk.TreeIter iter) {
EntryWrapper? wrapper = get_wrapper_at_iter(iter);
if (wrapper == null) {
@@ -173,101 +173,101 @@ public class Sidebar.Tree : Gtk.TreeView {
}
renderer.visible = !(wrapper.entry is Sidebar.Header);
}
-
+
private void on_drag_begin(Gdk.DragContext ctx) {
is_internal_drag_in_progress = true;
}
-
+
private void on_drag_end(Gdk.DragContext ctx) {
is_internal_drag_in_progress = false;
internal_drag_source_entry = null;
}
-
+
private bool on_drag_motion (Gdk.DragContext context, int x, int y, uint time_) {
if (is_internal_drag_in_progress && internal_drag_source_entry == null) {
Gtk.TreePath? path;
Gtk.TreeViewDropPosition position;
get_dest_row_at_pos(x, y, out path, out position);
-
+
if (path != null) {
EntryWrapper wrapper = get_wrapper_at_path(path);
if (wrapper != null)
internal_drag_source_entry = wrapper.entry;
}
}
-
+
return false;
}
-
+
private bool has_wrapper(Sidebar.Entry entry) {
return entry_map.has_key(entry);
}
-
+
private EntryWrapper? get_wrapper(Sidebar.Entry entry) {
EntryWrapper? wrapper = entry_map.get(entry);
if (wrapper == null)
debug("Entry %s not found in sidebar", entry.to_string());
-
+
return wrapper;
}
-
+
private EntryWrapper? get_wrapper_at_iter(Gtk.TreeIter iter) {
Value val;
store.get_value(iter, Columns.WRAPPER, out val);
-
+
EntryWrapper? wrapper = (EntryWrapper?) val;
if (wrapper == null)
message("No entry found in sidebar at %s", store.get_path(iter).to_string());
-
+
return wrapper;
}
-
+
private EntryWrapper? get_wrapper_at_path(Gtk.TreePath path) {
Gtk.TreeIter iter;
if (!store.get_iter(out iter, path)) {
message("No entry found in sidebar at %s", path.to_string());
-
+
return null;
}
-
+
return get_wrapper_at_iter(iter);
}
-
+
public void set_default_context_menu(Gtk.Menu context_menu) {
default_context_menu = context_menu;
}
-
+
// Note that this method will result in the "entry-selected" signal to fire if mask_signal
// is set to false.
public bool place_cursor(Sidebar.Entry entry, bool mask_signal) {
if (!expand_to_entry(entry))
return false;
-
+
EntryWrapper? wrapper = get_wrapper(entry);
if (wrapper == null)
return false;
-
+
get_selection().select_path(wrapper.get_path());
-
+
mask_entry_selected_signal = mask_signal;
set_cursor(wrapper.get_path(), null, false);
mask_entry_selected_signal = false;
-
+
return scroll_to_entry(entry);
}
-
+
public bool is_selected(Sidebar.Entry entry) {
EntryWrapper? wrapper = get_wrapper(entry);
-
+
// Even though get_selection() does not report its return type as nullable, it can be null
// if the window has been destroyed.
Gtk.TreeSelection selection = get_selection();
if (selection == null)
return false;
-
+
return (wrapper != null) ? selection.path_is_selected(wrapper.get_path()) : false;
}
-
+
public bool is_any_selected() {
return get_selection().count_selected_rows() != 0;
}
@@ -286,18 +286,18 @@ public class Sidebar.Tree : Gtk.TreeView {
private string get_name_for_entry(Sidebar.Entry entry) {
string name = Geary.HTML.escape_markup(entry.get_sidebar_name());
-
+
Sidebar.EmphasizableEntry? emphasizable_entry = entry as Sidebar.EmphasizableEntry;
if (emphasizable_entry != null && emphasizable_entry.is_emphasized())
name = "<b>%s</b>".printf(name);
-
+
return name;
}
-
+
public virtual bool accept_cursor_changed() {
return true;
}
-
+
public override void cursor_changed() {
Gtk.TreePath? path = get_selected_path();
if (path == null) {
@@ -305,16 +305,16 @@ public class Sidebar.Tree : Gtk.TreeView {
base.cursor_changed();
return;
}
-
+
EntryWrapper? wrapper = get_wrapper_at_path(path);
-
+
if (selected_wrapper != wrapper) {
EntryWrapper old_wrapper = selected_wrapper;
selected_wrapper = wrapper;
-
+
if (editing_disabled == 0 && wrapper != null && wrapper.entry is Sidebar.RenameableEntry)
text_renderer.editable = ((Sidebar.RenameableEntry) wrapper.entry).is_user_renameable();
-
+
if (wrapper != null && !mask_entry_selected_signal) {
Sidebar.SelectableEntry? selectable = wrapper.entry as Sidebar.SelectableEntry;
if (selectable != null) {
@@ -326,16 +326,16 @@ public class Sidebar.Tree : Gtk.TreeView {
}
}
}
-
+
if (base.cursor_changed != null)
base.cursor_changed();
}
-
+
public void disable_editing() {
if (editing_disabled++ == 0)
text_renderer.editable = false;
}
-
+
public void enable_editing() {
Gtk.TreePath? path = get_selected_path();
if (path != null && editing_disabled > 0 && --editing_disabled == 0) {
@@ -345,7 +345,7 @@ public class Sidebar.Tree : Gtk.TreeView {
is_user_renameable();
}
}
-
+
public void toggle_branch_expansion(Gtk.TreePath path, bool expand_all) {
expander_called_manually = true;
if (is_row_expanded(path))
@@ -353,290 +353,290 @@ public class Sidebar.Tree : Gtk.TreeView {
else
expand_row(path, expand_all);
}
-
+
public bool expand_to_entry(Sidebar.Entry entry) {
expander_called_manually = true;
EntryWrapper? wrapper = get_wrapper(entry);
if (wrapper == null)
return false;
-
+
expand_to_path(wrapper.get_path());
-
+
return true;
}
-
+
public void expand_to_first_child(Sidebar.Entry entry) {
expander_called_manually = true;
EntryWrapper? wrapper = get_wrapper(entry);
if (wrapper == null)
return;
-
+
Gtk.TreePath path = wrapper.get_path();
-
+
Gtk.TreeIter iter;
while (store.get_iter(out iter, path)) {
if (!store.iter_has_child(iter))
break;
-
+
path.down();
}
-
+
expand_to_path(path);
}
-
+
public bool has_branch(Sidebar.Branch branch) {
return branches.has_key(branch);
}
-
+
public void graft(Sidebar.Branch branch, int position) {
assert(!branches.has_key(branch));
-
+
branches.set(branch, position);
-
+
if (branch.get_show_branch()) {
associate_branch(branch);
-
+
if (branch.is_startup_expand_to_first_child())
expand_to_first_child(branch.get_root());
if (branch.is_startup_open_grouping())
expand_to_entry(branch.get_root());
}
-
+
branch.entry_added.connect(on_branch_entry_added);
branch.entry_removed.connect(on_branch_entry_removed);
branch.entry_moved.connect(on_branch_entry_moved);
branch.entry_reparented.connect(on_branch_entry_reparented);
branch.children_reordered.connect(on_branch_children_reordered);
branch.show_branch.connect(on_show_branch);
-
+
branch_added(branch);
}
-
+
public int get_position_for_branch(Sidebar.Branch branch) {
if (branches.has_key(branch))
return branches.get(branch);
-
+
return int.MIN;
}
-
+
// This is used to associate a known branch with the TreeView.
private void associate_branch(Sidebar.Branch branch) {
assert(branches.has_key(branch));
-
+
int position = branches.get(branch);
-
+
Gtk.TreeIter? insertion_iter = null;
-
+
// search current roots for insertion point
Gtk.TreeIter iter;
bool found = store.get_iter_first(out iter);
while (found) {
RootWrapper? root_wrapper = get_wrapper_at_iter(iter) as RootWrapper;
assert(root_wrapper != null);
-
+
if (position < root_wrapper.root_position) {
store.insert_before(out insertion_iter, null, iter);
-
+
break;
}
-
+
found = store.iter_next(ref iter);
}
-
+
// if not found, append
if (insertion_iter == null)
store.append(out insertion_iter, null);
-
+
associate_wrapper(insertion_iter,
new RootWrapper(store, branch.get_root(), store.get_path(insertion_iter), position));
-
+
// mirror the branch's initial contents from below the root down, let the signals handle
// future work
associate_children(branch, branch.get_root(), insertion_iter);
}
-
+
private void associate_children(Sidebar.Branch branch, Sidebar.Entry parent,
Gtk.TreeIter parent_iter) {
Gee.List<Sidebar.Entry>? children = branch.get_children(parent);
if (children == null)
return;
-
+
foreach (Sidebar.Entry child in children) {
Gtk.TreeIter append_iter;
store.append(out append_iter, parent_iter);
-
+
associate_entry(append_iter, child);
associate_children(branch, child, append_iter);
}
}
-
+
private void associate_entry(Gtk.TreeIter assoc_iter, Sidebar.Entry entry) {
associate_wrapper(assoc_iter, new EntryWrapper(store, entry, store.get_path(assoc_iter)));
}
-
+
private void associate_wrapper(Gtk.TreeIter assoc_iter, EntryWrapper wrapper) {
Sidebar.Entry entry = wrapper.entry;
-
+
assert(!entry_map.has_key(entry));
entry_map.set(entry, wrapper);
-
+
store.set(assoc_iter, Columns.NAME, get_name_for_entry(entry));
store.set(assoc_iter, Columns.TOOLTIP, entry.get_sidebar_tooltip() != null ?
Geary.HTML.escape_markup(entry.get_sidebar_tooltip()) : null);
store.set(assoc_iter, Columns.WRAPPER, wrapper);
store.set(assoc_iter, Columns.COUNTER, entry.get_count());
load_entry_icons(assoc_iter);
-
+
entry.sidebar_tooltip_changed.connect(on_sidebar_tooltip_changed);
entry.sidebar_name_changed.connect(on_sidebar_name_changed);
entry.sidebar_count_changed.connect(on_sidebar_count_changed);
-
+
Sidebar.EmphasizableEntry? emphasizable = entry as Sidebar.EmphasizableEntry;
if (emphasizable != null)
emphasizable.is_emphasized_changed.connect(on_is_emphasized_changed);
-
+
entry.grafted(this);
}
-
+
private EntryWrapper reparent_wrapper(Gtk.TreeIter new_iter, EntryWrapper current_wrapper) {
Sidebar.Entry entry = current_wrapper.entry;
-
+
bool removed = entry_map.unset(entry);
assert(removed);
-
+
EntryWrapper new_wrapper = new EntryWrapper(store, entry, store.get_path(new_iter));
entry_map.set(entry, new_wrapper);
-
+
store.set(new_iter, Columns.NAME, get_name_for_entry(entry));
store.set(new_iter, Columns.TOOLTIP, Geary.HTML.escape_markup(entry.get_sidebar_tooltip()));
store.set(new_iter, Columns.COUNTER, entry.get_count());
store.set(new_iter, Columns.WRAPPER, new_wrapper);
load_entry_icons(new_iter);
-
+
return new_wrapper;
}
-
+
protected void prune_all() {
while (branches.keys.size > 0) {
Gee.Iterator<Sidebar.Branch> iterator = branches.keys.iterator();
if (!iterator.next())
break;
-
+
prune(iterator.get());
}
}
-
+
public void prune(Sidebar.Branch branch) {
assert(branches.has_key(branch));
-
+
if (has_wrapper(branch.get_root()))
disassociate_branch(branch);
-
+
branch.entry_added.disconnect(on_branch_entry_added);
branch.entry_removed.disconnect(on_branch_entry_removed);
branch.entry_moved.disconnect(on_branch_entry_moved);
branch.entry_reparented.disconnect(on_branch_entry_reparented);
branch.children_reordered.disconnect(on_branch_children_reordered);
branch.show_branch.disconnect(on_show_branch);
-
+
bool removed = branches.unset(branch);
assert(removed);
-
+
branch_removed(branch);
}
-
+
private void disassociate_branch(Sidebar.Branch branch) {
RootWrapper? root_wrapper = get_wrapper(branch.get_root()) as RootWrapper;
assert(root_wrapper != null);
-
+
disassociate_wrapper_and_signal(root_wrapper, false);
}
-
+
// A wrapper for disassociate_wrapper() (?!?) that fires the "selected-entry-removed" signal if
// condition exists
private void disassociate_wrapper_and_signal(EntryWrapper wrapper, bool only_children) {
bool selected = is_selected(wrapper.entry);
-
+
disassociate_wrapper(wrapper, only_children);
-
+
if (selected) {
Sidebar.SelectableEntry? selectable = wrapper.entry as Sidebar.SelectableEntry;
assert(selectable != null);
-
+
selected_entry_removed(selectable);
}
}
-
+
private void disassociate_wrapper(EntryWrapper wrapper, bool only_children) {
Gee.ArrayList<EntryWrapper> children = new Gee.ArrayList<EntryWrapper>();
-
+
Gtk.TreeIter child_iter;
bool found = store.iter_children(out child_iter, wrapper.get_iter());
while (found) {
EntryWrapper? child_wrapper = get_wrapper_at_iter(child_iter);
assert(child_wrapper != null);
-
+
children.add(child_wrapper);
-
+
found = store.iter_next(ref child_iter);
}
-
+
foreach (EntryWrapper child_wrapper in children)
disassociate_wrapper(child_wrapper, false);
-
+
if (only_children)
return;
-
+
Gtk.TreeIter iter = wrapper.get_iter();
store.remove(ref iter);
-
+
if (selected_wrapper == wrapper)
selected_wrapper = null;
-
+
Sidebar.Entry entry = wrapper.entry;
-
+
entry.pruned(this);
-
+
entry.sidebar_tooltip_changed.disconnect(on_sidebar_tooltip_changed);
entry.sidebar_name_changed.disconnect(on_sidebar_name_changed);
entry.sidebar_count_changed.disconnect(on_sidebar_count_changed);
-
+
Sidebar.EmphasizableEntry? emphasizable = entry as Sidebar.EmphasizableEntry;
if (emphasizable != null)
emphasizable.is_emphasized_changed.disconnect(on_is_emphasized_changed);
-
+
bool removed = entry_map.unset(entry);
assert(removed);
}
-
+
private void on_branch_entry_added(Sidebar.Branch branch, Sidebar.Entry entry) {
Sidebar.Entry? parent = branch.get_parent(entry);
assert(parent != null);
-
+
EntryWrapper? parent_wrapper = get_wrapper(parent);
assert(parent_wrapper != null);
-
+
Gtk.TreeIter insertion_iter;
Sidebar.Entry? next = branch.get_next_sibling(entry);
if (next != null) {
EntryWrapper next_wrapper = get_wrapper(next);
-
+
// insert before the next sibling in this branch level
store.insert_before(out insertion_iter, parent_wrapper.get_iter(), next_wrapper.get_iter());
} else {
// append to the bottom of this branch level
store.append(out insertion_iter, parent_wrapper.get_iter());
}
-
+
associate_entry(insertion_iter, entry);
associate_children(branch, entry, insertion_iter);
-
+
if (branch.is_auto_open_on_new_child())
expand_to_entry(entry);
}
-
+
private void on_branch_entry_removed(Sidebar.Branch branch, Sidebar.Entry entry) {
EntryWrapper? wrapper = get_wrapper(entry);
if (wrapper != null) {
@@ -644,120 +644,120 @@ public class Sidebar.Tree : Gtk.TreeView {
disassociate_wrapper_and_signal(wrapper, false);
}
}
-
+
private void on_branch_entry_moved(Sidebar.Branch branch, Sidebar.Entry entry) {
EntryWrapper? wrapper = get_wrapper(entry);
assert(wrapper != null);
assert(!(wrapper is RootWrapper));
-
+
// null means entry is now at the top of the sibling list
Gtk.TreeIter? prev_iter = null;
Sidebar.Entry? prev = branch.get_previous_sibling(entry);
if (prev != null) {
EntryWrapper? prev_wrapper = get_wrapper(prev);
assert(prev_wrapper != null);
-
+
prev_iter = prev_wrapper.get_iter();
}
-
+
Gtk.TreeIter entry_iter = wrapper.get_iter();
store.move_after(ref entry_iter, prev_iter);
}
-
+
private void on_branch_entry_reparented(Sidebar.Branch branch, Sidebar.Entry entry,
Sidebar.Entry old_parent) {
EntryWrapper? wrapper = get_wrapper(entry);
assert(wrapper != null);
assert(!(wrapper is RootWrapper));
-
+
bool selected = (get_current_path().compare(wrapper.get_path()) == 0);
-
+
// remove from current position in tree
Gtk.TreeIter iter = wrapper.get_iter();
store.remove(ref iter);
-
+
Sidebar.Entry? parent = branch.get_parent(entry);
assert(parent != null);
-
+
EntryWrapper? parent_wrapper = get_wrapper(parent);
assert(parent_wrapper != null);
-
+
// null means entry is now at the top of the sibling list
Gtk.TreeIter? prev_iter = null;
Sidebar.Entry? prev = branch.get_previous_sibling(entry);
if (prev != null) {
EntryWrapper? prev_wrapper = get_wrapper(prev);
assert(prev_wrapper != null);
-
+
prev_iter = prev_wrapper.get_iter();
}
-
+
Gtk.TreeIter new_iter;
store.insert_after(out new_iter, parent_wrapper.get_iter(), prev_iter);
-
+
EntryWrapper new_wrapper = reparent_wrapper(new_iter, wrapper);
-
+
if (selected) {
expand_to_entry(new_wrapper.entry);
place_cursor(new_wrapper.entry, false);
}
}
-
+
private void on_branch_children_reordered(Sidebar.Branch branch, Sidebar.Entry entry) {
Gee.List<Sidebar.Entry>? children = branch.get_children(entry);
if (children == null)
return;
-
+
// This works by moving the entries to the bottom of the tree's list in the order they
// are presented in the Sidebar.Branch list.
foreach (Sidebar.Entry child in children) {
EntryWrapper? child_wrapper = get_wrapper(child);
assert(child_wrapper != null);
-
+
Gtk.TreeIter child_iter = child_wrapper.get_iter();
store.move_before(ref child_iter, null);
}
}
-
+
private void on_show_branch(Sidebar.Branch branch, bool shown) {
if (shown)
associate_branch(branch);
else
disassociate_branch(branch);
-
+
branch_shown(branch, shown);
}
-
+
private void on_sidebar_tooltip_changed(Sidebar.Entry entry, string? tooltip) {
EntryWrapper? wrapper = get_wrapper(entry);
assert(wrapper != null);
-
- store.set(wrapper.get_iter(), Columns.TOOLTIP, tooltip != null ?
+
+ store.set(wrapper.get_iter(), Columns.TOOLTIP, tooltip != null ?
Geary.HTML.escape_markup(tooltip) : null);
}
-
+
private void rename_entry(Sidebar.Entry entry) {
EntryWrapper? wrapper = get_wrapper(entry);
assert(wrapper != null);
-
+
store.set(wrapper.get_iter(), Columns.NAME, get_name_for_entry(entry));
}
-
+
private void on_sidebar_name_changed(Sidebar.Entry entry, string name) {
rename_entry(entry);
}
-
+
private void on_is_emphasized_changed(Sidebar.EmphasizableEntry entry, bool is_emphasized) {
rename_entry(entry);
}
-
+
private void on_sidebar_count_changed(Sidebar.Entry entry, int coun) {
EntryWrapper? wrapper = get_wrapper(entry);
assert(wrapper != null);
-
+
store.set(wrapper.get_iter(), Columns.COUNTER, entry.get_count());
}
-
+
private void load_entry_icons(Gtk.TreeIter iter) {
EntryWrapper? wrapper = get_wrapper_at_iter(iter);
if (wrapper == null)
@@ -765,10 +765,10 @@ public class Sidebar.Tree : Gtk.TreeView {
string? icon = wrapper.entry.get_sidebar_icon();
store.set(iter, Columns.ICON, icon);
}
-
+
private void load_branch_icons(Gtk.TreeIter iter) {
load_entry_icons(iter);
-
+
Gtk.TreeIter child_iter;
if (store.iter_children(out child_iter, iter)) {
do {
@@ -776,15 +776,15 @@ public class Sidebar.Tree : Gtk.TreeView {
} while (store.iter_next(ref child_iter));
}
}
-
+
private bool on_selection(Gtk.TreeSelection selection, Gtk.TreeModel model, Gtk.TreePath path,
bool path_currently_selected) {
// only allow selection if a page is selectable
EntryWrapper? wrapper = get_wrapper_at_path(path);
-
+
return (wrapper != null) ? (wrapper.entry is Sidebar.SelectableEntry) : false;
}
-
+
private Gtk.TreePath? get_path_from_event(Gdk.EventButton event) {
int x, y;
Gdk.ModifierType mask;
@@ -797,38 +797,38 @@ public class Sidebar.Tree : Gtk.TreeView {
Gtk.TreePath path;
return get_path_at_pos(x, y, out path, null, out cell_x, out cell_y) ? path : null;
}
-
+
private Gtk.TreePath? get_current_path() {
Gtk.TreeModel model;
GLib.List<Gtk.TreePath> rows = get_selection().get_selected_rows(out model);
assert(rows.length() == 0 || rows.length() == 1);
-
+
return rows.length() != 0 ? rows.nth_data(0) : null;
}
-
+
private bool on_context_menu_keypress() {
GLib.List<Gtk.TreePath> rows = get_selection().get_selected_rows(null);
if (rows == null)
return false;
-
+
Gtk.TreePath? path = rows.data;
if (path == null)
return false;
-
+
scroll_to_cell(path, null, false, 0, 0);
-
+
return popup_context_menu(path);
}
-
+
private bool popup_context_menu(Gtk.TreePath path, Gdk.EventButton? event = null) {
EntryWrapper? wrapper = get_wrapper_at_path(path);
if (wrapper == null)
return false;
-
+
Sidebar.Contextable? contextable = wrapper.entry as Sidebar.Contextable;
if (contextable == null)
return false;
-
+
Gtk.Menu? context_menu = contextable.get_sidebar_context_menu(event);
if (context_menu == null)
return false;
@@ -849,13 +849,13 @@ public class Sidebar.Tree : Gtk.TreeView {
if (wrapper == null) {
return false; // don't affect things
}
-
+
// Most of the time, only allow manual toggles
bool should_allow_toggle = expander_called_manually;
-
+
// Cancel out the manual flag
expander_called_manually = false;
-
+
// If we are an expanded parent entry with content
if (is_row_expanded(path) && store.iter_has_child(iter) && wrapper.entry is Sidebar.SelectableEntry)
{
// We are taking a special action
@@ -871,17 +871,17 @@ public class Sidebar.Tree : Gtk.TreeView {
// Reset the special behavior count
expander_special_count = 0;
}
-
+
if (should_allow_toggle) {
return false;
}
// Prevent branch expansion toggle
return true;
}
-
+
public override bool button_press_event(Gdk.EventButton event) {
Gtk.TreePath? path = get_path_from_event(event);
-
+
if (event.button == 3 && event.type == Gdk.EventType.BUTTON_PRESS) {
// single right click
if (path != null)
@@ -893,44 +893,44 @@ public class Sidebar.Tree : Gtk.TreeView {
old_path_ref = null;
return base.button_press_event(event);
}
-
+
EntryWrapper? wrapper = get_wrapper_at_path(path);
-
+
if (wrapper == null) {
old_path_ref = null;
return base.button_press_event(event);
}
-
+
// Enable single click to toggle tree entries (bug 4985)
if (wrapper.entry is Sidebar.ExpandableEntry
|| wrapper.entry is Sidebar.InternalDropTargetEntry) {
// all labels are InternalDropTargetEntries
toggle_branch_expansion(path, false);
}
-
+
// Is this a click on an already-highlighted tree item?
if ((old_path_ref != null) && (old_path_ref.get_path() != null)
&& (old_path_ref.get_path().compare(path) == 0)) {
- // yes, don't allow single-click editing, but
+ // yes, don't allow single-click editing, but
// pass the event on for dragging.
text_renderer.editable = false;
return base.button_press_event(event);
}
-
+
// Got click on different tree item, make sure it is editable
// if it needs to be.
if (wrapper.entry is Sidebar.RenameableEntry &&
((Sidebar.RenameableEntry) wrapper.entry).is_user_renameable()) {
text_renderer.editable = true;
}
-
+
// Remember what tree item is highlighted for next time.
old_path_ref = new Gtk.TreeRowReference(store, path);
}
return base.button_press_event(event);
}
-
+
public bool is_keypress_interpreted(Gdk.EventKey event) {
switch (Gdk.keyval_name(event.keyval)) {
case "F2":
@@ -938,12 +938,12 @@ public class Sidebar.Tree : Gtk.TreeView {
case "Return":
case "KP_Enter":
return true;
-
+
default:
return false;
}
}
-
+
public override bool key_press_event(Gdk.EventKey event) {
switch (Gdk.keyval_name(event.keyval)) {
case "Return":
@@ -951,59 +951,59 @@ public class Sidebar.Tree : Gtk.TreeView {
Gtk.TreePath? path = get_current_path();
if (path != null)
toggle_branch_expansion(path, false);
-
+
return true;
-
+
case "F2":
return rename_in_place();
-
+
case "Delete":
Gtk.TreePath? path = get_current_path();
-
+
return (path != null) ? destroy_path(path) : false;
}
-
+
return base.key_press_event(event);
}
-
+
public bool rename_entry_in_place(Sidebar.Entry entry) {
if (!expand_to_entry(entry))
return false;
-
+
if (!place_cursor(entry, false))
return false;
-
+
return rename_in_place();
}
-
+
private bool rename_in_place() {
Gtk.TreePath? cursor_path;
Gtk.TreeViewColumn? cursor_column;
get_cursor(out cursor_path, out cursor_column);
-
+
if (can_rename_path(cursor_path)) {
set_cursor(cursor_path, cursor_column, true);
-
+
return true;
}
-
+
return false;
}
-
+
public bool scroll_to_entry(Sidebar.Entry entry) {
EntryWrapper? wrapper = get_wrapper(entry);
if (wrapper == null)
return false;
-
+
scroll_to_cell(wrapper.get_path(), null, false, 0, 0);
-
+
return true;
}
public override void drag_data_get(Gdk.DragContext context, Gtk.SelectionData selection_data,
uint info, uint time) {
InternalDragSourceEntry? drag_source = null;
-
+
if (internal_drag_source_entry != null) {
Sidebar.SelectableEntry selectable =
internal_drag_source_entry as Sidebar.SelectableEntry;
@@ -1011,7 +1011,7 @@ public class Sidebar.Tree : Gtk.TreeView {
drag_source = internal_drag_source_entry as InternalDragSourceEntry;
}
}
-
+
if (drag_source == null) {
Gtk.TreePath? selected_path = get_selected_path();
if (selected_path == null)
@@ -1020,18 +1020,18 @@ public class Sidebar.Tree : Gtk.TreeView {
EntryWrapper? wrapper = get_wrapper_at_path(selected_path);
if (wrapper == null)
return;
-
+
drag_source = wrapper.entry as InternalDragSourceEntry;
if (drag_source == null)
return;
}
-
+
drag_source.prepare_selection_data(selection_data);
}
-
+
public override void drag_data_received(Gdk.DragContext context, int x, int y,
Gtk.SelectionData selection_data, uint info, uint time) {
-
+
Gtk.TreePath path;
Gtk.TreeViewDropPosition pos;
if (!get_dest_row_at_pos(x, y, out path, out pos)) {
@@ -1040,37 +1040,37 @@ public class Sidebar.Tree : Gtk.TreeView {
drop_handler(context, null, selection_data, info, time);
else
Gtk.drag_finish(context, false, false, time);
-
+
return;
}
-
+
// Note that a drop outside a sidebar entry is legal if an external drop.
EntryWrapper? wrapper = get_wrapper_at_path(path);
-
+
// If an external drop, hand it off to the handler
if (Gtk.drag_get_source_widget(context) == null) {
drop_handler(context, (wrapper != null) ? wrapper.entry : null, selection_data,
info, time);
-
+
return;
}
-
+
// An internal drop only applies to DropTargetEntry's
if (wrapper == null) {
Gtk.drag_finish(context, false, false, time);
-
+
return;
}
-
+
Sidebar.InternalDropTargetEntry? targetable = wrapper.entry as Sidebar.InternalDropTargetEntry;
if (targetable == null) {
Gtk.drag_finish(context, false, false, time);
-
+
return;
}
-
+
bool success = targetable.internal_drop_received(context, selection_data);
-
+
Gtk.drag_finish(context, success, false, time);
}
@@ -1081,36 +1081,36 @@ public class Sidebar.Tree : Gtk.TreeView {
Gtk.TreePath path;
Gtk.TreeViewDropPosition pos;
bool has_dest = get_dest_row_at_pos(x, y, out path, out pos);
-
+
// we don't want to insert between rows, only select the rows themselves
if (!has_dest || pos == Gtk.TreeViewDropPosition.BEFORE)
set_drag_dest_row(path, Gtk.TreeViewDropPosition.INTO_OR_BEFORE);
else if (pos == Gtk.TreeViewDropPosition.AFTER)
set_drag_dest_row(path, Gtk.TreeViewDropPosition.INTO_OR_AFTER);
-
+
Gdk.drag_status(context, context.get_suggested_action(), time);
-
+
return has_dest;
}
-
+
// Returns true if path is renameable, and selects the path as well.
private bool can_rename_path(Gtk.TreePath path) {
if (editing_disabled > 0)
return false;
-
+
EntryWrapper? wrapper = get_wrapper_at_path(path);
if (wrapper == null)
return false;
-
+
Sidebar.RenameableEntry? renameable = wrapper.entry as Sidebar.RenameableEntry;
if (renameable == null)
return false;
-
+
if (wrapper.entry is Sidebar.Grouping)
return false;
-
+
get_selection().select_path(path);
-
+
return true;
}
@@ -1118,13 +1118,13 @@ public class Sidebar.Tree : Gtk.TreeView {
EntryWrapper? wrapper = get_wrapper_at_path(path);
if (wrapper == null)
return false;
-
+
Sidebar.DestroyableEntry? destroyable = wrapper.entry as Sidebar.DestroyableEntry;
if (destroyable == null)
return false;
-
+
destroyable.destroy_source();
-
+
return true;
}
@@ -1136,24 +1136,24 @@ public class Sidebar.Tree : Gtk.TreeView {
text_entry.editable = true;
}
}
-
+
private void on_editing_canceled() {
text_entry.editable = false;
-
+
text_entry.editing_done.disconnect(on_editing_done);
text_entry.focus_out_event.disconnect(on_editing_focus_out);
}
-
+
private void on_editing_done() {
text_entry.editable = false;
-
+
EntryWrapper? wrapper = get_wrapper_at_path(get_current_path());
if (wrapper != null) {
Sidebar.RenameableEntry? renameable = wrapper.entry as Sidebar.RenameableEntry;
if (renameable != null)
renameable.rename(text_entry.get_text());
}
-
+
text_entry.editing_done.disconnect(on_editing_done);
text_entry.focus_out_event.disconnect(on_editing_focus_out);
}
diff --git a/src/client/util/util-date.vala b/src/client/util/util-date.vala
index a1ec7e93..58239734 100644
--- a/src/client/util/util-date.vala
+++ b/src/client/util/util-date.vala
@@ -10,9 +10,9 @@ public enum ClockFormat {
TWELVE_HOURS,
TWENTY_FOUR_HOURS,
LOCALE_DEFAULT,
-
+
TOTAL;
-
+
internal int to_index() {
// clamp to array boundaries
return ((int) this).clamp(0, ClockFormat.TOTAL - 1);
@@ -41,27 +41,27 @@ private string? xlat_diff_year = null;
public void init() {
if (init_count++ != 0)
return;
-
+
// Ripped from Shotwell proposed patch for localizing time (http://redmine.yorba.org/issues/2462)
// courtesy Marcel Stimberg. Another example may be found here:
//
http://bazaar.launchpad.net/~indicator-applet-developers/indicator-datetime/trunk.12.10/view/head:/src/utils.c
-
+
// Because setlocale() is a process-wide setting, need to cache strings at startup, otherwise
// risk problems with threading
-
+
string? messages_locale = Intl.setlocale(LocaleCategory.MESSAGES, null);
string? time_locale = Intl.setlocale(LocaleCategory.TIME, null);
-
+
// LANGUAGE must be unset before changing locales, as it trumps all the LC_* variables
string? language_env = Environment.get_variable("LANGUAGE");
if (language_env != null)
Environment.unset_variable("LANGUAGE");
-
+
// Swap LC_TIME's setting into LC_MESSAGE's. This allows performinglookups of time-based values
// from a different translation file, useful in mixed-locale settings
if (time_locale != null)
Intl.setlocale(LocaleCategory.MESSAGES, time_locale);
-
+
xlat_pretty_dates = new string[ClockFormat.TOTAL];
/// Datetime format for 12-hour time, i.e. 8:31 am
/// See http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
@@ -72,16 +72,16 @@ public void init() {
/// Datetime format for the locale default, i.e. 8:31 am or 16:35,
/// See http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
xlat_pretty_dates[ClockFormat.LOCALE_DEFAULT] = C_("Default clock format", "%l:%M %P");
-
+
/// Date format for dates within the current year, i.e. Nov 8
/// See http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
xlat_same_year = _("%b %-e");
-
+
/// Date format for dates within a different year, i.e. 02/04/10
/// See http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
/* xgettext:no-c-format */
xlat_diff_year = _("%x");
-
+
xlat_pretty_verbose_dates = new string[ClockFormat.TOTAL];
/// Verbose datetime format for 12-hour time, i.e. November 8, 2010 8:42 am
/// See http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
@@ -92,7 +92,7 @@ public void init() {
/// Verbose datetime format for the locale default (full month, day and time)
/// See http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
xlat_pretty_verbose_dates[ClockFormat.LOCALE_DEFAULT] = C_("Default full date", "%B %-e, %Y %-l:%M %P");
-
+
// return LC_MESSAGES back to proper locale and return LANGUAGE environment variable
if (messages_locale != null)
Intl.setlocale(LocaleCategory.MESSAGES, messages_locale);
@@ -104,7 +104,7 @@ public void init() {
private void terminate() {
if (--init_count != 0)
return;
-
+
xlat_pretty_dates = null;
xlat_same_year = null;
xlat_diff_year = null;
@@ -114,10 +114,10 @@ private void terminate() {
private bool same_day(DateTime a, DateTime b) {
int year1, month1, day1;
a.get_ymd(out year1, out month1, out day1);
-
+
int year2, month2, day2;
b.get_ymd(out year2, out month2, out day2);
-
+
return year1 == year2 && month1 == month2 && day1 == day2;
}
@@ -162,38 +162,38 @@ private string pretty_print_coarse(CoarseDate coarse_date, ClockFormat clock_for
switch (coarse_date) {
case CoarseDate.NOW:
return _("Now");
-
+
case CoarseDate.MINUTES:
return ngettext("%dm ago", "%dm ago", (ulong) (diff / TimeSpan.MINUTE)).printf((int) (diff /
TimeSpan.MINUTE));
-
+
case CoarseDate.HOURS:
int rounded = (int) Math.round((double) diff / TimeSpan.HOUR);
return ngettext("%dh ago", "%dh ago", (ulong) rounded).printf(rounded);
-
+
case CoarseDate.TODAY:
fmt = xlat_pretty_dates[clock_format.to_index()];
break;
-
+
case CoarseDate.YESTERDAY:
return _("Yesterday");
-
+
case CoarseDate.THIS_WEEK:
/// Date format that shows the weekday (Monday, Tuesday, ...)
/// See http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
fmt = _("%A");
break;
-
+
case CoarseDate.THIS_YEAR:
fmt = xlat_same_year;
break;
-
+
case CoarseDate.YEARS:
case CoarseDate.FUTURE:
default:
fmt = xlat_diff_year;
break;
}
-
+
return datetime.format(fmt);
}
@@ -201,7 +201,7 @@ public string pretty_print(DateTime datetime, ClockFormat clock_format) {
DateTime to_local = datetime.to_local();
DateTime now = new DateTime.now_local();
TimeSpan diff = now.difference(to_local);
-
+
return pretty_print_coarse(as_coarse_date(to_local, now, diff), clock_format, to_local, diff);
}
diff --git a/src/client/util/util-email.vala b/src/client/util/util-email.vala
index 143465e5..f89fe6eb 100644
--- a/src/client/util/util-email.vala
+++ b/src/client/util/util-email.vala
@@ -7,12 +7,12 @@
public int compare_conversation_ascending(Geary.App.Conversation a, Geary.App.Conversation b) {
Geary.Email? a_latest = a.get_latest_recv_email(Geary.App.Conversation.Location.IN_FOLDER_OUT_OF_FOLDER);
Geary.Email? b_latest = b.get_latest_recv_email(Geary.App.Conversation.Location.IN_FOLDER_OUT_OF_FOLDER);
-
+
if (a_latest == null)
return (b_latest == null) ? 0 : -1;
else if (b_latest == null)
return 1;
-
+
// use date-received so newly-arrived messages float to the top, even if they're send date
// was earlier (think of mailing lists that batch up forwarded mail)
return Geary.Email.compare_recv_date_ascending(a_latest, b_latest);
@@ -26,7 +26,7 @@ namespace EmailUtil {
public string strip_subject_prefixes(Geary.Email email) {
string? cleaned = (email.subject != null) ? email.subject.strip_prefixes() : null;
-
+
return !Geary.String.is_empty(cleaned) ? cleaned : _("(no subject)");
}
diff --git a/src/engine/api/geary-abstract-local-folder.vala b/src/engine/api/geary-abstract-local-folder.vala
index 06a38450..afc3fe7a 100644
--- a/src/engine/api/geary-abstract-local-folder.vala
+++ b/src/engine/api/geary-abstract-local-folder.vala
@@ -10,7 +10,7 @@
public abstract class Geary.AbstractLocalFolder : Geary.Folder {
private ProgressMonitor _opening_monitor = new
Geary.ReentrantProgressMonitor(Geary.ProgressType.ACTIVITY);
public override Geary.ProgressMonitor opening_monitor { get { return _opening_monitor; } }
-
+
private int open_count = 0;
private Nonblocking.Semaphore closed_semaphore = new Nonblocking.Semaphore();
@@ -23,12 +23,12 @@ public abstract class Geary.AbstractLocalFolder : Geary.Folder {
public override Geary.Folder.OpenState get_open_state() {
return open_count > 0 ? Geary.Folder.OpenState.LOCAL : Geary.Folder.OpenState.CLOSED;
}
-
+
protected void check_open() throws EngineError {
if (open_count == 0)
throw new EngineError.OPEN_REQUIRED("%s not open", to_string());
}
-
+
protected bool is_open() {
return open_count > 0;
}
@@ -37,26 +37,26 @@ public abstract class Geary.AbstractLocalFolder : Geary.Folder {
throws Error {
if (open_count++ > 0)
return false;
-
+
closed_semaphore.reset();
-
+
notify_opened(Geary.Folder.OpenState.LOCAL, properties.email_total);
-
+
return true;
}
-
+
public override async bool close_async(Cancellable? cancellable = null) throws Error {
if (open_count == 0 || --open_count > 0)
return false;
-
+
closed_semaphore.blind_notify();
-
+
notify_closed(Geary.Folder.CloseReason.LOCAL_CLOSE);
notify_closed(Geary.Folder.CloseReason.FOLDER_CLOSED);
-
+
return false;
}
-
+
public override async void wait_for_close_async(Cancellable? cancellable = null) throws Error {
yield closed_semaphore.wait_async(cancellable);
}
diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala
index d4e77012..b4828b95 100644
--- a/src/engine/api/geary-account.vala
+++ b/src/engine/api/geary-account.vala
@@ -134,9 +134,9 @@ public abstract class Geary.Account : BaseObject {
public signal void opened();
-
+
public signal void closed();
-
+
public signal void email_sent(Geary.RFC822.Message rfc822);
/**
@@ -203,42 +203,42 @@ public abstract class Geary.Account : BaseObject {
* Fired when a Folder's contents is detected having changed.
*/
public signal void folders_contents_altered(Gee.Collection<Geary.Folder> altered);
-
+
/**
* Fired when a Folder's type is detected having changed.
*/
public signal void folders_special_type(Gee.Collection<Geary.Folder> altered);
-
+
/**
* Fired when emails are appended to a folder in this account.
*/
public signal void email_appended(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
-
+
/**
* Fired when emails are inserted to a folder in this account.
*
* @see Folder.email_inserted
*/
public signal void email_inserted(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
-
+
/**
* Fired when emails are removed from a folder in this account.
*/
public signal void email_removed(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
-
+
/**
* Fired when one or more emails have been locally saved to a folder with
* the full set of Fields.
*/
public signal void email_locally_complete(Geary.Folder folder,
Gee.Collection<Geary.EmailIdentifier> ids);
-
+
/**
* Fired when one or more emails have been discovered (added) to the Folder, but not necessarily
* appended (i.e. old email pulled down due to user request or background fetching).
*/
public signal void email_discovered(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
-
+
/**
* Fired when the supplied email flags have changed from any folder.
*/
@@ -273,7 +273,7 @@ public abstract class Geary.Account : BaseObject {
* version of Geary.
*/
public abstract async void open_async(Cancellable? cancellable = null) throws Error;
-
+
/**
* Closes the {@link Account}, which makes most its operations unavailable.
*
@@ -282,12 +282,12 @@ public abstract class Geary.Account : BaseObject {
* Returns without error if the Account is already closed.
*/
public abstract async void close_async(Cancellable? cancellable = null) throws Error;
-
+
/**
* Returns true if this account is open, else false.
*/
public abstract bool is_open();
-
+
/**
* Rebuild the local data stores for this {@link Account}.
*
@@ -319,13 +319,13 @@ public abstract class Geary.Account : BaseObject {
*/
public abstract Gee.Collection<Geary.Folder> list_matching_folders(Geary.FolderPath? parent)
throws Error;
-
+
/**
* Lists all currently-available folders. See caveats under
* list_matching_folders().
*/
public abstract Gee.Collection<Geary.Folder> list_folders() throws Error;
-
+
/**
* Gets a perpetually update-to-date collection of autocompletion contacts.
*/
@@ -338,7 +338,7 @@ public abstract class Geary.Account : BaseObject {
*/
public abstract async bool folder_exists_async(Geary.FolderPath path, Cancellable? cancellable = null)
throws Error;
-
+
/**
* Fetches a Folder object corresponding to the supplied path. If the backing medium does
* not have a record of a folder at the path, EngineError.NOT_FOUND will be thrown.
@@ -350,7 +350,7 @@ public abstract class Geary.Account : BaseObject {
*/
public abstract async Geary.Folder fetch_folder_async(Geary.FolderPath path,
Cancellable? cancellable = null) throws Error;
-
+
/**
* Returns the folder representing the given special folder type. If no such folder exists,
* null is returned.
@@ -359,7 +359,7 @@ public abstract class Geary.Account : BaseObject {
return traverse<Folder>(list_folders())
.first_matching(f => f.special_folder_type == special);
}
-
+
/**
* Returns the Folder object with the given special folder type. The folder will be
* created on the server if it doesn't already exist. An error will be thrown if the
@@ -368,7 +368,7 @@ public abstract class Geary.Account : BaseObject {
*/
public abstract async Geary.Folder get_required_special_folder_async(Geary.SpecialFolderType special,
Cancellable? cancellable = null) throws Error;
-
+
/**
* Submits a ComposedEmail for delivery. Messages may be scheduled for later delivery or immediately
* sent. Subscribe to the "email-sent" signal to be notified of delivery. Note that that signal
@@ -377,7 +377,7 @@ public abstract class Geary.Account : BaseObject {
*/
public abstract async void send_email_async(Geary.ComposedEmail composed, Cancellable? cancellable =
null)
throws Error;
-
+
/**
* Search the local account for emails referencing a Message-ID value
* (which can appear in the Message-ID header itself, as well as the
@@ -392,7 +392,7 @@ public abstract class Geary.Account : BaseObject {
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 a single email fulfilling the required fields. The email to pull
* is identified by an EmailIdentifier from a previous call to
@@ -402,7 +402,7 @@ public abstract class Geary.Account : BaseObject {
*/
public abstract async Geary.Email local_fetch_email_async(Geary.EmailIdentifier email_id,
Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error;
-
+
/**
* Create a new {@link SearchQuery} for this {@link Account}.
*
@@ -419,7 +419,7 @@ public abstract class Geary.Account : BaseObject {
* Dropping the last reference to the SearchQuery will close it.
*/
public abstract Geary.SearchQuery open_search(string query, Geary.SearchQuery.Strategy strategy);
-
+
/**
* Performs a search with the given query. Optionally, a list of folders not to search
* can be passed as well as a list of email identifiers to restrict the search to only those messages.
@@ -431,13 +431,13 @@ public abstract class Geary.Account : BaseObject {
public abstract 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;
-
+
/**
* Given a list of mail IDs, returns a set of casefolded words that match for the query.
*/
public abstract async Gee.Set<string>? get_search_matches_async(Geary.SearchQuery query,
Gee.Collection<Geary.EmailIdentifier> ids, Cancellable? cancellable = null) throws Error;
-
+
/**
* Return a map of each passed-in email identifier to the set of folders
* that contain it. If an email id doesn't appear in the resulting map,
@@ -447,7 +447,7 @@ public abstract class Geary.Account : BaseObject {
*/
public abstract async Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath>?
get_containing_folders_async(
Gee.Collection<Geary.EmailIdentifier> ids, Cancellable? cancellable) throws Error;
-
+
/**
* Used only for debugging. Should not be used for user-visible strings.
*/
diff --git a/src/engine/api/geary-aggregated-folder-properties.vala
b/src/engine/api/geary-aggregated-folder-properties.vala
index cfb5825c..750c3263 100644
--- a/src/engine/api/geary-aggregated-folder-properties.vala
+++ b/src/engine/api/geary-aggregated-folder-properties.vala
@@ -17,7 +17,7 @@ private class Geary.AggregatedFolderProperties : Geary.FolderProperties {
// Map of child FolderProperties to their bindings.
private Gee.Map<FolderProperties, Gee.List<Binding>> child_bindings
= new Gee.HashMap<FolderProperties, Gee.List<Binding>>();
-
+
/**
* Creates an aggregate FolderProperties.
*/
@@ -25,7 +25,7 @@ private class Geary.AggregatedFolderProperties : Geary.FolderProperties {
// Set defaults.
base(0, 0, Trillian.UNKNOWN, Trillian.UNKNOWN, Trillian.UNKNOWN, is_local_only, is_virtual, false);
}
-
+
/**
* Adds a child FolderProperties. The child's property values will overwrite
* this class's property values.
@@ -36,7 +36,7 @@ private class Geary.AggregatedFolderProperties : Geary.FolderProperties {
assert(bindings != null);
child_bindings.set(child, bindings);
}
-
+
/**
* Removes a child FolderProperties.
*/
@@ -44,10 +44,10 @@ private class Geary.AggregatedFolderProperties : Geary.FolderProperties {
Gee.List<Binding> bindings;
if (child_bindings.unset(child, out bindings)) {
Geary.ObjectUtils.unmirror_properties(bindings);
-
+
return true;
}
-
+
return false;
}
}
diff --git a/src/engine/api/geary-composed-email.vala b/src/engine/api/geary-composed-email.vala
index 034934e6..48467539 100644
--- a/src/engine/api/geary-composed-email.vala
+++ b/src/engine/api/geary-composed-email.vala
@@ -21,7 +21,7 @@ public class Geary.ComposedEmail : BaseObject {
| Geary.Email.Field.REFERENCES
| Geary.Email.Field.SUBJECT
| Geary.Email.Field.DATE;
-
+
public DateTime date { get; set; }
// TODO: sender goes here, but not beyond, as it's not properly supported by GMime yet.
public RFC822.MailboxAddress? sender { get; set; default = null; }
@@ -47,7 +47,7 @@ public class Geary.ComposedEmail : BaseObject {
public string img_src_prefix { get; set; default = ""; }
- public ComposedEmail(DateTime date, RFC822.MailboxAddresses from,
+ public ComposedEmail(DateTime date, RFC822.MailboxAddresses from,
RFC822.MailboxAddresses? to = null, RFC822.MailboxAddresses? cc = null,
RFC822.MailboxAddresses? bcc = null, string? subject = null,
string? body_text = null, string? body_html = null) {
@@ -60,7 +60,7 @@ public class Geary.ComposedEmail : BaseObject {
this.body_text = body_text;
this.body_html = body_html;
}
-
+
public Geary.RFC822.Message to_rfc822_message(string? message_id = null) {
return new RFC822.Message.from_composed_email(this, message_id);
}
diff --git a/src/engine/api/geary-contact-flags.vala b/src/engine/api/geary-contact-flags.vala
index 3ecb910b..5497b7ae 100644
--- a/src/engine/api/geary-contact-flags.vala
+++ b/src/engine/api/geary-contact-flags.vala
@@ -15,35 +15,35 @@ public class Geary.ContactFlags : Geary.NamedFlags {
public static NamedFlag ALWAYS_LOAD_REMOTE_IMAGES { get {
if (_always_load_remote_images == null)
_always_load_remote_images = new NamedFlag("ALWAYSLOADREMOTEIMAGES");
-
+
return _always_load_remote_images;
} }
-
+
public ContactFlags() {
}
-
+
public static ContactFlags deserialize(string? flags) {
if (String.is_empty(flags))
return new ContactFlags();
-
+
ContactFlags result = new ContactFlags();
-
+
string[] tokens = flags.split(" ");
foreach (string flag in tokens)
result.add(new NamedFlag(flag));
-
+
return result;
}
-
+
public inline bool always_load_remote_images() {
return contains(ALWAYS_LOAD_REMOTE_IMAGES);
}
-
+
public string serialize() {
string ret = "";
foreach (NamedFlag flag in list)
ret += flag.serialize() + " ";
-
+
return ret.strip();
}
}
diff --git a/src/engine/api/geary-contact-importance.vala b/src/engine/api/geary-contact-importance.vala
index 0ea9b25e..235c8f6d 100644
--- a/src/engine/api/geary-contact-importance.vala
+++ b/src/engine/api/geary-contact-importance.vala
@@ -17,7 +17,7 @@
* || CC || appeared in the 'CC' or 'BCC' fields OR did not appear in any field (assuming BCC) ||
*
* "Examples:"
- *
+ *
* || "Enum Value" || "Account Owner" || "Contact" ||
* || FROM_TO || Appeared in 'from' or 'sender' || Appeared in 'to' ||
* || CC_FROM || Appeared in 'CC', 'BCC', or did not appear || Appeared in 'from' or 'sender'. ||
diff --git a/src/engine/api/geary-contact-store.vala b/src/engine/api/geary-contact-store.vala
index c7b3579d..f7c522f1 100644
--- a/src/engine/api/geary-contact-store.vala
+++ b/src/engine/api/geary-contact-store.vala
@@ -3,22 +3,22 @@
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
-
+
public abstract class Geary.ContactStore : BaseObject {
public Gee.Collection<Contact> contacts {
owned get { return contact_map.values; }
}
-
+
private Gee.Map<string, Contact> contact_map;
-
+
public signal void contacts_added(Gee.Collection<Contact> contacts);
-
+
public signal void contacts_updated(Gee.Collection<Contact> contacts);
-
+
protected ContactStore() {
contact_map = new Gee.HashMap<string, Contact>();
}
-
+
public void update_contacts(Gee.Collection<Contact> new_contacts) {
Gee.LinkedList<Contact> added = new Gee.LinkedList<Contact>();
Gee.LinkedList<Contact> updated = new Gee.LinkedList<Contact>();
@@ -40,10 +40,10 @@ public abstract class Geary.ContactStore : BaseObject {
if (!updated.is_empty)
contacts_updated(updated);
}
-
+
public abstract async void mark_contacts_async(Gee.Collection<Contact> contacts, ContactFlags? to_add,
ContactFlags? to_remove) throws Error;
-
+
public Contact? get_by_rfc822(Geary.RFC822.MailboxAddress address) {
return contact_map[address.address.normalize().casefold()];
}
diff --git a/src/engine/api/geary-contact.vala b/src/engine/api/geary-contact.vala
index ea963555..34c6ae3a 100644
--- a/src/engine/api/geary-contact.vala
+++ b/src/engine/api/geary-contact.vala
@@ -10,7 +10,7 @@ public class Geary.Contact : BaseObject {
public string? real_name { get; private set; }
public int highest_importance { get; set; }
public ContactFlags? contact_flags { get; set; default = null; }
-
+
public Contact(string email, string? real_name, int highest_importance,
string? normalized_email = null, ContactFlags? contact_flags = null) {
this.normalized_email = normalized_email ?? email.normalize().casefold();
@@ -19,15 +19,15 @@ public class Geary.Contact : BaseObject {
this.highest_importance = highest_importance;
this.contact_flags = contact_flags;
}
-
+
public Contact.from_rfc822_address(RFC822.MailboxAddress address, int highest_importance) {
this(address.address, address.name, highest_importance);
}
-
+
public RFC822.MailboxAddress get_rfc822_address() {
return new RFC822.MailboxAddress(real_name, email);
}
-
+
public inline bool always_load_remote_images() {
return contact_flags != null && contact_flags.always_load_remote_images();
}
diff --git a/src/engine/api/geary-email-flags.vala b/src/engine/api/geary-email-flags.vala
index 0831c8d5..018bf127 100644
--- a/src/engine/api/geary-email-flags.vala
+++ b/src/engine/api/geary-email-flags.vala
@@ -26,7 +26,7 @@ public class Geary.EmailFlags : Geary.NamedFlags {
public static NamedFlag LOAD_REMOTE_IMAGES { owned get {
return new NamedFlag("LOADREMOTEIMAGES");
} }
-
+
public static NamedFlag DRAFT { owned get {
return new NamedFlag("DRAFT");
} }
@@ -34,29 +34,29 @@ public class Geary.EmailFlags : Geary.NamedFlags {
public static NamedFlag DELETED { owned get {
return new NamedFlag("DELETED");
} }
-
+
/// Signifies a message in our outbox that has been sent but we're still
/// keeping around for other purposes, i.e. pushing up to Sent Mail.
public static NamedFlag OUTBOX_SENT { owned get {
// This shouldn't ever touch the wire, so make it invalid IMAP.
return new NamedFlag(" OUTBOX SENT ");
} }
-
+
public EmailFlags() {
}
-
+
/**
* Create a new {@link EmailFlags} container initialized with one or more flags.
*/
public EmailFlags.with(Geary.NamedFlag flag1, ...) {
va_list args = va_list();
NamedFlag? flag = flag1;
-
+
do {
add(flag);
} while((flag = args.arg()) != null);
}
-
+
// Convenience method to check if the unread flag is set.
public inline bool is_unread() {
return contains(UNREAD);
@@ -65,15 +65,15 @@ public class Geary.EmailFlags : Geary.NamedFlags {
public inline bool is_flagged() {
return contains(FLAGGED);
}
-
+
public inline bool load_remote_images() {
return contains(LOAD_REMOTE_IMAGES);
}
-
+
public inline bool is_draft() {
return contains(DRAFT);
}
-
+
public inline bool is_outbox_sent() {
return contains(OUTBOX_SENT);
}
diff --git a/src/engine/api/geary-email-identifier.vala b/src/engine/api/geary-email-identifier.vala
index e044ab14..017faef6 100644
--- a/src/engine/api/geary-email-identifier.vala
+++ b/src/engine/api/geary-email-identifier.vala
@@ -19,22 +19,22 @@
public abstract class Geary.EmailIdentifier : BaseObject, Gee.Hashable<Geary.EmailIdentifier> {
// Warning: only change this if you know what you are doing.
protected string unique;
-
+
protected EmailIdentifier(string unique) {
this.unique = unique;
}
-
+
public virtual uint hash() {
return unique.hash();
}
-
+
public virtual bool equal_to(Geary.EmailIdentifier other) {
if (this == other)
return true;
-
+
return unique == other.unique;
}
-
+
/**
* A comparator for stabilizing sorts.
*
@@ -44,10 +44,10 @@ public abstract class Geary.EmailIdentifier : BaseObject, Gee.Hashable<Geary.Ema
public virtual int stable_sort_comparator(Geary.EmailIdentifier other) {
if (this == other)
return 0;
-
+
return strcmp(unique, other.unique);
}
-
+
/**
* A comparator for finding which {@link EmailIdentifier} is earliest in the "natural"
* sorting of a {@link Folder}'s list.
@@ -68,7 +68,7 @@ public abstract class Geary.EmailIdentifier : BaseObject, Gee.Hashable<Geary.Ema
* @see Folder.list_email_by_id_async
*/
public abstract int natural_sort_comparator(Geary.EmailIdentifier other);
-
+
/**
* Sorts the supplied Collection of {@link EmailIdentifier} by their natural sort order.
*
@@ -82,14 +82,14 @@ public abstract class Geary.EmailIdentifier : BaseObject, Gee.Hashable<Geary.Ema
int cmp = a.natural_sort_comparator(b);
if (cmp == 0)
cmp = a.stable_sort_comparator(b);
-
+
return cmp;
});
sorted.add_all(ids);
-
+
return sorted;
}
-
+
/**
* Sorts the supplied Collection of {@link EmailIdentifier} by their natural sort order.
*
@@ -103,14 +103,14 @@ public abstract class Geary.EmailIdentifier : BaseObject, Gee.Hashable<Geary.Ema
int cmp = a.id.natural_sort_comparator(b.id);
if (cmp == 0)
cmp = a.id.stable_sort_comparator(b.id);
-
+
return cmp;
});
sorted.add_all(emails);
-
+
return sorted;
}
-
+
public virtual string to_string() {
return "[%s]".printf(unique.to_string());
}
diff --git a/src/engine/api/geary-email-properties.vala b/src/engine/api/geary-email-properties.vala
index de9d9dbe..b1b89f30 100644
--- a/src/engine/api/geary-email-properties.vala
+++ b/src/engine/api/geary-email-properties.vala
@@ -20,17 +20,17 @@ public abstract class Geary.EmailProperties : BaseObject {
* the INTERNALDATE supplied by the server.
*/
public DateTime date_received { get; protected set; }
-
+
/**
* Total size of the email (header and body) in bytes.
*/
public int64 total_bytes { get; protected set; }
-
+
public EmailProperties(DateTime date_received, int64 total_bytes) {
this.date_received = date_received;
this.total_bytes = total_bytes;
}
-
+
public abstract string to_string();
}
diff --git a/src/engine/api/geary-email.vala b/src/engine/api/geary-email.vala
index 296f55e0..4242faf1 100644
--- a/src/engine/api/geary-email.vala
+++ b/src/engine/api/geary-email.vala
@@ -124,50 +124,50 @@ public class Geary.Email : BaseObject {
FLAGS
};
}
-
+
public inline bool is_all_set(Field required_fields) {
return (this & required_fields) == required_fields;
}
-
+
public inline bool is_any_set(Field required_fields) {
return (this & required_fields) != 0;
}
-
+
public inline Field set(Field field) {
return (this | field);
}
-
+
public inline Field clear(Field field) {
return (this & ~(field));
}
-
+
public inline bool fulfills(Field required_fields) {
return is_all_set(required_fields);
}
-
+
public inline bool fulfills_any(Field required_fields) {
return is_any_set(required_fields);
}
-
+
public inline bool require(Field required_fields) {
return is_all_set(required_fields);
}
-
+
public inline bool requires_any(Field required_fields) {
return is_any_set(required_fields);
}
-
+
public string to_list_string() {
StringBuilder builder = new StringBuilder();
foreach (Field f in all()) {
if (is_all_set(f)) {
if (!String.is_empty(builder.str))
builder.append(", ");
-
+
builder.append(f.to_string());
}
}
-
+
return builder.str;
}
}
@@ -195,29 +195,29 @@ public class Geary.Email : BaseObject {
public Geary.RFC822.MailboxAddresses? to { get; private set; default = null; }
public Geary.RFC822.MailboxAddresses? cc { get; private set; default = null; }
public Geary.RFC822.MailboxAddresses? bcc { get; private set; default = null; }
-
+
// REFERENCES
public Geary.RFC822.MessageID? message_id { get; private set; default = null; }
public Geary.RFC822.MessageIDList? in_reply_to { get; private set; default = null; }
public Geary.RFC822.MessageIDList? references { get; private set; default = null; }
-
+
// SUBJECT
public Geary.RFC822.Subject? subject { get; private set; default = null; }
-
+
// HEADER
public RFC822.Header? header { get; private set; default = null; }
-
+
// BODY
public RFC822.Text? body { get; private set; default = null; }
public Gee.List<Geary.Attachment> attachments { get; private set;
default = new Gee.ArrayList<Geary.Attachment>(); }
-
+
// PROPERTIES
public Geary.EmailProperties? properties { get; private set; default = null; }
-
+
// PREVIEW
public RFC822.PreviewText? preview { get; private set; default = null; }
-
+
// FLAGS
public Geary.EmailFlags? email_flags { get; private set; default = null; }
@@ -238,7 +238,7 @@ public class Geary.Email : BaseObject {
public Geary.Email.Field fields { get; private set; default = Field.NONE; }
private Geary.RFC822.Message? message = null;
-
+
public Email(Geary.EmailIdentifier id) {
this.id = id;
}
@@ -278,7 +278,7 @@ public class Geary.Email : BaseObject {
public void set_send_date(Geary.RFC822.Date? date) {
this.date = date;
-
+
fields |= Field.DATE;
}
@@ -307,69 +307,69 @@ public class Geary.Email : BaseObject {
this.to = to;
this.cc = cc;
this.bcc = bcc;
-
+
fields |= Field.RECEIVERS;
}
-
+
public void set_full_references(Geary.RFC822.MessageID? message_id, Geary.RFC822.MessageIDList?
in_reply_to,
Geary.RFC822.MessageIDList? references) {
this.message_id = message_id;
this.in_reply_to = in_reply_to;
this.references = references;
-
+
fields |= Field.REFERENCES;
}
-
+
public void set_message_subject(Geary.RFC822.Subject? subject) {
this.subject = subject;
-
+
fields |= Field.SUBJECT;
}
-
+
public void set_message_header(Geary.RFC822.Header header) {
this.header = header;
-
+
// reset the message object, which is built from this text
message = null;
-
+
fields |= Field.HEADER;
}
-
+
public void set_message_body(Geary.RFC822.Text body) {
this.body = body;
-
+
// reset the message object, which is built from this text
message = null;
-
+
fields |= Field.BODY;
}
-
+
public void set_email_properties(Geary.EmailProperties properties) {
this.properties = properties;
-
+
fields |= Field.PROPERTIES;
}
-
+
public void set_message_preview(Geary.RFC822.PreviewText preview) {
this.preview = preview;
-
+
fields |= Field.PREVIEW;
}
public void set_flags(Geary.EmailFlags email_flags) {
this.email_flags = email_flags;
-
+
fields |= Field.FLAGS;
}
public void add_attachment(Geary.Attachment attachment) {
attachments.add(attachment);
}
-
+
public void add_attachments(Gee.Collection<Geary.Attachment> attachments) {
this.attachments.add_all(attachments);
}
-
+
public string get_searchable_attachment_list() {
StringBuilder search = new StringBuilder();
foreach (Geary.Attachment attachment in attachments) {
@@ -391,12 +391,12 @@ public class Geary.Email : BaseObject {
public Geary.RFC822.Message get_message() throws EngineError, RFC822Error {
if (message != null)
return message;
-
+
if (!fields.fulfills(REQUIRED_FOR_MESSAGE))
throw new EngineError.INCOMPLETE_MESSAGE("Parsed email requires HEADER and BODY");
-
+
message = new Geary.RFC822.Message.from_parts(header, body);
-
+
return message;
}
@@ -427,27 +427,27 @@ public class Geary.Email : BaseObject {
*/
public Gee.Set<RFC822.MessageID>? get_ancestors() {
Gee.Set<RFC822.MessageID> ancestors = new Gee.HashSet<RFC822.MessageID>();
-
+
// the email's Message-ID counts as its lineage
if (message_id != null)
ancestors.add(message_id);
-
+
// References list the email trail back to its source
if (references != null)
ancestors.add_all(references.list);
-
+
// RFC822 requires the In-Reply-To Message-ID be prepended to the References list, but
// this ensures that's the case
if (in_reply_to != null)
ancestors.add_all(in_reply_to.list);
-
+
return (ancestors.size > 0) ? ancestors : null;
}
-
+
public string get_preview_as_string() {
return (preview != null) ? preview.buffer.to_string() : "";
}
-
+
/**
* Returns the primary originator of an email, which is defined as the first mailbox address
* in From:, Sender:, or Reply-To:, in that order, depending on availability.
@@ -470,7 +470,7 @@ public class Geary.Email : BaseObject {
public string to_string() {
return "[%s] ".printf(id.to_string());
}
-
+
/**
* Converts a Collection of {@link Email}s to a Map of Emails keyed by {@link EmailIdentifier}s.
*
@@ -479,15 +479,15 @@ public class Geary.Email : BaseObject {
public static Gee.Map<Geary.EmailIdentifier, Geary.Email>? emails_to_map(Gee.Collection<Geary.Email>?
emails) {
if (emails == null || emails.size == 0)
return null;
-
+
Gee.Map<Geary.EmailIdentifier, Geary.Email> map = new Gee.HashMap<Geary.EmailIdentifier,
Geary.Email>();
foreach (Email email in emails)
map.set(email.id, email);
-
+
return map;
}
-
+
/**
* CompareFunc to sort {@link Email} by {@link date} ascending.
*
@@ -497,16 +497,16 @@ public class Geary.Email : BaseObject {
public static int compare_sent_date_ascending(Geary.Email aemail, Geary.Email bemail) {
if (aemail.date == null || bemail.date == null) {
GLib.message("Warning: comparing email for sent date but no Date: field loaded");
-
+
return compare_id_ascending(aemail, bemail);
}
-
+
int compare = aemail.date.value.compare(bemail.date.value);
-
+
// stabilize sort by using the mail identifier's stable sort ordering
return (compare != 0) ? compare : compare_id_ascending(aemail, bemail);
}
-
+
/**
* CompareFunc to sort {@link Email} by {@link date} descending.
*
@@ -516,7 +516,7 @@ public class Geary.Email : BaseObject {
public static int compare_sent_date_descending(Geary.Email aemail, Geary.Email bemail) {
return compare_sent_date_ascending(bemail, aemail);
}
-
+
/**
* CompareFunc to sort {@link Email} by {@link EmailProperties.date_received} ascending.
*
@@ -526,16 +526,16 @@ public class Geary.Email : BaseObject {
public static int compare_recv_date_ascending(Geary.Email aemail, Geary.Email bemail) {
if (aemail.properties == null || bemail.properties == null) {
GLib.message("Warning: comparing email for received date but email properties not loaded");
-
+
return compare_id_ascending(aemail, bemail);
}
-
+
int compare = aemail.properties.date_received.compare(bemail.properties.date_received);
-
+
// stabilize sort with identifiers
return (compare != 0) ? compare : compare_id_ascending(aemail, bemail);
}
-
+
/**
* CompareFunc to sort {@link Email} by {@link EmailProperties.date_received} descending.
*
@@ -545,12 +545,12 @@ public class Geary.Email : BaseObject {
public static int compare_recv_date_descending(Geary.Email aemail, Geary.Email bemail) {
return compare_recv_date_ascending(bemail, aemail);
}
-
+
// only used to stabilize a sort
private static int compare_id_ascending(Geary.Email aemail, Geary.Email bemail) {
return aemail.id.stable_sort_comparator(bemail.id);
}
-
+
/**
* CompareFunc to sort Email by EmailProperties.total_bytes. If not available, emails are
* compared by EmailIdentifier.
@@ -558,18 +558,18 @@ public class Geary.Email : BaseObject {
public static int compare_size_ascending(Geary.Email aemail, Geary.Email bemail) {
Geary.EmailProperties? aprop = (Geary.EmailProperties) aemail.properties;
Geary.EmailProperties? bprop = (Geary.EmailProperties) bemail.properties;
-
+
if (aprop == null || bprop == null) {
GLib.message("Warning: comparing email by size but email properties not loaded");
-
+
return compare_id_ascending(aemail, bemail);
}
-
+
int cmp = (int) (aprop.total_bytes - bprop.total_bytes).clamp(-1, 1);
-
+
return (cmp != 0) ? cmp : compare_id_ascending(aemail, bemail);
}
-
+
/**
* CompareFunc to sort Email by EmailProperties.total_bytes. If not available, emails are
* compared by EmailIdentifier.
diff --git a/src/engine/api/geary-engine.vala b/src/engine/api/geary-engine.vala
index 4b947802..c0000010 100644
--- a/src/engine/api/geary-engine.vala
+++ b/src/engine/api/geary-engine.vala
@@ -97,7 +97,7 @@ public class Geary.Engine : BaseObject {
if (!is_open)
throw new EngineError.OPEN_REQUIRED("Geary.Engine instance not open");
}
-
+
// This can't be called from within the ctor, as initialization code may want to access the
// Engine instance to make their own calls and, in particular, subscribe to signals.
//
@@ -107,7 +107,7 @@ public class Geary.Engine : BaseObject {
private void initialize_library() {
if (is_initialized)
return;
-
+
is_initialized = true;
Logging.init();
diff --git a/src/engine/api/geary-folder-properties.vala b/src/engine/api/geary-folder-properties.vala
index 33c9caf9..9015f37a 100644
--- a/src/engine/api/geary-folder-properties.vala
+++ b/src/engine/api/geary-folder-properties.vala
@@ -12,24 +12,24 @@ public abstract class Geary.FolderProperties : BaseObject {
public const string PROP_NAME_IS_OPENABLE = "is-openable";
public const string PROP_NAME_IS_LOCAL_ONLY = "is-local-only";
public const string PROP_NAME_IS_VIRTUAL = "is-virtual";
-
+
/**
* The total count of email in the {@link Folder}.
*/
public int email_total { get; protected set; }
-
+
/**
* The total count of unread email in the {@link Folder}.
*/
public int email_unread { get; protected set; }
-
+
/**
* Returns a {@link Trillian} indicating if this {@link Folder} has children.
*
* has_children == {@link Trillian.TRUE} implies {@link supports_children} == Trilian.TRUE.
*/
public Trillian has_children { get; protected set; }
-
+
/**
* Returns a {@link Trillian} indicating if this {@link Folder} can parent new children
* {@link Folder}s.
@@ -37,12 +37,12 @@ public abstract class Geary.FolderProperties : BaseObject {
* This does ''not'' mean creating a sub-folder is guaranteed to succeed.
*/
public Trillian supports_children { get; protected set; }
-
+
/**
* Returns a {@link Trillian} indicating if {@link Folder.open_async} can succeed remotely.
*/
public Trillian is_openable { get; protected set; }
-
+
/**
* Returns true if the {@link Folder} is local-only, that is, has no remote folder backing
* it.
@@ -52,7 +52,7 @@ public abstract class Geary.FolderProperties : BaseObject {
* a Folder interface.
*/
public bool is_local_only { get; private set; }
-
+
/**
* Returns true if the {@link Folder} is virtual, that is, it is either generated by some
* external criteria and/or is aggregating the content of other Folders.
@@ -61,7 +61,7 @@ public abstract class Geary.FolderProperties : BaseObject {
* copy.
*/
public bool is_virtual { get; private set; }
-
+
/**
* True if the {@link Folder} offers the {@link FolderSupport.Create} interface but is
* guaranteed not to return a {@link EmailIdentifier}, even if
@@ -71,7 +71,7 @@ public abstract class Geary.FolderProperties : BaseObject {
* will usually be false.
*/
public bool create_never_returns_id { get; protected set; }
-
+
protected FolderProperties(int email_total, int email_unread, Trillian has_children,
Trillian supports_children, Trillian is_openable, bool is_local_only, bool is_virtual,
bool create_never_returns_id) {
diff --git a/src/engine/api/geary-folder.vala b/src/engine/api/geary-folder.vala
index f8c0cea2..b6f57fc8 100644
--- a/src/engine/api/geary-folder.vala
+++ b/src/engine/api/geary-folder.vala
@@ -123,12 +123,12 @@ public abstract class Geary.Folder : BaseObject {
REMOTE_CLOSE,
REMOTE_ERROR,
FOLDER_CLOSED;
-
+
public bool is_error() {
return (this == LOCAL_ERROR) || (this == REMOTE_ERROR);
}
}
-
+
[Flags]
public enum CountChangeReason {
NONE = 0,
@@ -197,27 +197,27 @@ public abstract class Geary.Folder : BaseObject {
public bool is_any_set(ListFlags flags) {
return (this & flags) != 0;
}
-
+
public bool is_all_set(ListFlags flags) {
return (this & flags) == flags;
}
-
+
public bool is_local_only() {
return is_all_set(LOCAL_ONLY);
}
-
+
public bool is_force_update() {
return is_all_set(FORCE_UPDATE);
}
-
+
public bool is_including_id() {
return is_all_set(INCLUDING_ID);
}
-
+
public bool is_oldest_to_newest() {
return is_all_set(OLDEST_TO_NEWEST);
}
-
+
public bool is_newest_to_oldest() {
return !is_oldest_to_newest();
}
@@ -278,7 +278,7 @@ public abstract class Geary.Folder : BaseObject {
* Error.
*
* This signal may be fired more than once before the Folder is
- * closed, especially in the case of a remote session
+ * closed, especially in the case of a remote session
*/
public signal void open_failed(OpenFailed failure, Error? err);
@@ -302,7 +302,7 @@ public abstract class Geary.Folder : BaseObject {
* @see email_locally_appended
*/
public signal void email_appended(Gee.Collection<Geary.EmailIdentifier> ids);
-
+
/**
* Fired when previously unknown messages have been appended to the list of email in the folder.
*
@@ -315,7 +315,7 @@ public abstract class Geary.Folder : BaseObject {
* @see email_appended
*/
public signal void email_locally_appended(Gee.Collection<Geary.EmailIdentifier> ids);
-
+
/**
* Fired when email has been inserted into the list of messages in the folder.
*
@@ -327,7 +327,7 @@ public abstract class Geary.Folder : BaseObject {
* @see email_locally_inserted
*/
public signal void email_inserted(Gee.Collection<Geary.EmailIdentifier> ids);
-
+
/**
* Fired when previously unknown messages have been appended to the list of email in the folder.
*
@@ -341,7 +341,7 @@ public abstract class Geary.Folder : BaseObject {
* @see email_locally_inserted
*/
public signal void email_locally_inserted(Gee.Collection<Geary.EmailIdentifier> ids);
-
+
/**
* Fired when email has been removed (deleted or moved) from the folder.
*
@@ -354,7 +354,7 @@ public abstract class Geary.Folder : BaseObject {
* signal will ''not'' fire, although {@link email_count_changed} will.
*/
public signal void email_removed(Gee.Collection<Geary.EmailIdentifier> ids);
-
+
/**
* Fired when the total count of email in a folder has changed in any way.
*
@@ -362,19 +362,19 @@ public abstract class Geary.Folder : BaseObject {
* and {@link email_removed} (although see the note at email_removed).
*/
public signal void email_count_changed(int new_count, CountChangeReason reason);
-
+
/**
* Fired when the supplied email flags have changed, whether due to local action or reported by
* the server.
*/
public signal void email_flags_changed(Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> map);
-
+
/**
* Fired when one or more emails have been locally saved with the full set
* of Fields.
*/
public signal void email_locally_complete(Gee.Collection<Geary.EmailIdentifier> ids);
-
+
/**
* Fired when the {@link SpecialFolderType} has changed.
*
@@ -383,58 +383,58 @@ public abstract class Geary.Folder : BaseObject {
*/
public signal void special_folder_type_changed(Geary.SpecialFolderType old_type,
Geary.SpecialFolderType new_type);
-
+
/**
* Fired when the Folder's display name has changed.
*
* @see get_display_name
*/
public signal void display_name_changed();
-
+
protected Folder() {
}
-
+
protected virtual void notify_opened(Geary.Folder.OpenState state, int count) {
opened(state, count);
}
-
+
protected virtual void notify_open_failed(Geary.Folder.OpenFailed failure, Error? err) {
open_failed(failure, err);
}
-
+
protected virtual void notify_closed(Geary.Folder.CloseReason reason) {
closed(reason);
}
-
+
protected virtual void notify_email_appended(Gee.Collection<Geary.EmailIdentifier> ids) {
email_appended(ids);
}
-
+
protected virtual void notify_email_locally_appended(Gee.Collection<Geary.EmailIdentifier> ids) {
email_locally_appended(ids);
}
-
+
protected virtual void notify_email_inserted(Gee.Collection<Geary.EmailIdentifier> ids) {
email_inserted(ids);
}
-
+
protected virtual void notify_email_locally_inserted(Gee.Collection<Geary.EmailIdentifier> ids) {
email_locally_inserted(ids);
}
-
+
protected virtual void notify_email_removed(Gee.Collection<Geary.EmailIdentifier> ids) {
email_removed(ids);
}
-
+
protected virtual void notify_email_count_changed(int new_count, Folder.CountChangeReason reason) {
email_count_changed(new_count, reason);
}
-
+
protected virtual void notify_email_flags_changed(Gee.Map<Geary.EmailIdentifier,
Geary.EmailFlags> flag_map) {
email_flags_changed(flag_map);
}
-
+
protected virtual void notify_email_locally_complete(Gee.Collection<Geary.EmailIdentifier> ids) {
email_locally_complete(ids);
}
@@ -639,7 +639,7 @@ public abstract class Geary.Folder : BaseObject {
public abstract async Gee.List<Geary.Email>? list_email_by_sparse_id_async(
Gee.Collection<Geary.EmailIdentifier> ids, Geary.Email.Field required_fields, ListFlags flags,
Cancellable? cancellable = null) throws Error;
-
+
/**
* Returns the locally available Geary.Email.Field fields for the specified emails. If a
* list or fetch operation occurs on the emails that specifies a field not returned here,
@@ -652,7 +652,7 @@ public abstract class Geary.Folder : BaseObject {
*/
public abstract async Gee.Map<Geary.EmailIdentifier, Geary.Email.Field>? list_local_email_fields_async(
Gee.Collection<Geary.EmailIdentifier> ids, Cancellable? cancellable = null) throws Error;
-
+
/**
* Returns a single email that fulfills the required_fields flag at the ordered position in
* the folder. If the email_id is invalid for the folder's contents, an EngineError.NOT_FOUND
diff --git a/src/engine/api/geary-named-flag.vala b/src/engine/api/geary-named-flag.vala
index b8067ed7..eefd8824 100644
--- a/src/engine/api/geary-named-flag.vala
+++ b/src/engine/api/geary-named-flag.vala
@@ -12,26 +12,26 @@
public class Geary.NamedFlag : BaseObject, Gee.Hashable<Geary.NamedFlag> {
public string name { get; private set; }
-
+
public NamedFlag(string name) {
this.name = name;
}
-
+
public bool equal_to(Geary.NamedFlag other) {
if (this == other)
return true;
-
+
return name.down() == other.name.down();
}
-
+
public uint hash() {
return name.down().hash();
}
-
+
public string serialize() {
return name;
}
-
+
public string to_string() {
return name;
}
diff --git a/src/engine/api/geary-named-flags.vala b/src/engine/api/geary-named-flags.vala
index 4f1668d3..56e48613 100644
--- a/src/engine/api/geary-named-flags.vala
+++ b/src/engine/api/geary-named-flags.vala
@@ -11,91 +11,91 @@
public class Geary.NamedFlags : BaseObject, Gee.Hashable<Geary.NamedFlags> {
protected Gee.Set<NamedFlag> list = new Gee.HashSet<NamedFlag>();
-
+
public virtual signal void added(Gee.Collection<NamedFlag> flags) {
}
-
+
public virtual signal void removed(Gee.Collection<NamedFlag> flags) {
}
-
+
public NamedFlags() {
}
-
+
protected virtual void notify_added(Gee.Collection<NamedFlag> flags) {
added(flags);
}
-
+
protected virtual void notify_removed(Gee.Collection<NamedFlag> flags) {
removed(flags);
}
-
+
public bool contains(NamedFlag flag) {
return list.contains(flag);
}
-
+
public bool contains_any(NamedFlags flags) {
return Geary.traverse<NamedFlag>(list).any(f => flags.contains(f));
}
-
+
public Gee.Set<NamedFlag> get_all() {
return list.read_only_view;
}
-
+
public virtual void add(NamedFlag flag) {
if (!list.contains(flag)) {
list.add(flag);
notify_added(Geary.iterate<NamedFlag>(flag).to_array_list());
}
}
-
+
public virtual void add_all(NamedFlags flags) {
Gee.ArrayList<NamedFlag> added = Geary.traverse<NamedFlag>(flags.get_all())
.filter(f => !list.contains(f))
.to_array_list();
-
+
list.add_all(added);
notify_added(added);
}
-
+
public virtual bool remove(NamedFlag flag) {
bool removed = list.remove(flag);
if (removed)
notify_removed(Geary.iterate<NamedFlag>(flag).to_array_list());
-
+
return removed;
}
-
+
public virtual bool remove_all(NamedFlags flags) {
Gee.ArrayList<NamedFlag> removed = Geary.traverse<NamedFlag>(flags.get_all())
.filter(f => list.contains(f))
.to_array_list();
-
+
list.remove_all(removed);
notify_removed(removed);
-
+
return removed.size > 0;
}
-
+
public bool equal_to(Geary.NamedFlags other) {
if (this == other)
return true;
-
+
if (list.size != other.list.size)
return false;
-
+
return Geary.traverse<NamedFlag>(list).all(f => other.contains(f));
}
-
+
public uint hash() {
return Geary.String.stri_hash(to_string());
}
-
+
public string to_string() {
string ret = "[";
foreach (NamedFlag flag in list) {
ret += flag.to_string() + " ";
}
-
+
return ret + "]";
}
}
diff --git a/src/engine/api/geary-progress-monitor.vala b/src/engine/api/geary-progress-monitor.vala
index 4ce4dc88..f9f26d3a 100644
--- a/src/engine/api/geary-progress-monitor.vala
+++ b/src/engine/api/geary-progress-monitor.vala
@@ -21,41 +21,41 @@ public enum Geary.ProgressType {
public abstract class Geary.ProgressMonitor : BaseObject {
public const double MIN = 0.0;
public const double MAX = 1.0;
-
+
public double progress { get; protected set; default = MIN; }
public bool is_in_progress { get; protected set; default = false; }
public Geary.ProgressType progress_type { get; protected set; }
-
+
/**
* The start signal is fired just before progress begins. It will not fire again until after
* {@link finish} has fired.
*/
public signal void start();
-
+
/**
* Notifies the user of existing progress. Note that monitor refers to the monitor that
* invoked this update, which may not be the same as this object.
*/
public signal void update(double total_progress, double change, Geary.ProgressMonitor monitor);
-
+
/**
* Finish is fired when progress has completed.
*/
public signal void finish();
-
+
/**
* Users must call this before calling update.
*
* Must not be called again until {@link ProgressMonitor.notify_finish} has been called.
- */
+ */
public virtual void notify_start() {
assert(!is_in_progress);
progress = MIN;
is_in_progress = true;
-
+
start();
}
-
+
/**
* Users must call this when progress has completed.
*
@@ -64,7 +64,7 @@ public abstract class Geary.ProgressMonitor : BaseObject {
public virtual void notify_finish() {
assert(is_in_progress);
is_in_progress = false;
-
+
finish();
}
}
@@ -77,11 +77,11 @@ public abstract class Geary.ProgressMonitor : BaseObject {
public class Geary.ReentrantProgressMonitor : Geary.ProgressMonitor {
private int start_count = 0;
-
+
public ReentrantProgressMonitor(ProgressType type) {
this.progress_type = type;
}
-
+
/**
* {@inheritDoc}
*
@@ -95,7 +95,7 @@ public class Geary.ReentrantProgressMonitor : Geary.ProgressMonitor {
if (start_count++ == 0)
base.notify_start();
}
-
+
/**
* {@inheritDoc}
*
@@ -106,10 +106,10 @@ public class Geary.ReentrantProgressMonitor : Geary.ProgressMonitor {
*/
public override void notify_finish() {
bool finished = (--start_count == 0);
-
+
// prevent underflow before signalling
start_count = start_count.clamp(0, int.MAX);
-
+
if (finished)
base.notify_finish();
}
@@ -125,7 +125,7 @@ public class Geary.SimpleProgressMonitor : Geary.ProgressMonitor {
public SimpleProgressMonitor(ProgressType type) {
this.progress_type = type;
}
-
+
/**
* Updates the progress by the given value. Must be between {@link ProgressMonitor.MIN} and
* {@link ProgressMonitor.MAX}.
@@ -136,10 +136,10 @@ public class Geary.SimpleProgressMonitor : Geary.ProgressMonitor {
public void increment(double value) {
assert(value > 0);
assert(is_in_progress);
-
+
if (progress + value > MAX)
value = MAX - progress;
-
+
progress += value;
update(progress, value, this);
}
@@ -152,7 +152,7 @@ public class Geary.IntervalProgressMonitor : Geary.ProgressMonitor {
private int min_interval;
private int max_interval;
private int current = 0;
-
+
/**
* Creates a new progress monitor with the given interval range.
*/
@@ -161,7 +161,7 @@ public class Geary.IntervalProgressMonitor : Geary.ProgressMonitor {
this.min_interval = min;
this.max_interval = max;
}
-
+
/**
* Sets a new interval. Must not be done while in progress.
*/
@@ -170,26 +170,26 @@ public class Geary.IntervalProgressMonitor : Geary.ProgressMonitor {
this.min_interval = min;
this.max_interval = max;
}
-
+
public override void notify_start() {
current = 0;
base.notify_start();
}
-
+
/**
- * Incrememts the progress
+ * Incrememts the progress
*/
public void increment(int count = 1) {
assert(is_in_progress);
assert(count + progress >= min_interval);
assert(count + progress <= max_interval);
-
+
current += count;
-
+
double new_progress = (1.0 * current - min_interval) / (1.0 * max_interval - min_interval);
double change = new_progress - progress;
progress = new_progress;
-
+
update(progress, change, this);
}
}
@@ -200,14 +200,14 @@ public class Geary.IntervalProgressMonitor : Geary.ProgressMonitor {
*/
public class Geary.AggregateProgressMonitor : Geary.ProgressMonitor {
private Gee.HashSet<Geary.ProgressMonitor> monitors = new Gee.HashSet<Geary.ProgressMonitor>();
-
+
/**
* Creates an aggregate progress monitor.
*/
public AggregateProgressMonitor() {
this.progress_type = Geary.ProgressType.AGGREGATED;
}
-
+
/**
* Adds a new progress monitor to this aggregate.
*/
@@ -241,45 +241,45 @@ public class Geary.AggregateProgressMonitor : Geary.ProgressMonitor {
break;
}
}
-
+
if (issue_signal)
notify_finish();
}
}
-
+
private void on_start() {
if (!is_in_progress)
notify_start();
}
-
+
private void on_update(double total_progress, double change, ProgressMonitor monitor) {
assert(is_in_progress);
-
+
double updated_progress = MIN;
foreach(Geary.ProgressMonitor pm in monitors)
updated_progress += pm.progress;
-
+
updated_progress /= monitors.size;
-
+
double aggregated_change = updated_progress - progress;
if (aggregated_change < 0)
aggregated_change = 0;
-
+
progress += updated_progress;
-
+
if (progress > MAX)
progress = MAX;
-
+
update(progress, aggregated_change, monitor);
}
-
+
private void on_finish() {
// Only signal completion once all progress monitors are complete.
foreach(Geary.ProgressMonitor pm in monitors) {
if (pm.is_in_progress)
return;
}
-
+
notify_finish();
}
}
diff --git a/src/engine/api/geary-revokable.vala b/src/engine/api/geary-revokable.vala
index ad35e46e..ed2c2953 100644
--- a/src/engine/api/geary-revokable.vala
+++ b/src/engine/api/geary-revokable.vala
@@ -16,7 +16,7 @@
public abstract class Geary.Revokable : BaseObject {
public const string PROP_VALID = "valid";
public const string PROP_IN_PROCESS = "in-process";
-
+
/**
* Indicates if {@link revoke_async} or {@link commit_async} are valid operations for this
* {@link Revokable}.
@@ -27,7 +27,7 @@ public abstract class Geary.Revokable : BaseObject {
* @see set_invalid
*/
public bool valid { get; private set; default = true; }
-
+
/**
* Indicates a {@link revoke_async} or {@link commit_async} operation is underway.
*
@@ -37,16 +37,16 @@ public abstract class Geary.Revokable : BaseObject {
* @see valid
*/
public bool in_process { get; protected set; default = false; }
-
+
private uint commit_timeout_id = 0;
-
+
/**
* Fired when the {@link Revokable} has been revoked.
*
* {@link valid} will stil be true when this is fired.
*/
public signal void revoked();
-
+
/**
* Fired when the {@link Revokable} has been committed.
*
@@ -55,7 +55,7 @@ public abstract class Geary.Revokable : BaseObject {
* {@link valid} will stil be true when this is fired.
*/
public signal void committed(Geary.Revokable? commit_revokable);
-
+
/**
* Create a {@link Revokable} with optional parameters.
*
@@ -65,11 +65,11 @@ public abstract class Geary.Revokable : BaseObject {
protected Revokable(int commit_timeout_sec = 0) {
if (commit_timeout_sec == 0)
return;
-
+
// This holds a reference to the Revokable, meaning cancelling the timeout in the dtor is
// largely symbolic, but so be it
commit_timeout_id = Timeout.add_seconds(commit_timeout_sec, on_timed_commit);
-
+
// various events that cancel the need for a timed commit; this is important to drop the
// ref to this object within the event loop
revoked.connect(cancel_timed_commit);
@@ -79,19 +79,19 @@ public abstract class Geary.Revokable : BaseObject {
cancel_timed_commit();
});
}
-
+
~Revokable() {
cancel_timed_commit();
}
-
+
protected virtual void notify_revoked() {
revoked();
}
-
+
protected virtual void notify_committed(Geary.Revokable? commit_revokable) {
committed(commit_revokable);
}
-
+
/**
* Mark the {@link Revokable} as invalid.
*
@@ -102,7 +102,7 @@ public abstract class Geary.Revokable : BaseObject {
protected void set_invalid() {
valid = false;
}
-
+
/**
* Revoke (undo) the operation.
*
@@ -115,10 +115,10 @@ public abstract class Geary.Revokable : BaseObject {
public virtual async void revoke_async(Cancellable? cancellable = null) throws Error {
if (in_process)
throw new EngineError.ALREADY_OPEN("Already revoking or committing operation");
-
+
if (!valid)
throw new EngineError.ALREADY_CLOSED("Revokable not valid");
-
+
in_process = true;
try {
yield internal_revoke_async(cancellable);
@@ -126,7 +126,7 @@ public abstract class Geary.Revokable : BaseObject {
in_process = false;
}
}
-
+
/**
* The child class's implementation of {@link revoke_async}.
*
@@ -138,7 +138,7 @@ public abstract class Geary.Revokable : BaseObject {
* if successful.
*/
protected abstract async void internal_revoke_async(Cancellable? cancellable) throws Error;
-
+
/**
* Commits (completes) the operation immediately.
*
@@ -155,10 +155,10 @@ public abstract class Geary.Revokable : BaseObject {
public virtual async void commit_async(Cancellable? cancellable = null) throws Error {
if (in_process)
throw new EngineError.ALREADY_OPEN("Already revoking or committing operation");
-
+
if (!valid)
throw new EngineError.ALREADY_CLOSED("Revokable not valid");
-
+
in_process = true;
try {
yield internal_commit_async(cancellable);
@@ -166,7 +166,7 @@ public abstract class Geary.Revokable : BaseObject {
in_process = false;
}
}
-
+
/**
* The child class's implementation of {@link commit_async}.
*
@@ -178,20 +178,20 @@ public abstract class Geary.Revokable : BaseObject {
* if successful.
*/
protected abstract async void internal_commit_async(Cancellable? cancellable) throws Error;
-
+
private bool on_timed_commit() {
commit_timeout_id = 0;
-
+
if (valid && !in_process)
commit_async.begin();
-
+
return false;
}
-
+
private void cancel_timed_commit() {
if (commit_timeout_id == 0)
return;
-
+
Source.remove(commit_timeout_id);
commit_timeout_id = 0;
}
diff --git a/src/engine/api/geary-search-folder.vala b/src/engine/api/geary-search-folder.vala
index 6dadce02..9a2d0cb8 100644
--- a/src/engine/api/geary-search-folder.vala
+++ b/src/engine/api/geary-search-folder.vala
@@ -22,37 +22,37 @@
public abstract class Geary.SearchFolder : Geary.AbstractLocalFolder {
private weak Account _account;
public override Account account { get { return _account; } }
-
+
private FolderProperties _properties;
public override FolderProperties properties { get { return _properties; } }
-
+
private FolderPath? _path = null;
public override FolderPath path { get { return _path; } }
-
+
public override SpecialFolderType special_folder_type {
get {
return Geary.SpecialFolderType.SEARCH;
}
}
-
+
public Geary.SearchQuery? search_query { get; protected set; default = null; }
-
+
/**
* Fired when the search query has changed. This signal is fired *after* the search
* has completed.
*/
public signal void search_query_changed(Geary.SearchQuery? query);
-
+
protected SearchFolder(Account account, FolderProperties properties, FolderPath path) {
_account = account;
_properties = properties;
_path = path;
}
-
+
protected virtual void notify_search_query_changed(SearchQuery? query) {
search_query_changed(query);
}
-
+
/**
* Sets the keyword string for this search.
*
@@ -63,14 +63,14 @@ public abstract class Geary.SearchFolder : Geary.AbstractLocalFolder {
* the {@link search_query} property to change before completion.
*/
public abstract void search(string query, SearchQuery.Strategy strategy, Cancellable? cancellable =
null);
-
+
/**
* Clears the search query and results.
*
* {@link search_query_changed} will be fired and {@link search_query} will be set to null.
*/
public abstract void clear();
-
+
/**
* Given a list of mail IDs, returns a set of casefolded words that match for the current
* search query.
diff --git a/src/engine/api/geary-search-query.vala b/src/engine/api/geary-search-query.vala
index a4d4456b..17df026a 100644
--- a/src/engine/api/geary-search-query.vala
+++ b/src/engine/api/geary-search-query.vala
@@ -49,17 +49,17 @@ public abstract class Geary.SearchQuery : BaseObject {
*/
HORIZON
}
-
+
/**
* The original user search text.
*/
public string raw { get; private set; }
-
+
/**
* The selected {@link Strategy} quality.
*/
public Strategy strategy { get; private set; }
-
+
protected SearchQuery(string raw, Strategy strategy) {
this.raw = raw;
this.strategy = strategy;
diff --git a/src/engine/api/geary-special-folder-type.vala b/src/engine/api/geary-special-folder-type.vala
index c4d09f53..9d1b9865 100644
--- a/src/engine/api/geary-special-folder-type.vala
+++ b/src/engine/api/geary-special-folder-type.vala
@@ -17,48 +17,48 @@ public enum Geary.SpecialFolderType {
TRASH,
OUTBOX,
ARCHIVE;
-
+
public unowned string get_display_name() {
switch (this) {
case INBOX:
return _("Inbox");
-
+
case DRAFTS:
return _("Drafts");
-
+
case SENT:
return _("Sent Mail");
-
+
case FLAGGED:
return _("Starred");
-
+
case IMPORTANT:
return _("Important");
-
+
case ALL_MAIL:
return _("All Mail");
-
+
case SPAM:
return _("Spam");
-
+
case TRASH:
return _("Trash");
-
+
case OUTBOX:
return _("Outbox");
-
+
case SEARCH:
return _("Search");
-
+
case ARCHIVE:
return _("Archive");
-
+
case NONE:
default:
return _("None");
}
}
-
+
public bool is_outgoing() {
return this == SENT || this == OUTBOX;
}
diff --git a/src/engine/app/app-draft-manager.vala b/src/engine/app/app-draft-manager.vala
index 4c80e22a..620453ba 100644
--- a/src/engine/app/app-draft-manager.vala
+++ b/src/engine/app/app-draft-manager.vala
@@ -32,7 +32,7 @@ public class Geary.App.DraftManager : BaseObject {
public const string PROP_VERSIONS_SAVED = "versions-saved";
public const string PROP_VERSIONS_DROPPED = "versions-dropped";
public const string PROP_DISCARD_ON_CLOSE = "discard-on-close";
-
+
/**
* Current saved state of the draft.
*/
@@ -56,19 +56,19 @@ public class Geary.App.DraftManager : BaseObject {
*/
ERROR
}
-
+
private enum OperationType {
PUSH,
CLOSE
}
-
+
private class Operation : BaseObject {
public OperationType op_type;
public RFC822.Message? draft;
public EmailFlags? flags;
public DateTime? date_received;
public Nonblocking.Semaphore? semaphore;
-
+
public Operation(OperationType op_type, RFC822.Message? draft, EmailFlags? flags,
DateTime? date_received, Nonblocking.Semaphore? semaphore) {
this.op_type = op_type;
@@ -78,7 +78,7 @@ public class Geary.App.DraftManager : BaseObject {
this.semaphore = semaphore;
}
}
-
+
/**
* Indicates the {@link DraftManager} is open and ready for service.
*
@@ -86,17 +86,17 @@ public class Geary.App.DraftManager : BaseObject {
* {@link open_async} completes, not when this property changes to true.
*/
public bool is_open { get; private set; default = false; }
-
+
/**
* The current saved state of the draft.
*/
public DraftState draft_state { get; private set; default = DraftState.NOT_STORED; }
-
+
/**
* The {@link Geary.EmailIdentifier} of the last saved draft.
*/
public Geary.EmailIdentifier? current_draft_id { get; private set; default = null; }
-
+
/**
* The version number of the most recently saved draft.
*
@@ -106,21 +106,21 @@ public class Geary.App.DraftManager : BaseObject {
* A {@link discard} operation will reset this counter to zero.
*/
public int versions_saved { get; private set; default = 0; }
-
+
/**
* The number of drafts dropped as new ones are added to the queue.
*
* @see dropped
*/
public int versions_dropped { get; private set; default = 0; }
-
+
/**
* When set, the draft will be discarded when {@link close_async} is called.
*
* In addition, when set all future {@link update}s will result in the draft being dropped.
*/
public bool discard_on_close { get; set; default = false; }
-
+
private Account account;
private Folder? drafts_folder = null;
private FolderSupport.Create? create_support = null;
@@ -129,17 +129,17 @@ public class Geary.App.DraftManager : BaseObject {
new Nonblocking.Queue<Operation?>.fifo();
private bool was_opened = false;
private Error? fatal_err = null;
-
+
/**
* Fired when a draft is successfully saved.
*/
public signal void stored(Geary.RFC822.Message draft);
-
+
/**
* Fired when a draft is discarded.
*/
public signal void discarded();
-
+
/**
* Fired when a draft is dropped.
*
@@ -147,7 +147,7 @@ public class Geary.App.DraftManager : BaseObject {
* to be pushed to the server. The queued draft is dropped in favor of the new one.
*/
public signal void dropped(Geary.RFC822.Message draft);
-
+
/**
* Fired when unable to save a draft but the {@link DraftManager} remains open.
*
@@ -158,7 +158,7 @@ public class Geary.App.DraftManager : BaseObject {
public virtual signal void draft_failed(Geary.RFC822.Message draft, Error err) {
debug("%s: Unable to create draft: %s", to_string(), err.message);
}
-
+
/**
* Fired if an unrecoverable error occurs while processing drafts.
*
@@ -166,24 +166,24 @@ public class Geary.App.DraftManager : BaseObject {
*/
public virtual signal void fatal(Error err) {
fatal_err = err;
-
+
debug("%s: Irrecoverable failure: %s", to_string(), err.message);
}
-
+
public DraftManager(Geary.Account account) {
this.account = account;
}
-
+
protected virtual void notify_stored(Geary.RFC822.Message draft) {
versions_saved++;
stored(draft);
}
-
+
protected virtual void notify_discarded() {
versions_saved = 0;
discarded();
}
-
+
/**
* Open the {@link DraftManager} and prepare it for handling composed messages.
*
@@ -205,17 +205,17 @@ public class Geary.App.DraftManager : BaseObject {
throw new EngineError.ALREADY_OPEN("%s is already open", to_string());
else if (was_opened)
throw new EngineError.UNSUPPORTED("%s cannot be re-opened", to_string());
-
+
was_opened = true;
-
+
current_draft_id = initial_draft_id;
if (current_draft_id != null)
draft_state = DraftState.STORED;
-
+
drafts_folder = account.get_special_folder(SpecialFolderType.DRAFTS);
if (drafts_folder == null)
throw new EngineError.NOT_FOUND("%s: No drafts folder found", to_string());
-
+
// if drafts folder doesn't support create and remove, call it quits
create_support = drafts_folder as Geary.FolderSupport.Create;
remove_support = drafts_folder as Geary.FolderSupport.Remove;
@@ -223,7 +223,7 @@ public class Geary.App.DraftManager : BaseObject {
throw new EngineError.UNSUPPORTED("%s: Drafts folder %s does not support create and remove",
to_string(), drafts_folder.to_string());
}
-
+
drafts_folder.closed.connect(on_folder_closed);
yield drafts_folder.open_async(Folder.OpenFlags.NO_DELAY, cancellable);
@@ -236,25 +236,25 @@ public class Geary.App.DraftManager : BaseObject {
} catch (Error err) {
// ignore
}
-
+
throw new EngineError.UNSUPPORTED("%s: Drafts folder %s does not return created mail ID",
to_string(), drafts_folder.to_string());
}
-
+
// start the operation message loop, which ensures commands are handled in orderly fashion
operation_loop_async.begin();
-
+
// done
is_open = true;
}
-
+
private void on_folder_closed(Folder.CloseReason reason) {
if (reason == Folder.CloseReason.FOLDER_CLOSED) {
fatal(new EngineError.SERVER_UNAVAILABLE("%s: Unexpected drafts folder closed (%s)",
to_string(), reason.to_string()));
}
}
-
+
/**
* Flush pending operations and close the {@link DraftManager}.
*
@@ -266,10 +266,10 @@ public class Geary.App.DraftManager : BaseObject {
public async void close_async(Cancellable? cancellable = null) throws Error {
if (!is_open || drafts_folder == null)
return;
-
+
// prevent further operations
is_open = false;
-
+
// don't flush a CLOSE down the pipe if failed, the operation loop is closed for business
if (fatal_err == null) {
// if discarding on close, do so now
@@ -278,25 +278,25 @@ public class Geary.App.DraftManager : BaseObject {
// which doesn't
submit_push(null, null, null);
}
-
+
// flush pending I/O
Nonblocking.Semaphore semaphore = new Nonblocking.Semaphore(cancellable);
mailbox.send(new Operation(OperationType.CLOSE, null, null, null, semaphore));
-
+
// wait for close to complete
try {
yield semaphore.wait_async(cancellable);
} catch (Error err) {
if (err is IOError.CANCELLED)
throw err;
-
+
// fall through
}
}
-
+
// Disconnect before closing, as signal handler is for unexpected closes
drafts_folder.closed.disconnect(on_folder_closed);
-
+
try {
yield drafts_folder.close_async(cancellable);
} finally {
@@ -305,12 +305,12 @@ public class Geary.App.DraftManager : BaseObject {
remove_support = null;
}
}
-
+
private void check_open() throws EngineError {
if (!is_open)
throw new EngineError.OPEN_REQUIRED("%s is not open", to_string());
}
-
+
/**
* Save draft on the server, potentially replacing (deleting) an already-existing version.
*
@@ -323,10 +323,10 @@ public class Geary.App.DraftManager : BaseObject {
public Geary.Nonblocking.Semaphore? update(Geary.RFC822.Message draft, Geary.EmailFlags? flags,
DateTime? date_received) throws Error {
check_open();
-
+
return submit_push(draft, flags, date_received);
}
-
+
/**
* Delete all versions of the composed email from the server.
*
@@ -341,10 +341,10 @@ public class Geary.App.DraftManager : BaseObject {
*/
public Geary.Nonblocking.Semaphore? discard() throws Error {
check_open();
-
+
return submit_push(null, null, null);
}
-
+
// Note that this call doesn't check_open(), important when used within close_async()
private Nonblocking.Semaphore? submit_push(RFC822.Message? draft, EmailFlags? flags,
DateTime? date_received) {
@@ -352,10 +352,10 @@ public class Geary.App.DraftManager : BaseObject {
if (draft != null && discard_on_close) {
versions_dropped++;
dropped(draft);
-
+
return null;
}
-
+
// clear out pending pushes (which can be updates or discards)
mailbox.revoke_matching((op) => {
// count and notify of dropped drafts
@@ -363,18 +363,18 @@ public class Geary.App.DraftManager : BaseObject {
versions_dropped++;
dropped(op.draft);
}
-
+
return op.op_type == OperationType.PUSH;
});
-
+
Nonblocking.Semaphore semaphore = new Nonblocking.Semaphore();
-
+
// schedule this draft for update (if null, it's a discard)
mailbox.send(new Operation(OperationType.PUSH, draft, flags, date_received, semaphore));
-
+
return semaphore;
}
-
+
private async void operation_loop_async() {
for (;;) {
// if a fatal error occurred (it can happen outside the loop), shutdown without
@@ -391,34 +391,34 @@ public class Geary.App.DraftManager : BaseObject {
}
bool continue_loop = yield operation_loop_iteration_async(op);
-
+
// fire semaphore, if present
if (op.semaphore != null)
op.semaphore.blind_notify();
-
+
if (!continue_loop)
break;
}
}
-
+
// Returns false if time to exit.
private async bool operation_loop_iteration_async(Operation op) {
// watch for CLOSE
if (op.op_type == OperationType.CLOSE)
return false;
-
+
// make sure there's a folder to work with
if (drafts_folder == null || drafts_folder.get_open_state() == Folder.OpenState.CLOSED) {
fatal(new EngineError.SERVER_UNAVAILABLE("%s: premature drafts folder close", to_string()));
-
+
return false;
}
-
+
// at this point, only operation left is PUSH
assert(op.op_type == OperationType.PUSH);
-
+
draft_state = DraftState.STORING;
-
+
// delete old draft for all PUSHes: best effort ... since create_email_async() will handle
// replacement in a transactional-kinda-way, only outright delete if not using create
if (current_draft_id != null && op.draft == null) {
@@ -431,35 +431,35 @@ public class Geary.App.DraftManager : BaseObject {
debug("%s: Unable to remove existing draft %s: %s", to_string(),
current_draft_id.to_string(),
err.message);
}
-
+
// always clear draft id (assuming that retrying a failed remove is unnecessary), but
// only signal the discard if it actually was removed
current_draft_id = null;
if (success)
notify_discarded();
}
-
+
// if draft supplied, save it
if (op.draft != null) {
try {
current_draft_id = yield create_support.create_email_async(op.draft, op.flags,
op.date_received, current_draft_id, null);
-
+
draft_state = DraftState.STORED;
notify_stored(op.draft);
} catch (Error err) {
draft_state = DraftState.ERROR;
-
+
// notify subscribers
draft_failed(op.draft, err);
}
} else {
draft_state = DraftState.NOT_STORED;
}
-
+
return true;
}
-
+
public string to_string() {
return "%s DraftManager".printf(account.to_string());
}
diff --git a/src/engine/app/app-email-store.vala b/src/engine/app/app-email-store.vala
index 7fe1af52..a346409b 100644
--- a/src/engine/app/app-email-store.vala
+++ b/src/engine/app/app-email-store.vala
@@ -6,11 +6,11 @@
public class Geary.App.EmailStore : BaseObject {
public weak Geary.Account account { get; private set; }
-
+
public EmailStore(Geary.Account account) {
this.account = account;
}
-
+
/**
* Return a map of EmailIdentifiers to the special Geary.FolderSupport
* interfaces each one supports. For example, if an EmailIdentifier comes
@@ -24,7 +24,7 @@ public class Geary.App.EmailStore : BaseObject {
= yield account.get_containing_folders_async(emails, cancellable);
if (folders == null)
return null;
-
+
Gee.HashSet<Type> all_support = new Gee.HashSet<Type>();
all_support.add(typeof(Geary.FolderSupport.Archive));
all_support.add(typeof(Geary.FolderSupport.Copy));
@@ -32,12 +32,12 @@ public class Geary.App.EmailStore : BaseObject {
all_support.add(typeof(Geary.FolderSupport.Mark));
all_support.add(typeof(Geary.FolderSupport.Move));
all_support.add(typeof(Geary.FolderSupport.Remove));
-
+
Gee.HashMultiMap<Geary.EmailIdentifier, Type> map
= new Gee.HashMultiMap<Geary.EmailIdentifier, Type>();
foreach (Geary.EmailIdentifier email in folders.get_keys()) {
Gee.HashSet<Type> support = new Gee.HashSet<Type>();
-
+
foreach (Geary.FolderPath path in folders.get(email)) {
Geary.Folder folder;
try {
@@ -46,7 +46,7 @@ public class Geary.App.EmailStore : BaseObject {
debug("Error getting a folder from path %s: %s", path.to_string(), e.message);
continue;
}
-
+
foreach (Type type in all_support) {
if (folder.get_type().is_a(type))
support.add(type);
@@ -54,13 +54,13 @@ public class Geary.App.EmailStore : BaseObject {
if (support.contains_all(all_support))
break;
}
-
+
Geary.Collection.multi_map_set_all<Geary.EmailIdentifier, Type>(map, email, support);
}
-
+
return (map.size > 0 ? map : null);
}
-
+
/**
* Lists any set of EmailIdentifiers as if they were all in one folder.
*/
@@ -71,7 +71,7 @@ public class Geary.App.EmailStore : BaseObject {
yield do_folder_operation_async(op, emails, cancellable);
return (op.results.size > 0 ? op.results : null);
}
-
+
/**
* Fetches any EmailIdentifier regardless of what folder it's in.
*/
@@ -81,12 +81,12 @@ public class Geary.App.EmailStore : BaseObject {
FetchOperation op = new Geary.App.FetchOperation(required_fields, flags);
yield do_folder_operation_async(op,
Geary.iterate<Geary.EmailIdentifier>(email_id).to_array_list(), cancellable);
-
+
if (op.result == null)
throw new EngineError.NOT_FOUND("Couldn't fetch email ID %s", email_id.to_string());
return op.result;
}
-
+
/**
* Marks any set of EmailIdentifiers as if they were all in one
* Geary.FolderSupport.Mark folder.
@@ -97,7 +97,7 @@ public class Geary.App.EmailStore : BaseObject {
yield do_folder_operation_async(new Geary.App.MarkOperation(flags_to_add, flags_to_remove),
emails, cancellable);
}
-
+
/**
* Copies any set of EmailIdentifiers as if they were all in one
* Geary.FolderSupport.Copy folder.
@@ -107,7 +107,7 @@ public class Geary.App.EmailStore : BaseObject {
yield do_folder_operation_async(new Geary.App.CopyOperation(destination),
emails, cancellable);
}
-
+
private async Gee.HashMap<Geary.FolderPath, Geary.Folder> get_folder_instances_async(
Gee.Collection<Geary.FolderPath> paths, Cancellable? cancellable) throws Error {
Gee.HashMap<Geary.FolderPath, Geary.Folder> folders
@@ -118,7 +118,7 @@ public class Geary.App.EmailStore : BaseObject {
}
return folders;
}
-
+
private Geary.FolderPath? next_folder_for_operation(AsyncFolderOperation operation,
Gee.MultiMap<Geary.FolderPath, Geary.EmailIdentifier> folders_to_ids,
Gee.Map<Geary.FolderPath, Geary.Folder> folders) throws Error {
@@ -129,11 +129,11 @@ public class Geary.App.EmailStore : BaseObject {
assert(folders.has_key(path));
if (!folders.get(path).get_type().is_a(operation.folder_type))
continue;
-
+
int count = folders_to_ids.get(path).size;
if (count == 0)
continue;
-
+
if (folders.get(path).get_open_state() == Geary.Folder.OpenState.REMOTE) {
if (!best_is_open) {
best_is_open = true;
@@ -142,34 +142,34 @@ public class Geary.App.EmailStore : BaseObject {
} else if (best_is_open) {
continue;
}
-
+
if (count > best_count) {
best_count = count;
best = path;
}
}
-
+
return best;
}
-
+
private async void do_folder_operation_async(AsyncFolderOperation operation,
Gee.Collection<Geary.EmailIdentifier> emails, Cancellable? cancellable) throws Error {
if (emails.size == 0)
return;
-
+
debug("EmailStore %s running %s on %d emails", account.to_string(),
operation.get_type().name(), emails.size);
-
+
Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath>? ids_to_folders
= yield account.get_containing_folders_async(emails, cancellable);
if (ids_to_folders == null)
return;
-
+
Gee.MultiMap<Geary.FolderPath, Geary.EmailIdentifier> folders_to_ids
= Geary.Collection.reverse_multi_map<Geary.EmailIdentifier, Geary.FolderPath>(ids_to_folders);
Gee.HashMap<Geary.FolderPath, Geary.Folder> folders
= yield get_folder_instances_async(folders_to_ids.get_keys(), cancellable);
-
+
Geary.FolderPath? path;
while ((path = next_folder_for_operation(operation, folders_to_ids, folders)) != null) {
Geary.Folder folder = folders.get(path);
diff --git a/src/engine/app/conversation-monitor/app-conversation-operation-queue.vala
b/src/engine/app/conversation-monitor/app-conversation-operation-queue.vala
index 7d4cb8d5..3bc41d81 100644
--- a/src/engine/app/conversation-monitor/app-conversation-operation-queue.vala
+++ b/src/engine/app/conversation-monitor/app-conversation-operation-queue.vala
@@ -56,7 +56,7 @@ private class Geary.App.ConversationOperationQueue : BaseObject {
public async void run_process_async() {
is_processing = true;
-
+
for (;;) {
ConversationOperation op;
try {
@@ -67,7 +67,7 @@ private class Geary.App.ConversationOperationQueue : BaseObject {
}
if (op is TerminateOperation)
break;
-
+
if (!progress_monitor.is_in_progress)
progress_monitor.notify_start();
@@ -80,7 +80,7 @@ private class Geary.App.ConversationOperationQueue : BaseObject {
if (mailbox.size == 0)
progress_monitor.notify_finish();
}
-
+
is_processing = false;
processing_done_spinlock.blind_notify();
}
diff --git a/src/engine/app/conversation-monitor/app-local-search-operation.vala
b/src/engine/app/conversation-monitor/app-local-search-operation.vala
index 711e927c..7af514a7 100644
--- a/src/engine/app/conversation-monitor/app-local-search-operation.vala
+++ b/src/engine/app/conversation-monitor/app-local-search-operation.vala
@@ -11,10 +11,10 @@ private class Geary.App.LocalSearchOperation : Geary.Nonblocking.BatchOperation
public Geary.Email.Field required_fields;
public Gee.Collection<Geary.FolderPath>? blacklist;
public Geary.EmailFlags? flag_blacklist;
-
+
// OUT
public Gee.MultiMap<Geary.Email, Geary.FolderPath?>? emails = null;
-
+
public LocalSearchOperation(Geary.Account account, RFC822.MessageID message_id,
Geary.Email.Field required_fields, Gee.Collection<Geary.FolderPath?> blacklist,
Geary.EmailFlags? flag_blacklist) {
@@ -24,11 +24,11 @@ private class Geary.App.LocalSearchOperation : Geary.Nonblocking.BatchOperation
this.blacklist = blacklist;
this.flag_blacklist = flag_blacklist;
}
-
+
public override async Object? execute_async(Cancellable? cancellable) throws Error {
emails = yield account.local_search_message_id_async(message_id, required_fields,
false, blacklist, flag_blacklist);
-
+
return null;
}
}
diff --git a/src/engine/app/conversation-monitor/app-terminate-operation.vala
b/src/engine/app/conversation-monitor/app-terminate-operation.vala
index dd60dd51..6911f964 100644
--- a/src/engine/app/conversation-monitor/app-terminate-operation.vala
+++ b/src/engine/app/conversation-monitor/app-terminate-operation.vala
@@ -8,7 +8,7 @@ private class Geary.App.TerminateOperation : ConversationOperation {
public TerminateOperation() {
base(null);
}
-
+
public override async void execute_async() {
}
}
diff --git a/src/engine/app/email-store/app-async-folder-operation.vala
b/src/engine/app/email-store/app-async-folder-operation.vala
index 22106362..170830d2 100644
--- a/src/engine/app/email-store/app-async-folder-operation.vala
+++ b/src/engine/app/email-store/app-async-folder-operation.vala
@@ -6,7 +6,7 @@
private abstract class Geary.App.AsyncFolderOperation : BaseObject {
public abstract Type folder_type { get; }
-
+
public abstract async Gee.Collection<Geary.EmailIdentifier> execute_async(
Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids,
Cancellable? cancellable) throws Error;
diff --git a/src/engine/app/email-store/app-copy-operation.vala
b/src/engine/app/email-store/app-copy-operation.vala
index 0e368806..44c76fd7 100644
--- a/src/engine/app/email-store/app-copy-operation.vala
+++ b/src/engine/app/email-store/app-copy-operation.vala
@@ -6,19 +6,19 @@
private class Geary.App.CopyOperation : Geary.App.AsyncFolderOperation {
public override Type folder_type { get { return typeof(Geary.FolderSupport.Copy); } }
-
+
public Geary.FolderPath destination;
-
+
public CopyOperation(Geary.FolderPath destination) {
this.destination = destination;
}
-
+
public override async Gee.Collection<Geary.EmailIdentifier> execute_async(
Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids,
Cancellable? cancellable) throws Error {
Geary.FolderSupport.Copy? copy = folder as Geary.FolderSupport.Copy;
assert(copy != null);
-
+
Gee.List<Geary.EmailIdentifier> list
= Geary.Collection.to_array_list<Geary.EmailIdentifier>(ids);
yield copy.copy_email_async(list, destination, cancellable);
diff --git a/src/engine/app/email-store/app-fetch-operation.vala
b/src/engine/app/email-store/app-fetch-operation.vala
index 6525493c..eef93d10 100644
--- a/src/engine/app/email-store/app-fetch-operation.vala
+++ b/src/engine/app/email-store/app-fetch-operation.vala
@@ -6,23 +6,23 @@
private class Geary.App.FetchOperation : Geary.App.AsyncFolderOperation {
public override Type folder_type { get { return typeof(Geary.Folder); } }
-
+
public Geary.Email? result = null;
public Geary.Email.Field required_fields;
public Geary.Folder.ListFlags flags;
-
+
public FetchOperation(Geary.Email.Field required_fields, Geary.Folder.ListFlags flags) {
this.required_fields = required_fields;
this.flags = flags;
}
-
+
public override async Gee.Collection<Geary.EmailIdentifier> execute_async(
Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids,
Cancellable? cancellable) throws Error {
assert(result == null);
Geary.EmailIdentifier? id = Geary.Collection.get_first(ids);
assert(id != null);
-
+
result = yield folder.fetch_email_async(
id, required_fields, flags, cancellable);
return Geary.iterate<Geary.EmailIdentifier>(id).to_array_list();
diff --git a/src/engine/app/email-store/app-list-operation.vala
b/src/engine/app/email-store/app-list-operation.vala
index d7761522..08afd640 100644
--- a/src/engine/app/email-store/app-list-operation.vala
+++ b/src/engine/app/email-store/app-list-operation.vala
@@ -6,17 +6,17 @@
private class Geary.App.ListOperation : Geary.App.AsyncFolderOperation {
public override Type folder_type { get { return typeof(Geary.Folder); } }
-
+
public Gee.HashSet<Geary.Email> results;
public Geary.Email.Field required_fields;
public Geary.Folder.ListFlags flags;
-
+
public ListOperation(Geary.Email.Field required_fields, Geary.Folder.ListFlags flags) {
results = new Gee.HashSet<Geary.Email>();
this.required_fields = required_fields;
this.flags = flags;
}
-
+
public override async Gee.Collection<Geary.EmailIdentifier> execute_async(
Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids,
Cancellable? cancellable) throws Error {
diff --git a/src/engine/app/email-store/app-mark-operation.vala
b/src/engine/app/email-store/app-mark-operation.vala
index 819e237c..f69b9049 100644
--- a/src/engine/app/email-store/app-mark-operation.vala
+++ b/src/engine/app/email-store/app-mark-operation.vala
@@ -6,21 +6,21 @@
private class Geary.App.MarkOperation : Geary.App.AsyncFolderOperation {
public override Type folder_type { get { return typeof(Geary.FolderSupport.Mark); } }
-
+
public Geary.EmailFlags? flags_to_add;
public Geary.EmailFlags? flags_to_remove;
-
+
public MarkOperation(Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove) {
this.flags_to_add = flags_to_add;
this.flags_to_remove = flags_to_remove;
}
-
+
public override async Gee.Collection<Geary.EmailIdentifier> execute_async(
Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids,
Cancellable? cancellable) throws Error {
Geary.FolderSupport.Mark? mark = folder as Geary.FolderSupport.Mark;
assert(mark != null);
-
+
Gee.List<Geary.EmailIdentifier> list
= Geary.Collection.to_array_list<Geary.EmailIdentifier>(ids);
yield mark.mark_email_async(list, flags_to_add, flags_to_remove, cancellable);
diff --git a/src/engine/common/common-message-data.vala b/src/engine/common/common-message-data.vala
index cafc68b0..e4b3438d 100644
--- a/src/engine/common/common-message-data.vala
+++ b/src/engine/common/common-message-data.vala
@@ -33,30 +33,30 @@ public interface Geary.MessageData.SearchableMessageData {
public abstract class Geary.MessageData.StringMessageData : AbstractMessageData,
Gee.Hashable<StringMessageData> {
public string value { get; private set; }
-
+
private uint stored_hash = uint.MAX;
-
+
public StringMessageData(string value) {
this.value = value;
}
-
+
/**
* Default definition of equals is case-sensitive comparison.
*/
public virtual bool equal_to(StringMessageData other) {
if (this == other)
return true;
-
+
if (hash() != other.hash())
return false;
-
+
return (value == other.value);
}
-
+
public virtual uint hash() {
return (stored_hash != uint.MAX) ? stored_hash : (stored_hash = str_hash(value));
}
-
+
public override string to_string() {
return value;
}
@@ -65,19 +65,19 @@ public abstract class Geary.MessageData.StringMessageData : AbstractMessageData,
public abstract class Geary.MessageData.IntMessageData : AbstractMessageData,
Gee.Hashable<IntMessageData> {
public int value { get; private set; }
-
+
public IntMessageData(int value) {
this.value = value;
}
-
+
public virtual bool equal_to(IntMessageData other) {
return (value == other.value);
}
-
+
public virtual uint hash() {
return value;
}
-
+
public override string to_string() {
return value.to_string();
}
@@ -86,24 +86,24 @@ public abstract class Geary.MessageData.IntMessageData : AbstractMessageData,
public abstract class Geary.MessageData.Int64MessageData : AbstractMessageData,
Gee.Hashable<Int64MessageData> {
public int64 value { get; private set; }
-
+
private uint stored_hash = uint.MAX;
-
+
public Int64MessageData(int64 value) {
this.value = value;
}
-
+
public virtual bool equal_to(Int64MessageData other) {
if (this == other)
return true;
-
+
return (value == other.value);
}
-
+
public virtual uint hash() {
return (stored_hash != uint.MAX) ? stored_hash : (stored_hash = int64_hash(value));
}
-
+
public override string to_string() {
return value.to_string();
}
@@ -112,12 +112,12 @@ public abstract class Geary.MessageData.Int64MessageData : AbstractMessageData,
public abstract class Geary.MessageData.BlockMessageData : AbstractMessageData {
public string data_name { get; private set; }
public Geary.Memory.Buffer buffer { get; private set; }
-
+
public BlockMessageData(string data_name, Geary.Memory.Buffer buffer) {
this.data_name = data_name;
this.buffer = buffer;
}
-
+
public override string to_string() {
return "%s (%lub)".printf(data_name, buffer.size);
}
diff --git a/src/engine/db/db-connection.vala b/src/engine/db/db-connection.vala
index ff44d4c6..8949a80d 100644
--- a/src/engine/db/db-connection.vala
+++ b/src/engine/db/db-connection.vala
@@ -23,13 +23,13 @@ public class Geary.Db.Connection : Geary.Db.Context {
* Default value is for *no* timeout, that is, the Sqlite will not retry BUSY results.
*/
public const int DEFAULT_BUSY_TIMEOUT_MSEC = 0;
-
+
/**
* This value gives a generous amount of time for SQLite to finish a big write operation and
* relinquish the lock to other waiting transactions.
*/
public const int RECOMMENDED_BUSY_TIMEOUT_MSEC = 60 * 1000;
-
+
private const string PRAGMA_FOREIGN_KEYS = "foreign_keys";
private const string PRAGMA_RECURSIVE_TRIGGERS = "recursive_triggers";
private const string PRAGMA_USER_VERSION = "user_version";
@@ -39,45 +39,45 @@ public class Geary.Db.Connection : Geary.Db.Context {
private const string PRAGMA_FREELIST_COUNT = "freelist_count";
private const string PRAGMA_PAGE_COUNT = "page_count";
private const string PRAGMA_PAGE_SIZE = "page_size";
-
+
// this is used for logging purposes only; connection numbers mean nothing to SQLite
private static int next_cx_number = 0;
-
+
/**
* See [[http://www.sqlite.org/c3ref/last_insert_rowid.html]]
*/
public int64 last_insert_rowid { get {
return db.last_insert_rowid();
} }
-
+
/**
* See [[http://www.sqlite.org/c3ref/changes.html]]
*/
public int last_modified_rows { get {
return db.changes();
} }
-
+
/**
* See [[http://www.sqlite.org/c3ref/total_changes.html]]
*/
public int total_modified_rows { get {
return db.total_changes();
} }
-
+
public weak Database database { get; private set; }
-
+
internal Sqlite.Database db;
-
+
private int cx_number;
private int busy_timeout_msec = DEFAULT_BUSY_TIMEOUT_MSEC;
-
+
internal Connection(Database database, int sqlite_flags, Cancellable? cancellable) throws Error {
this.database = database;
-
+
lock (next_cx_number) {
cx_number = next_cx_number++;
}
-
+
check_cancelled("Connection.ctor", cancellable);
try {
@@ -93,7 +93,7 @@ public class Geary.Db.Connection : Geary.Db.Context {
throw derr;
}
}
-
+
/**
* Execute a plain text SQL statement. More than one SQL statement may be in the string. See
* [[http://www.sqlite.org/lang.html]] for more information on SQLite's SQL syntax.
@@ -107,13 +107,13 @@ public class Geary.Db.Connection : Geary.Db.Context {
*/
public void exec(string sql, Cancellable? cancellable = null) throws Error {
check_cancelled("Connection.exec", cancellable);
-
+
throw_on_error("Connection.exec", db.exec(sql), sql);
-
+
// Don't use Context.log(), which is designed for logging Results and Statements
Logging.debug(Logging.Flag.SQL, "exec:\n\t%s", sql);
}
-
+
/**
* Loads a text file of SQL commands into memory and executes them at once with exec().
*
@@ -124,13 +124,13 @@ public class Geary.Db.Connection : Geary.Db.Context {
*/
public void exec_file(File file, Cancellable? cancellable = null) throws Error {
check_cancelled("Connection.exec_file", cancellable);
-
+
string sql;
FileUtils.get_contents(file.get_path(), out sql);
-
+
exec(sql, cancellable);
}
-
+
/**
* Executes a plain text SQL statement and returns a Result object directly.
* This call creates an intermediate Statement object which may be fetched from Result.statement.
@@ -138,7 +138,7 @@ public class Geary.Db.Connection : Geary.Db.Context {
public Result query(string sql, Cancellable? cancellable = null) throws Error {
return (new Statement(this, sql)).exec(cancellable);
}
-
+
/**
* Prepares a Statement which may have values bound to it and executed. See
* [[http://www.sqlite.org/c3ref/prepare.html]]
@@ -146,7 +146,7 @@ public class Geary.Db.Connection : Geary.Db.Context {
public Statement prepare(string sql) throws DatabaseError {
return new Statement(this, sql);
}
-
+
/**
* See set_busy_timeout_msec().
*/
@@ -170,11 +170,11 @@ public class Geary.Db.Connection : Geary.Db.Context {
public void set_busy_timeout_msec(int busy_timeout_msec) throws Error {
if (this.busy_timeout_msec == busy_timeout_msec)
return;
-
+
throw_on_error("Database.set_busy_timeout", db.busy_timeout(busy_timeout_msec));
this.busy_timeout_msec = busy_timeout_msec;
}
-
+
/**
* Returns the result of a PRAGMA as a boolean. See [[http://www.sqlite.org/pragma.html]]
*
@@ -189,28 +189,28 @@ public class Geary.Db.Connection : Geary.Db.Context {
case "true":
case "on":
return true;
-
+
case "0":
case "no":
case "false":
case "off":
return false;
-
+
default:
debug("Db.Connection.get_pragma_bool: unknown PRAGMA boolean response \"%s\"",
response);
-
+
return false;
}
}
-
+
/**
* Sets a boolean PRAGMA value to either "true" or "false".
*/
public void set_pragma_bool(string name, bool b) throws Error {
exec("PRAGMA %s=%s".printf(name, b ? "true" : "false"));
}
-
+
/**
* Returns the result of a PRAGMA as an integer. See [[http://www.sqlite.org/pragma.html]]
*
@@ -221,14 +221,14 @@ public class Geary.Db.Connection : Geary.Db.Context {
public int get_pragma_int(string name) throws Error {
return query("PRAGMA %s".printf(name)).int_at(0);
}
-
+
/**
* Sets an integer PRAGMA value.
*/
public void set_pragma_int(string name, int d) throws Error {
exec("PRAGMA %s=%d".printf(name, d));
}
-
+
/**
* Returns the result of a PRAGMA as a 64-bit integer. See [[http://www.sqlite.org/pragma.html]]
*
@@ -239,28 +239,28 @@ public class Geary.Db.Connection : Geary.Db.Context {
public int64 get_pragma_int64(string name) throws Error {
return query("PRAGMA %s".printf(name)).int64_at(0);
}
-
+
/**
* Sets a 64-bit integer PRAGMA value.
*/
public void set_pragma_int64(string name, int64 ld) throws Error {
exec("PRAGMA %s=%s".printf(name, ld.to_string()));
}
-
+
/**
* Returns the result of a PRAGMA as a string. See [[http://www.sqlite.org/pragma.html]]
*/
public string get_pragma_string(string name) throws Error {
return query("PRAGMA %s".printf(name)).nonnull_string_at(0);
}
-
+
/**
* Sets a string PRAGMA value.
*/
public void set_pragma_string(string name, string str) throws Error {
exec("PRAGMA %s=%s".printf(name, str));
}
-
+
/**
* Returns the user_version number maintained by SQLite.
*
@@ -271,7 +271,7 @@ public class Geary.Db.Connection : Geary.Db.Context {
public int get_user_version_number() throws Error {
return get_pragma_int(PRAGMA_USER_VERSION);
}
-
+
/**
* Sets the user version number, which is a private number maintained by the user.
* VersionedDatabase uses this to maintain the version number of the database.
@@ -281,7 +281,7 @@ public class Geary.Db.Connection : Geary.Db.Context {
public void set_user_version_number(int version) throws Error {
set_pragma_int(PRAGMA_USER_VERSION, version);
}
-
+
/**
* Gets the schema version number, which is maintained by SQLite. See
* [[http://www.sqlite.org/pragma.html#pragma_schema_version]]
@@ -291,84 +291,84 @@ public class Geary.Db.Connection : Geary.Db.Context {
public int get_schema_version_number() throws Error {
return get_pragma_int(PRAGMA_SCHEMA_VERSION);
}
-
+
/**
* See [[http://www.sqlite.org/pragma.html#pragma_foreign_keys]]
*/
public void set_foreign_keys(bool enabled) throws Error {
set_pragma_bool(PRAGMA_FOREIGN_KEYS, enabled);
}
-
+
/**
* See [[http://www.sqlite.org/pragma.html#pragma_foreign_keys]]
*/
public bool get_foreign_keys() throws Error {
return get_pragma_bool(PRAGMA_FOREIGN_KEYS);
}
-
+
/**
* See [[http://www.sqlite.org/pragma.html#pragma_recursive_triggers]]
*/
public void set_recursive_triggers(bool enabled) throws Error {
set_pragma_bool(PRAGMA_RECURSIVE_TRIGGERS, enabled);
}
-
+
/**
* See [[http://www.sqlite.org/pragma.html#pragma_recursive_triggers]]
*/
public bool get_recursive_triggers() throws Error {
return get_pragma_bool(PRAGMA_RECURSIVE_TRIGGERS);
}
-
+
/**
* See [[http://www.sqlite.org/pragma.html#pragma_secure_delete]]
*/
public void set_secure_delete(bool enabled) throws Error {
set_pragma_bool(PRAGMA_SECURE_DELETE, enabled);
}
-
+
/**
* See [[http://www.sqlite.org/pragma.html#pragma_secure_delete]]
*/
public bool get_secure_delete() throws Error {
return get_pragma_bool(PRAGMA_SECURE_DELETE);
}
-
+
/**
* See [[http://www.sqlite.org/pragma.html#pragma_synchronous]]
*/
public void set_synchronous(SynchronousMode mode) throws Error {
set_pragma_string(PRAGMA_SYNCHRONOUS, mode.sql());
}
-
+
/**
* See [[http://www.sqlite.org/pragma.html#pragma_synchronous]]
*/
public SynchronousMode get_synchronous() throws Error {
return SynchronousMode.parse(get_pragma_string(PRAGMA_SYNCHRONOUS));
}
-
+
/**
* See [[https://www.sqlite.org/pragma.html#pragma_freelist_count]]
*/
public int64 get_free_page_count() throws Error {
return get_pragma_int64(PRAGMA_FREELIST_COUNT);
}
-
+
/**
* See [[https://www.sqlite.org/pragma.html#pragma_page_count]]
*/
public int64 get_total_page_count() throws Error {
return get_pragma_int64(PRAGMA_PAGE_COUNT);
}
-
+
/**
* See [[https://www.sqlite.org/pragma.html#pragma_page_size]]
*/
public int get_page_size() throws Error {
return get_pragma_int(PRAGMA_PAGE_SIZE);
}
-
+
/**
* Executes one or more queries inside an SQLite transaction. This call will initiate a
* transaction according to the TransactionType specified (although this is merely an
@@ -391,10 +391,10 @@ public class Geary.Db.Connection : Geary.Db.Context {
} catch (Error err) {
if (!(err is IOError.CANCELLED))
debug("Connection.exec_transaction: unable to %s: %s", type.sql(), err.message);
-
+
throw err;
}
-
+
// If transaction throws an Error, must rollback, always
TransactionOutcome outcome = TransactionOutcome.ROLLBACK;
Error? caught_err = null;
@@ -404,10 +404,10 @@ public class Geary.Db.Connection : Geary.Db.Context {
} catch (Error err) {
if (!(err is IOError.CANCELLED))
debug("Connection.exec_transaction: transaction threw error: %s", err.message);
-
+
caught_err = err;
}
-
+
// commit/rollback ... don't use Cancellable for TransactionOutcome because it's SQL *must*
// execute in order to unlock the database
try {
@@ -416,10 +416,10 @@ public class Geary.Db.Connection : Geary.Db.Context {
debug("Connection.exec_transaction: Unable to %s transaction: %s", outcome.to_string(),
err.message);
}
-
+
if (caught_err != null)
throw caught_err;
-
+
return outcome;
}
diff --git a/src/engine/db/db-context.vala b/src/engine/db/db-context.vala
index 288a7e44..aac70215 100644
--- a/src/engine/db/db-context.vala
+++ b/src/engine/db/db-context.vala
@@ -16,31 +16,31 @@ public abstract class Geary.Db.Context : BaseObject {
public virtual Database? get_database() {
return get_connection() != null ? get_connection().database : null;
}
-
+
public virtual Connection? get_connection() {
return get_statement() != null ? get_statement().connection : null;
}
-
+
public virtual Statement? get_statement() {
return get_result() != null ? get_result().statement : null;
}
-
+
public virtual Result? get_result() {
return null;
}
-
+
protected inline int throw_on_error(string? method, int result, string? raw = null) throws DatabaseError
{
return Db.throw_on_error(this, method, result, raw);
}
-
+
[PrintfFormat]
protected void log(string fmt, ...) {
if (!Logging.are_all_flags_set(Logging.Flag.SQL))
return;
-
+
Connection? cx = get_connection();
Statement? stmt = get_statement();
-
+
if (stmt != null) {
Logging.debug(Logging.Flag.SQL, "%s %s\n\t<%s>",
(cx != null) ? cx.to_string() : "[no cx]",
diff --git a/src/engine/db/db-database.vala b/src/engine/db/db-database.vala
index a13a8777..83db1dd4 100644
--- a/src/engine/db/db-database.vala
+++ b/src/engine/db/db-database.vala
@@ -54,7 +54,7 @@ public class Geary.Db.Database : Geary.Db.Context {
return _is_open;
}
}
-
+
private set {
lock (_is_open) {
_is_open = value;
@@ -149,20 +149,20 @@ public class Geary.Db.Database : Geary.Db.Context {
// theirs (however improbable it is to name a table "CorruptionCheckTable")
if ((flags & DatabaseFlags.READ_ONLY) == 0) {
Connection cx = new Connection(this, Sqlite.OPEN_READWRITE, cancellable);
-
+
try {
// drop existing test table (in case created in prior failed open)
cx.exec("DROP TABLE IF EXISTS CorruptionCheckTable");
-
+
// create dummy table with a "subtantial" column
cx.exec("CREATE TABLE CorruptionCheckTable (text_col TEXT)");
-
+
// insert row
cx.exec("INSERT INTO CorruptionCheckTable (text_col) VALUES ('xyzzy')");
-
+
// select row
cx.exec("SELECT * FROM CorruptionCheckTable");
-
+
// drop table
cx.exec("DROP TABLE CorruptionCheckTable");
} catch (Error err) {
@@ -186,13 +186,13 @@ public class Geary.Db.Database : Geary.Db.Context {
public virtual void close(Cancellable? cancellable = null) throws Error {
if (!is_open)
return;
-
+
// drop the master connection, which holds a ref back to this object
master_connection = null;
-
+
// As per the contract above, can't simply drop the thread and connection pools; that would
// be bad.
-
+
is_open = false;
}
@@ -233,10 +233,10 @@ public class Geary.Db.Database : Geary.Db.Context {
Connection cx = new Connection(this, sqlite_flags, cancellable);
if (prepare_cb != null)
prepare_cb(cx, master);
-
+
return cx;
}
-
+
/**
* The master connection is a general-use connection many of the calls in Database (including
* exec(), exec_file(), query(), prepare(), and exec_trnasaction()) use to perform their work.
@@ -247,10 +247,10 @@ public class Geary.Db.Database : Geary.Db.Context {
public Connection get_master_connection() throws Error {
if (master_connection == null)
master_connection = internal_open_connection(true, null);
-
+
return master_connection;
}
-
+
/**
* Calls Connection.exec() on the master connection.
*
@@ -259,7 +259,7 @@ public class Geary.Db.Database : Geary.Db.Context {
public void exec(string sql, Cancellable? cancellable = null) throws Error {
get_master_connection().exec(sql, cancellable);
}
-
+
/**
* Calls Connection.exec_file() on the master connection.
*
@@ -268,7 +268,7 @@ public class Geary.Db.Database : Geary.Db.Context {
public void exec_file(File file, Cancellable? cancellable = null) throws Error {
get_master_connection().exec_file(file, cancellable);
}
-
+
/**
* Calls Connection.prepare() on the master connection.
*
@@ -277,7 +277,7 @@ public class Geary.Db.Database : Geary.Db.Context {
public Statement prepare(string sql) throws Error {
return get_master_connection().prepare(sql);
}
-
+
/**
* Calls Connection.query() on the master connection.
*
@@ -286,7 +286,7 @@ public class Geary.Db.Database : Geary.Db.Context {
public Result query(string sql, Cancellable? cancellable = null) throws Error {
return get_master_connection().query(sql, cancellable);
}
-
+
/**
* Calls Connection.exec_transaction() on the master connection.
*
@@ -356,13 +356,13 @@ public class Geary.Db.Database : Geary.Db.Context {
job.execute(cx);
else
job.failed(open_err);
-
+
lock (outstanding_async_jobs) {
assert(outstanding_async_jobs > 0);
--outstanding_async_jobs;
}
}
-
+
public override Database? get_database() {
return this;
}
diff --git a/src/engine/db/db-result.vala b/src/engine/db/db-result.vala
index 400d0b14..3f600d7e 100644
--- a/src/engine/db/db-result.vala
+++ b/src/engine/db/db-result.vala
@@ -6,108 +6,108 @@
public class Geary.Db.Result : Geary.Db.Context {
public bool finished { get; private set; default = false; }
-
+
public Statement statement { get; private set; }
-
+
// This results in an automatic first next().
internal Result(Statement statement, Cancellable? cancellable) throws Error {
this.statement = statement;
-
+
statement.resetted.connect(on_query_finished);
statement.bindings_cleared.connect(on_query_finished);
-
+
next(cancellable);
}
-
+
~Result() {
statement.resetted.disconnect(on_query_finished);
statement.bindings_cleared.disconnect(on_query_finished);
}
-
+
private void on_query_finished() {
finished = true;
}
-
+
/**
* Returns true if results are waiting, false if finished, or throws a DatabaseError.
*/
public bool next(Cancellable? cancellable = null) throws Error {
check_cancelled("Result.next", cancellable);
-
+
if (!finished) {
Timer timer = new Timer();
finished = throw_on_error("Result.next", statement.stmt.step(), statement.sql) != Sqlite.ROW;
if (timer.elapsed() > 1.0)
debug("\n\nDB QUERY STEP \"%s\"\nelapsed=%lf\n\n", statement.sql, timer.elapsed());
-
+
log(finished ? "NO ROW" : "ROW");
}
-
+
return !finished;
}
-
+
/**
* column is zero-based.
*/
public bool is_null_at(int column) throws DatabaseError {
verify_at(column);
-
+
bool is_null = statement.stmt.column_type(column) == Sqlite.NULL;
log("is_null_at(%d) -> %s", column, is_null.to_string());
-
+
return is_null;
}
-
+
/**
* column is zero-based.
*/
public double double_at(int column) throws DatabaseError {
verify_at(column);
-
+
double d = statement.stmt.column_double(column);
log("double_at(%d) -> %lf", column, d);
-
+
return d;
}
-
+
/**
* column is zero-based.
*/
public int int_at(int column) throws DatabaseError {
verify_at(column);
-
+
int i = statement.stmt.column_int(column);
log("int_at(%d) -> %d", column, i);
-
+
return i;
}
-
+
/**
* column is zero-based.
*/
public uint uint_at(int column) throws DatabaseError {
return (uint) int64_at(column);
}
-
+
/**
* column is zero-based.
*/
public long long_at(int column) throws DatabaseError {
return (long) int64_at(column);
}
-
+
/**
* column is zero-based.
*/
public int64 int64_at(int column) throws DatabaseError {
verify_at(column);
-
+
int64 i64 = statement.stmt.column_int64(column);
log("int64_at(%d) -> %s", column, i64.to_string());
-
+
return i64;
}
-
+
/**
* Returns the column value as a bool. The value is treated as an int and converted into a
* bool: false == 0, true == !0.
@@ -117,7 +117,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public bool bool_at(int column) throws DatabaseError {
return int_at(column) != 0;
}
-
+
/**
* column is zero-based.
*
@@ -126,7 +126,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public int64 rowid_at(int column) throws DatabaseError {
return int64_at(column);
}
-
+
/**
* column is zero-based.
*
@@ -136,13 +136,13 @@ public class Geary.Db.Result : Geary.Db.Context {
*/
public unowned string? string_at(int column) throws DatabaseError {
verify_at(column);
-
+
unowned string? s = statement.stmt.column_text(column);
log("string_at(%d) -> %s", column, (s != null) ? s : "(null)");
-
+
return s;
}
-
+
/**
* column is zero-based.
*
@@ -152,10 +152,10 @@ public class Geary.Db.Result : Geary.Db.Context {
*/
public unowned string nonnull_string_at(int column) throws DatabaseError {
unowned string? s = string_at(column);
-
+
return (s != null) ? s : "";
}
-
+
/**
* column is zero-based.
*/
@@ -164,22 +164,22 @@ public class Geary.Db.Result : Geary.Db.Context {
// internally ... GrowableBuffer is better for large blocks
Memory.GrowableBuffer buffer = new Memory.GrowableBuffer();
buffer.append(nonnull_string_at(column).data);
-
+
return buffer;
}
-
+
private void verify_at(int column) throws DatabaseError {
if (finished)
throw new DatabaseError.FINISHED("Query finished");
-
+
if (column < 0)
throw new DatabaseError.LIMITS("column %d < 0", column);
-
+
int count = statement.get_column_count();
if (column >= count)
throw new DatabaseError.LIMITS("column %d >= %d", column, count);
}
-
+
/**
* name is the name of the column in the result set. See Statement.get_column_index() for name
* matching rules.
@@ -187,7 +187,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public bool is_null_for(string name) throws DatabaseError {
return is_null_at(convert_for(name));
}
-
+
/**
* name is the name of the column in the result set. See Statement.get_column_index() for name
* matching rules.
@@ -195,7 +195,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public double double_for(string name) throws DatabaseError {
return double_at(convert_for(name));
}
-
+
/**
* name is the name of the column in the result set. See Statement.get_column_index() for name
* matching rules.
@@ -203,7 +203,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public int int_for(string name) throws DatabaseError {
return int_at(convert_for(name));
}
-
+
/**
* name is the name of the column in the result set. See Statement.get_column_index() for name
* matching rules.
@@ -211,7 +211,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public uint uint_for(string name) throws DatabaseError {
return (uint) int64_for(name);
}
-
+
/**
* name is the name of the column in the result set. See Statement.get_column_index() for name
* matching rules.
@@ -219,7 +219,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public long long_for(string name) throws DatabaseError {
return long_at(convert_for(name));
}
-
+
/**
* name is the name of the column in the result set. See Statement.get_column_index() for name
* matching rules.
@@ -227,7 +227,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public int64 int64_for(string name) throws DatabaseError {
return int64_at(convert_for(name));
}
-
+
/**
* name is the name of the column in the result set. See Statement.get_column_index() for name
* matching rules.
@@ -237,7 +237,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public bool bool_for(string name) throws DatabaseError {
return bool_at(convert_for(name));
}
-
+
/**
* name is the name of the column in the result set. See Statement.get_column_index() for name
* matching rules.
@@ -247,7 +247,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public int64 rowid_for(string name) throws DatabaseError {
return int64_for(name);
}
-
+
/**
* name is the name of the column in the result set. See Statement.get_column_index() for name
* matching rules.
@@ -259,7 +259,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public unowned string? string_for(string name) throws DatabaseError {
return string_at(convert_for(name));
}
-
+
/**
* name is the name of the column in the result set. See Statement.get_column_index() for name
* matching rules.
@@ -271,7 +271,7 @@ public class Geary.Db.Result : Geary.Db.Context {
public unowned string nonnull_string_for(string name) throws DatabaseError {
return nonnull_string_at(convert_for(name));
}
-
+
/**
* name is the name of the column in the result set. See Statement.get_column_index() for name
* matching rules.
@@ -279,18 +279,18 @@ public class Geary.Db.Result : Geary.Db.Context {
public Memory.Buffer string_buffer_for(string name) throws DatabaseError {
return string_buffer_at(convert_for(name));
}
-
+
private int convert_for(string name) throws DatabaseError {
if (finished)
throw new DatabaseError.FINISHED("Query finished");
-
+
int column = statement.get_column_index(name);
if (column < 0)
throw new DatabaseError.LIMITS("column \"%s\" not in result set", name);
-
+
return column;
}
-
+
public override Result? get_result() {
return this;
}
diff --git a/src/engine/db/db-statement.vala b/src/engine/db/db-statement.vala
index 9bab255c..214cdf6c 100644
--- a/src/engine/db/db-statement.vala
+++ b/src/engine/db/db-statement.vala
@@ -9,41 +9,41 @@ public class Geary.Db.Statement : Geary.Db.Context {
public unowned string sql { get {
return !String.is_empty(raw) ? raw : stmt.sql();
} }
-
+
public Connection connection { get; private set; }
-
+
internal Sqlite.Statement stmt;
-
+
private Gee.HashMap<string, int>? column_map = null;
-
+
/**
* Fired when the Statement is executed the first time (after creation or after a reset).
*/
public signal void executed();
-
+
/**
* Fired when the Statement is reset.
*/
public signal void resetted();
-
+
/**
* Fired when the Statement's bindings are cleared.
*/
public signal void bindings_cleared();
-
+
private Gee.HashSet<Memory.Buffer> held_buffers = new Gee.HashSet<Memory.Buffer>();
-
+
internal Statement(Connection connection, string sql) throws DatabaseError {
this.connection = connection;
// save for logging in case prepare_v2() fails
raw = sql;
-
+
throw_on_error("Statement.ctor", connection.db.prepare_v2(sql, -1, out stmt, null), sql);
-
+
// not needed any longer
raw = null;
}
-
+
/**
* Reset the Statement for reuse, optionally clearing all bindings as well. If bindings are
* not cleared, valued bound previously will be maintained.
@@ -53,26 +53,26 @@ public class Geary.Db.Statement : Geary.Db.Context {
public Statement reset(ResetScope reset_scope) throws DatabaseError {
if (reset_scope == ResetScope.CLEAR_BINDINGS)
throw_on_error("Statement.clear_bindings", stmt.clear_bindings());
-
+
throw_on_error("Statement.reset", stmt.reset());
-
+
// fire signals after Statement has been altered -- this prevents reentrancy while the
// Statement is in a halfway state
if (reset_scope == ResetScope.CLEAR_BINDINGS)
bindings_cleared();
-
+
resetted();
-
+
return this;
}
-
+
/**
* Returns the number of columns the Statement will return in a Result.
*/
public int get_column_count() {
return stmt.column_count();
}
-
+
/**
* Returns the column name for column at the zero-based index.
*
@@ -81,7 +81,7 @@ public class Geary.Db.Statement : Geary.Db.Context {
public unowned string? get_column_name(int index) {
return stmt.column_name(index);
}
-
+
/**
* Returns the zero-based column index matching the column name. Column names are
* case-insensitive.
@@ -92,7 +92,7 @@ public class Geary.Db.Statement : Geary.Db.Context {
// prepare column map only if names requested
if (column_map == null) {
column_map = new Gee.HashMap<string, int>(Geary.String.stri_hash, Geary.String.stri_equal);
-
+
int cols = stmt.column_count();
for (int ctr = 0; ctr < cols; ctr++) {
string? column_name = stmt.column_name(ctr);
@@ -100,22 +100,22 @@ public class Geary.Db.Statement : Geary.Db.Context {
column_map.set(column_name, ctr);
}
}
-
+
return column_map.has_key(name) ? column_map.get(name) : -1;
}
-
+
/**
* Executes the Statement and returns a Result object. The Result starts pointing at the first
* row in the result set. If empty, Result.finished will be true.
*/
public Result exec(Cancellable? cancellable = null) throws Error {
Result results = new Result(this, cancellable);
-
+
executed();
-
+
return results;
}
-
+
/**
* Executes the Statement and returns the last inserted rowid. If this Statement is not
* an INSERT, it will return the rowid of the last prior INSERT.
@@ -125,13 +125,13 @@ public class Geary.Db.Statement : Geary.Db.Context {
public int64 exec_insert(Cancellable? cancellable = null) throws Error {
new Result(this, cancellable);
int64 rowid = connection.last_insert_rowid;
-
+
// fire signal after safely retrieving the rowid
executed();
-
+
return rowid;
}
-
+
/**
* Executes the Statement and returns the number of rows modified by the operation. This
* Statement should be an INSERT, UPDATE, or DELETE, otherwise this will return the number
@@ -142,54 +142,54 @@ public class Geary.Db.Statement : Geary.Db.Context {
public int exec_get_modified(Cancellable? cancellable = null) throws Error {
new Result(this, cancellable);
int modified = connection.last_modified_rows;
-
+
// fire signal after safely retrieving the count
executed();
-
+
return modified;
}
-
+
/**
* index is zero-based.
*/
public Statement bind_double(int index, double d) throws DatabaseError {
throw_on_error("Statement.bind_double", stmt.bind_double(index + 1, d));
-
+
return this;
}
-
+
/**
* index is zero-based.
*/
public Statement bind_int(int index, int i) throws DatabaseError {
throw_on_error("Statement.bind_int", stmt.bind_int(index + 1, i));
-
+
return this;
}
-
+
/**
* index is zero-based.
*/
public Statement bind_uint(int index, uint u) throws DatabaseError {
return bind_int64(index, (int64) u);
}
-
+
/**
* index is zero-based.
*/
public Statement bind_long(int index, long l) throws DatabaseError {
return bind_int64(index, (int64) l);
}
-
+
/**
* index is zero-based.
*/
public Statement bind_int64(int index, int64 i64) throws DatabaseError {
throw_on_error("Statement.bind_int64", stmt.bind_int64(index + 1, i64));
-
+
return this;
}
-
+
/**
* Binds a bool to the column. A bool is stored as an integer, false == 0, true == 1. Note
* that fetching a bool via Result is more lenient; see Result.bool_at() and Result.bool_from().
@@ -199,7 +199,7 @@ public class Geary.Db.Statement : Geary.Db.Context {
public Statement bind_bool(int index, bool b) throws DatabaseError {
return bind_int(index, b ? 1 : 0);
}
-
+
/**
* index is zero-based.
*
@@ -210,7 +210,7 @@ public class Geary.Db.Statement : Geary.Db.Context {
public Statement bind_rowid(int index, int64 rowid) throws DatabaseError {
return (rowid != Db.INVALID_ROWID) ? bind_int64(index, rowid) : bind_null(index);
}
-
+
/**
* index is zero-based.
*
@@ -218,19 +218,19 @@ public class Geary.Db.Statement : Geary.Db.Context {
*/
public Statement bind_null(int index) throws DatabaseError {
throw_on_error("Statement.bind_null", stmt.bind_null(index + 1));
-
+
return this;
}
-
+
/**
* index is zero-based.
*/
public Statement bind_string(int index, string? s) throws DatabaseError {
throw_on_error("Statement.bind_string", stmt.bind_text(index + 1, s));
-
+
return this;
}
-
+
/**
* Binds the string representation of a {@link Memory.Buffer} to the replacement value
* in the {@link Statement}.
@@ -244,25 +244,25 @@ public class Geary.Db.Statement : Geary.Db.Context {
public Statement bind_string_buffer(int index, Memory.Buffer? buffer) throws DatabaseError {
if (buffer == null)
return bind_string(index, null);
-
+
Memory.UnownedStringBuffer? unowned_buffer = buffer as Memory.UnownedStringBuffer;
if (unowned_buffer == null) {
throw_on_error("Statement.bind_string_buffer", stmt.bind_text(index + 1, buffer.to_string()));
-
+
return this;
}
-
+
// hold on to buffer for lifetime of Statement, SQLite's callback isn't enough for us to
// selectively unref each Buffer as it's done with it
held_buffers.add(unowned_buffer);
-
+
// note use of _bind_text, which is for static and other strings with their own memory
// management
stmt._bind_text(index + 1, unowned_buffer.to_unowned_string());
-
+
return this;
}
-
+
public override Statement? get_statement() {
return this;
}
diff --git a/src/engine/db/db-synchronous-mode.vala b/src/engine/db/db-synchronous-mode.vala
index c945e226..e8db2a5b 100644
--- a/src/engine/db/db-synchronous-mode.vala
+++ b/src/engine/db/db-synchronous-mode.vala
@@ -8,29 +8,29 @@ public enum Geary.Db.SynchronousMode {
OFF = 0,
NORMAL = 1,
FULL = 2;
-
+
public unowned string sql() {
switch (this) {
case OFF:
return "off";
-
+
case NORMAL:
return "normal";
-
+
case FULL:
default:
return "full";
}
}
-
+
public static SynchronousMode parse(string str) {
switch (str.down()) {
case "off":
return OFF;
-
+
case "normal":
return NORMAL;
-
+
case "full":
default:
return FULL;
diff --git a/src/engine/db/db-transaction-async-job.vala b/src/engine/db/db-transaction-async-job.vala
index ff70de16..9ab8d6b5 100644
--- a/src/engine/db/db-transaction-async-job.vala
+++ b/src/engine/db/db-transaction-async-job.vala
@@ -32,7 +32,7 @@ private class Geary.Db.TransactionAsyncJob : BaseObject {
public bool is_cancelled() {
return cancellable.is_cancelled();
}
-
+
// Called in background thread context
internal void execute(Connection cx) {
// execute transaction
@@ -40,36 +40,36 @@ private class Geary.Db.TransactionAsyncJob : BaseObject {
// possible was cancelled during interim of scheduling and execution
if (is_cancelled())
throw new IOError.CANCELLED("Async transaction cancelled");
-
+
outcome = cx.exec_transaction(type, cb, cancellable);
} catch (Error err) {
if (!(err is IOError.CANCELLED))
debug("AsyncJob: transaction completed with error: %s", err.message);
-
+
caught_err = err;
}
-
+
schedule_completion();
}
-
+
// Called in background thread context
internal void failed(Error err) {
// store as a caught thread to report to original caller
caught_err = err;
-
+
schedule_completion();
}
-
+
private void schedule_completion() {
// notify foreground thread of completion
// because Idle doesn't hold a ref, manually keep this object alive
ref();
-
+
// NonblockingSemaphore and its brethren are not thread-safe, so need to signal notification
// of completion in the main thread
Idle.add(on_notify_completed);
}
-
+
private bool on_notify_completed() {
try {
completed.notify();
@@ -81,10 +81,10 @@ private class Geary.Db.TransactionAsyncJob : BaseObject {
debug("Unable to notify AsyncTransaction has completed w/o err: %s", err.message);
}
}
-
+
// manually unref; do NOT touch "this" once unref() returns, as this object may be freed
unref();
-
+
return false;
}
diff --git a/src/engine/db/db-transaction-outcome.vala b/src/engine/db/db-transaction-outcome.vala
index ede38c81..101e6329 100644
--- a/src/engine/db/db-transaction-outcome.vala
+++ b/src/engine/db/db-transaction-outcome.vala
@@ -7,31 +7,31 @@
public enum Geary.Db.TransactionOutcome {
ROLLBACK = 0,
COMMIT = 1,
-
+
// coarse synonyms
SUCCESS = COMMIT,
FAILURE = ROLLBACK,
DONE = COMMIT;
-
+
public unowned string sql() {
switch (this) {
case COMMIT:
return "COMMIT TRANSACTION";
-
+
case ROLLBACK:
default:
return "ROLLBACK TRANSACTION";
}
}
-
+
public string to_string() {
switch (this) {
case ROLLBACK:
return "rollback";
-
+
case COMMIT:
return "commit";
-
+
default:
return "(unknown: %d)".printf(this);
}
diff --git a/src/engine/db/db-transaction-type.vala b/src/engine/db/db-transaction-type.vala
index 7548de79..37aa40cd 100644
--- a/src/engine/db/db-transaction-type.vala
+++ b/src/engine/db/db-transaction-type.vala
@@ -8,38 +8,38 @@ public enum Geary.Db.TransactionType {
DEFERRED,
IMMEDIATE,
EXCLUSIVE,
-
+
// coarse synonyms
RO = DEFERRED,
RW = IMMEDIATE,
WR = EXCLUSIVE,
WO = EXCLUSIVE;
-
+
public unowned string sql() {
switch (this) {
case IMMEDIATE:
return "BEGIN IMMEDIATE";
-
+
case EXCLUSIVE:
return "BEGIN EXCLUSIVE";
-
+
case DEFERRED:
default:
return "BEGIN DEFERRED";
}
}
-
+
public string to_string() {
switch (this) {
case DEFERRED:
return "DEFERRED";
-
+
case IMMEDIATE:
return "IMMEDIATE";
-
+
case EXCLUSIVE:
return "EXCLUSIVE";
-
+
default:
return "(unknown: %d)".printf(this);
}
diff --git a/src/engine/db/db-versioned-database.vala b/src/engine/db/db-versioned-database.vala
index 23d2987a..0bcdecb3 100644
--- a/src/engine/db/db-versioned-database.vala
+++ b/src/engine/db/db-versioned-database.vala
@@ -93,7 +93,7 @@ public class Geary.Db.VersionedDatabase : Geary.Db.Database {
// get Connection for upgrade activity
Connection cx = yield open_connection(cancellable);
-
+
int db_version = cx.get_user_version_number();
debug("VersionedDatabase.upgrade: current database schema for %s: %d",
this.path, db_version);
@@ -101,7 +101,7 @@ public class Geary.Db.VersionedDatabase : Geary.Db.Database {
// If the DB doesn't exist yet, the version number will be zero, but also treat negative
// values as new
bool new_db = db_version <= 0;
-
+
// Initialize new database to version 1 (note the preincrement in the loop below)
if (db_version < 0)
db_version = 0;
diff --git a/src/engine/db/db.vala b/src/engine/db/db.vala
index c3f60a54..65e8c84b 100644
--- a/src/engine/db/db.vala
+++ b/src/engine/db/db.vala
@@ -108,7 +108,7 @@ private int throw_on_error(Context ctx, string? method, int result, string? raw
case Sqlite.ROW:
return result;
}
-
+
string location = !String.is_empty(method)
? "(%s %s) ".printf(method, ctx.get_database().path)
: "(%s) ".printf(ctx.get_database().path);
@@ -120,14 +120,14 @@ private int throw_on_error(Context ctx, string? method, int result, string? raw
sql = " (%s)".printf(raw);
else
sql = "";
-
+
string msg = "%s[err=%d]%s%s".printf(location, result, errmsg, sql);
-
+
switch (result) {
case Sqlite.BUSY:
case Sqlite.LOCKED:
throw new DatabaseError.BUSY(msg);
-
+
case Sqlite.IOERR:
case Sqlite.PERM:
case Sqlite.READONLY:
@@ -135,32 +135,32 @@ private int throw_on_error(Context ctx, string? method, int result, string? raw
case Sqlite.NOLFS:
case Sqlite.AUTH:
throw new DatabaseError.ACCESS(msg);
-
+
case Sqlite.CORRUPT:
case Sqlite.FORMAT:
case Sqlite.NOTADB:
throw new DatabaseError.CORRUPT(msg);
-
+
case Sqlite.NOMEM:
throw new DatabaseError.MEMORY(msg);
-
+
case Sqlite.ABORT:
throw new DatabaseError.ABORT(msg);
-
+
case Sqlite.INTERRUPT:
throw new DatabaseError.INTERRUPT(msg);
-
+
case Sqlite.FULL:
case Sqlite.EMPTY:
case Sqlite.TOOBIG:
case Sqlite.CONSTRAINT:
case Sqlite.RANGE:
throw new DatabaseError.LIMITS(msg);
-
+
case Sqlite.SCHEMA:
case Sqlite.MISMATCH:
throw new DatabaseError.TYPESPEC(msg);
-
+
case Sqlite.ERROR:
case Sqlite.INTERNAL:
case Sqlite.MISUSE:
diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala
index f1f83650..c8341fe8 100644
--- a/src/engine/imap-db/imap-db-account.vala
+++ b/src/engine/imap-db/imap-db-account.vala
@@ -8,7 +8,7 @@
private class Geary.ImapDB.Account : BaseObject {
private const int POPULATE_SEARCH_TABLE_DELAY_SEC = 5;
-
+
// These characters are chosen for being commonly used to continue a single word (such as
// extended last names, i.e. "Lars-Eric") or in terms commonly searched for in an email client,
// i.e. unadorned mailbox addresses. Note that characters commonly used for wildcards or that
@@ -55,10 +55,10 @@ private class Geary.ImapDB.Account : BaseObject {
private class FolderReference : Geary.SmartReference {
public Geary.FolderPath path;
-
+
public FolderReference(ImapDB.Folder folder, Geary.FolderPath path) {
base (folder);
-
+
this.path = path;
}
}
@@ -265,12 +265,12 @@ private class Geary.ImapDB.Account : BaseObject {
if (db == null)
throw new EngineError.OPEN_REQUIRED("Database not open");
}
-
+
private ImapDB.SearchQuery check_search_query(Geary.SearchQuery q) throws Error {
ImapDB.SearchQuery? query = q as ImapDB.SearchQuery;
if (query == null || query.account != this)
throw new EngineError.BAD_PARAMETERS("Geary.SearchQuery not associated with %s", name);
-
+
return query;
}
@@ -300,14 +300,14 @@ private class Geary.ImapDB.Account : BaseObject {
cancellable);
} catch (Error err) {
warning("Unable to open database: %s", err.message);
-
+
// close database before exiting
db.close(null);
db = null;
-
+
throw err;
}
-
+
// have seen cases where multiple "Inbox" folders are created in the root with different
// case names, leading to trouble ... this clears out all Inboxes that don't match our
// "canonical" name
@@ -318,7 +318,7 @@ private class Geary.ImapDB.Account : BaseObject {
FROM FolderTable
WHERE parent_id IS NULL
""");
-
+
Db.Result results = stmt.exec(cancellable);
while (!results.finished) {
string name = results.string_for("name");
@@ -327,40 +327,40 @@ private class Geary.ImapDB.Account : BaseObject {
debug("%s: Removing duplicate INBOX \"%s\"", this.name, name);
do_delete_folder(cx, results.rowid_for("id"), cancellable);
}
-
+
results.next(cancellable);
}
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
} catch (Error err) {
debug("Error trimming duplicate INBOX from database: %s", err.message);
-
+
// drop database to indicate closed
db = null;
-
+
throw err;
}
background_cancellable = new Cancellable();
-
+
// Kick off a background update of the search table, but since the database is getting
// hammered at startup, wait a bit before starting the update ... use the ordinal to
// stagger these being fired off (important for users with many accounts registered)
int account_sec = account_information.ordinal.clamp(0, 10);
Timeout.add_seconds(POPULATE_SEARCH_TABLE_DELAY_SEC + account_sec, () => {
populate_search_table_async.begin(background_cancellable);
-
+
return false;
});
-
+
initialize_contacts(cancellable);
}
public async void close_async(Cancellable? cancellable) throws Error {
if (db == null)
return;
-
+
// close and always drop reference
try {
db.close(cancellable);
@@ -404,10 +404,10 @@ private class Geary.ImapDB.Account : BaseObject {
int64 parent_id = Db.INVALID_ROWID;
if (!do_fetch_parent_id(cx, path, true, out parent_id, cancellable)) {
debug("Unable to find parent ID to %s clone folder", path.to_string());
-
+
return Db.TransactionOutcome.ROLLBACK;
}
-
+
// create the folder object
Db.Statement stmt = cx.prepare(
"INSERT INTO FolderTable (name, parent_id, last_seen_total, last_seen_status_total, "
@@ -422,9 +422,9 @@ private class Geary.ImapDB.Account : BaseObject {
: Imap.UID.INVALID);
stmt.bind_string(6, properties.attrs.serialize());
stmt.bind_int(7, properties.email_unread);
-
+
stmt.exec(cancellable);
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
@@ -460,14 +460,14 @@ private class Geary.ImapDB.Account : BaseObject {
private void initialize_contacts(Cancellable? cancellable = null) throws Error {
check_open();
-
+
Gee.Collection<Contact> contacts = new Gee.LinkedList<Contact>();
Db.TransactionOutcome outcome = db.exec_transaction(Db.TransactionType.RO,
(context) => {
Db.Statement statement = context.prepare(
"SELECT email, real_name, highest_importance, normalized_email, flags " +
"FROM ContactTable");
-
+
Db.Result result = statement.exec(cancellable);
while (!result.finished) {
try {
@@ -479,13 +479,13 @@ private class Geary.ImapDB.Account : BaseObject {
// problem with one.
debug("Problem loading contact: %s", err.message);
}
-
+
result.next();
}
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
if (outcome == Db.TransactionOutcome.DONE) {
contact_store.update_contacts(contacts);
contacts_loaded();
@@ -549,16 +549,16 @@ private class Geary.ImapDB.Account : BaseObject {
// folder, as the STATUS is the count that is known first
properties.set_status_message_count(result.int_for("last_seen_status_total"),
(properties.select_examine_messages == 0));
-
+
id_map.set(path, result.rowid_for("id"));
prop_map.set(path, properties);
-
+
result.next(cancellable);
}
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
assert(id_map.size == prop_map.size);
if (id_map.size == 0) {
@@ -572,36 +572,36 @@ private class Geary.ImapDB.Account : BaseObject {
Geary.ImapDB.Folder? folder = get_local_folder(path);
if (folder == null && id_map.has_key(path) && prop_map.has_key(path))
folder = create_local_folder(path, id_map.get(path), prop_map.get(path));
-
+
folders.add(folder);
}
-
+
return folders;
}
public async Geary.ImapDB.Folder fetch_folder_async(Geary.FolderPath path, Cancellable? cancellable)
throws Error {
check_open();
-
+
// check references table first
Geary.ImapDB.Folder? folder = get_local_folder(path);
if (folder != null)
return folder;
-
+
int64 folder_id = Db.INVALID_ROWID;
Imap.FolderProperties? properties = null;
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
if (!do_fetch_folder_id(cx, path, false, out folder_id, cancellable))
return Db.TransactionOutcome.DONE;
-
+
if (folder_id == Db.INVALID_ROWID)
return Db.TransactionOutcome.DONE;
-
+
Db.Statement stmt = cx.prepare(
"SELECT last_seen_total, unread_count, last_seen_status_total, uid_validity, uid_next, "
+ "attributes FROM FolderTable WHERE id=?");
stmt.bind_rowid(0, folder_id);
-
+
Db.Result results = stmt.exec(cancellable);
if (!results.finished) {
properties = new Imap.FolderProperties.from_imapdb(
@@ -620,28 +620,28 @@ private class Geary.ImapDB.Account : BaseObject {
(properties.select_examine_messages == 0)
);
}
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
if (folder_id == Db.INVALID_ROWID || properties == null)
throw new EngineError.NOT_FOUND("%s not found in local database", path.to_string());
-
+
return create_local_folder(path, folder_id, properties);
}
-
+
private Geary.ImapDB.Folder? get_local_folder(Geary.FolderPath path) {
FolderReference? folder_ref = folder_refs.get(path);
if (folder_ref == null)
return null;
-
+
ImapDB.Folder? folder = (Geary.ImapDB.Folder?) folder_ref.get_reference();
if (folder == null)
return null;
-
+
return folder;
}
-
+
private Geary.ImapDB.Folder create_local_folder(Geary.FolderPath path, int64 folder_id,
Imap.FolderProperties properties) throws Error {
// return current if already created
@@ -673,35 +673,35 @@ private class Geary.ImapDB.Account : BaseObject {
private void on_folder_reference_broken(Geary.SmartReference reference) {
FolderReference folder_ref = (FolderReference) reference;
-
+
// drop from folder references table, all cleaned up
folder_refs.unset(folder_ref.path);
}
-
+
public async Gee.MultiMap<Geary.Email, Geary.FolderPath?>? 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 {
check_open();
-
+
Gee.HashMultiMap<Geary.Email, Geary.FolderPath?> messages
= new Gee.HashMultiMap<Geary.Email, Geary.FolderPath?>();
-
+
if (flag_blacklist != null)
requested_fields = requested_fields | Geary.Email.Field.FLAGS;
-
+
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
Db.Statement stmt = cx.prepare("SELECT id FROM MessageTable WHERE message_id = ? OR in_reply_to
= ?");
stmt.bind_string(0, message_id.value);
stmt.bind_string(1, message_id.value);
-
+
Db.Result result = stmt.exec(cancellable);
while (!result.finished) {
int64 id = result.int64_at(0);
Geary.Email.Field db_fields;
MessageRow row = Geary.ImapDB.Folder.do_fetch_message_row(
cx, id, requested_fields, out db_fields, cancellable);
-
+
// Ignore any messages that don't have the required fields.
if (partial_ok || row.fields.fulfills(requested_fields)) {
Geary.Email email = row.to_email(new Geary.ImapDB.EmailIdentifier(id, null));
@@ -725,19 +725,19 @@ private class Geary.ImapDB.Account : BaseObject {
}
}
}
-
+
// Check for blacklisted flags.
if (flag_blacklist != null && email.email_flags != null &&
email.email_flags.contains_any(flag_blacklist))
messages.remove_all(email);
}
-
+
result.next(cancellable);
}
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
return (messages.size == 0 ? null : messages);
}
@@ -803,11 +803,11 @@ private class Geary.ImapDB.Account : BaseObject {
private string? stem_search_term(ImapDB.SearchQuery query, string term) {
if (!query.allow_stemming)
return null;
-
+
int term_length = term.length;
if (term_length < query.min_term_length_for_stemming)
return null;
-
+
string? stemmed = null;
try {
Db.Statement stmt = db.prepare("""
@@ -816,7 +816,7 @@ private class Geary.ImapDB.Account : BaseObject {
WHERE input=?
""");
stmt.bind_string(0, term);
-
+
// get stemmed string; if no result, fall through
Db.Result result = stmt.exec();
if (!result.finished)
@@ -825,37 +825,37 @@ private class Geary.ImapDB.Account : BaseObject {
debug("No stemmed term returned for \"%s\"", term);
} catch (Error err) {
debug("Unable to query tokenizer table for stemmed term for \"%s\": %s", term, err.message);
-
+
// fall-through
}
-
+
if (String.is_empty(stemmed)) {
debug("Empty stemmed term returned for \"%s\"", term);
-
+
return null;
}
-
+
// If same term returned, treat as non-stemmed
if (stemmed == term)
return null;
-
+
// Don't search for stemmed words that are significantly shorter than the user's search term
if (term_length - stemmed.length > query.max_difference_term_stem_lengths) {
debug("Stemmed \"%s\" dropped searching for \"%s\": too much distance in terms",
stemmed, term);
-
+
return null;
}
-
+
debug("Search processing: term -> stem is \"%s\" -> \"%s\"", term, stemmed);
-
+
return stemmed;
}
-
+
private void prepare_search_query(ImapDB.SearchQuery query) {
if (query.parsed)
return;
-
+
// A few goals here:
// 1) Append an * after every term so it becomes a prefix search
// (see <https://www.sqlite.org/fts3.html#section_3>)
@@ -867,7 +867,7 @@ private class Geary.ImapDB.Account : BaseObject {
// We ignore everything inside quotes to give the user a way to
// override our algorithm here. The idea is to offer one search query
// syntax for Geary that we can use locally and via IMAP, etc.
-
+
string quote_balanced = query.raw;
if (Geary.String.count_char(query.raw, '"') % 2 != 0) {
// Remove the last quote if it's not balanced. This has the
@@ -876,20 +876,20 @@ private class Geary.ImapDB.Account : BaseObject {
assert(last_quote >= 0);
quote_balanced = query.raw.splice(last_quote, last_quote + 1, " ");
}
-
+
string[] words = quote_balanced.split_set(" \t\r\n()%*\\");
bool in_quote = false;
foreach (string s in words) {
string? field = null;
-
+
s = s.strip();
-
+
int quotes = Geary.String.count_char(s, '"');
if (!in_quote && quotes > 0) {
in_quote = true;
--quotes;
}
-
+
SearchTerm? term;
if (in_quote) {
// HACK: this helps prevent a syntax error when the user types
@@ -911,19 +911,19 @@ private class Geary.ImapDB.Account : BaseObject {
case "not":
case "near":
continue;
-
+
default:
if (lower.has_prefix("near/"))
continue;
break;
}
-
+
if (s.has_prefix("-"))
s = s.substring(1);
-
+
if (s == "")
continue;
-
+
// TODO: support quotes after :
string[] parts = s.split(":", 2);
if (parts.length > 1)
@@ -960,28 +960,28 @@ private class Geary.ImapDB.Account : BaseObject {
term = new SearchTerm(original, s, stemmed, sql_s, sql_stemmed);
}
}
-
+
if (in_quote && quotes % 2 != 0)
in_quote = false;
-
+
query.add_search_term(field, term);
}
-
+
assert(!in_quote);
-
+
query.parsed = true;
}
-
+
// Return a map of column -> phrase, to use as WHERE column MATCH 'phrase'.
private Gee.HashMap<string, string> get_query_phrases(ImapDB.SearchQuery query) {
prepare_search_query(query);
-
+
Gee.HashMap<string, string> phrases = new Gee.HashMap<string, string>();
foreach (string? field in query.get_fields()) {
Gee.List<SearchTerm>? terms = query.get_search_terms(field);
if (terms == null || terms.size == 0 || field == "is")
continue;
-
+
// Each SearchTerm is an AND but the SQL text within in are OR ... this allows for
// each user term to be AND but the variants of each term are or. So, if terms are
// [party] and [eventful] and stems are [parti] and [event], the search would be:
@@ -1002,7 +1002,7 @@ private class Geary.ImapDB.Account : BaseObject {
foreach (SearchTerm term in terms) {
if (term.sql.size == 0)
continue;
-
+
if (term.is_exact) {
builder.append_printf("%s ", term.parsed);
} else {
@@ -1010,19 +1010,19 @@ private class Geary.ImapDB.Account : BaseObject {
foreach (string sql in term.sql) {
if (!is_first_sql)
builder.append(" OR ");
-
+
builder.append_printf("%s ", sql);
is_first_sql = false;
}
}
}
-
+
phrases.set(field ?? "MessageSearchTable", builder.str);
}
-
+
return phrases;
}
-
+
private void sql_add_query_phrases(StringBuilder sql, Gee.HashMap<string, string> query_phrases,
string operator, string columns, string condition) {
bool is_first_field = true;
@@ -1041,7 +1041,7 @@ private class Geary.ImapDB.Account : BaseObject {
is_first_field = false;
}
}
-
+
private int sql_bind_query_phrases(Db.Statement stmt, int start_index,
Gee.HashMap<string, string> query_phrases) throws Geary.DatabaseError {
int i = start_index;
@@ -1052,25 +1052,25 @@ private class Geary.ImapDB.Account : BaseObject {
stmt.bind_string(i++, query_phrases.get(field));
return i - start_index;
}
-
+
// Append each id in the collection to the StringBuilder, in a format
// suitable for use in an SQL statement IN (...) clause.
private void sql_append_ids(StringBuilder s, Gee.Iterable<int64?> ids) {
bool first = true;
foreach (int64? id in ids) {
assert(id != null);
-
+
if (!first)
s.append(", ");
s.append(id.to_string());
first = false;
}
}
-
+
private string? get_search_ids_sql(Gee.Collection<Geary.EmailIdentifier>? search_ids) throws Error {
if (search_ids == null)
return null;
-
+
Gee.ArrayList<int64?> ids = new Gee.ArrayList<int64?>();
foreach (Geary.EmailIdentifier id in search_ids) {
ImapDB.EmailIdentifier? imapdb_id = id as ImapDB.EmailIdentifier;
@@ -1078,15 +1078,15 @@ private class Geary.ImapDB.Account : BaseObject {
throw new EngineError.BAD_PARAMETERS(
"search_ids must contain only Geary.ImapDB.EmailIdentifiers");
}
-
+
ids.add(imapdb_id.message_id);
}
-
+
StringBuilder sql = new StringBuilder();
sql_append_ids(sql, ids);
return sql.str;
}
-
+
public async Gee.Collection<Geary.EmailIdentifier>? search_async(Geary.SearchQuery q,
int limit = 100, int offset = 0, Gee.Collection<Geary.FolderPath?>? folder_blacklist = null,
Gee.Collection<Geary.EmailIdentifier>? search_ids = null, Cancellable? cancellable = null)
@@ -1130,7 +1130,7 @@ private class Geary.ImapDB.Account : BaseObject {
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
string blacklisted_ids_sql = do_get_blacklisted_message_ids_sql(
folder_blacklist, cx, cancellable);
-
+
// Every mutation of this query we could think of has been tried,
// and this version was found to minimize running time. We
// discovered that just doing a JOIN between the MessageTable and
@@ -1157,7 +1157,7 @@ private class Geary.ImapDB.Account : BaseObject {
sql.append(")");
} else
sql.append(" WHERE 1=1");
-
+
if (blacklisted_ids_sql != "")
sql.append(" AND id NOT IN (%s)".printf(blacklisted_ids_sql));
if (!Geary.String.is_empty(search_ids_sql))
@@ -1165,17 +1165,17 @@ private class Geary.ImapDB.Account : BaseObject {
sql.append(" ORDER BY internaldate_time_t DESC");
if (limit > 0)
sql.append(" LIMIT ? OFFSET ?");
-
+
Db.Statement stmt = cx.prepare(sql.str);
int bind_index = sql_bind_query_phrases(stmt, 0, query_phrases);
if (limit > 0) {
stmt.bind_int(bind_index++, limit);
stmt.bind_int(bind_index++, offset);
}
-
+
Gee.HashMap<int64?, ImapDB.EmailIdentifier> id_map = new Gee.HashMap<int64?,
ImapDB.EmailIdentifier>(
Collection.int64_hash_func, Collection.int64_equal_func);
-
+
Db.Result result = stmt.exec(cancellable);
while (!result.finished) {
int64 message_id = result.int64_at(0);
@@ -1335,14 +1335,14 @@ private class Geary.ImapDB.Account : BaseObject {
Gee.Collection<ImapDB.EmailIdentifier> ids, Cancellable? cancellable = null) throws Error {
check_open();
ImapDB.SearchQuery query = check_search_query(q);
-
+
Gee.Set<string>? search_matches = null;
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
Gee.HashMap<int64?, ImapDB.EmailIdentifier> id_map = new Gee.HashMap<
int64?, ImapDB.EmailIdentifier>(Collection.int64_hash_func, Collection.int64_equal_func);
foreach (ImapDB.EmailIdentifier id in ids)
id_map.set(id.message_id, id);
-
+
Gee.Map<ImapDB.EmailIdentifier, Gee.Set<string>>? match_map =
do_get_search_matches(cx, query, id_map, cancellable);
if (match_map == null || match_map.size == 0)
@@ -1355,17 +1355,17 @@ private class Geary.ImapDB.Account : BaseObject {
search_matches = new Gee.HashSet<string>();
foreach (Gee.Set<string> matches in match_map.values)
search_matches.add_all(matches);
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
return search_matches;
}
-
+
public async Geary.Email fetch_email_async(ImapDB.EmailIdentifier email_id,
Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error {
check_open();
-
+
Geary.Email? email = null;
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
// TODO: once we have a way of deleting messages, we won't be able
@@ -1374,7 +1374,7 @@ private class Geary.ImapDB.Account : BaseObject {
Geary.Email.Field db_fields;
MessageRow row = Geary.ImapDB.Folder.do_fetch_message_row(
cx, email_id.message_id, required_fields, out db_fields, cancellable);
-
+
if (!row.fields.fulfills(required_fields))
throw new EngineError.INCOMPLETE_MESSAGE(
"Message %s only fulfills %Xh fields (required: %Xh)",
@@ -1387,22 +1387,22 @@ private class Geary.ImapDB.Account : BaseObject {
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
assert(email != null);
return email;
}
-
+
public async void update_contact_flags_async(Geary.Contact contact, Cancellable? cancellable)
throws Error{
check_open();
-
+
yield db.exec_transaction_async(Db.TransactionType.RW, (cx, cancellable) => {
Db.Statement update_stmt =
cx.prepare("UPDATE ContactTable SET flags=? WHERE email=?");
update_stmt.bind_string(0, contact.contact_flags.serialize());
update_stmt.bind_string(1, contact.email);
update_stmt.exec(cancellable);
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
}
@@ -1452,13 +1452,13 @@ private class Geary.ImapDB.Account : BaseObject {
} catch (Error e) {
debug("Error populating %s search table: %s", account_information.id, e.message);
}
-
+
if (search_index_monitor.is_in_progress)
search_index_monitor.notify_finish();
-
+
debug("%s: Done populating search table", account_information.id);
}
-
+
private static Gee.HashSet<int64?> do_build_rowid_set(Db.Result result, Cancellable? cancellable)
throws Error {
Gee.HashSet<int64?> rowid_set = new Gee.HashSet<int64?>(Collection.int64_hash_func,
@@ -1467,16 +1467,16 @@ private class Geary.ImapDB.Account : BaseObject {
rowid_set.add(result.rowid_at(0));
result.next(cancellable);
}
-
+
return rowid_set;
}
-
+
private async bool populate_search_table_batch_async(int limit, Cancellable? cancellable)
throws Error {
check_open();
debug("%s: Searching for up to %d missing indexed messages...", account_information.id,
limit);
-
+
int count = 0, total_unindexed = 0;
yield db.exec_transaction_async(Db.TransactionType.RW, (cx, cancellable) => {
// Embedding a SELECT within a SELECT is painfully slow
@@ -1510,19 +1510,19 @@ private class Geary.ImapDB.Account : BaseObject {
foreach (int64 message_id in message_ids) {
if (search_ids.contains(message_id))
continue;
-
+
unindexed_message_ids.add(message_id);
if (unindexed_message_ids.size >= limit)
break;
}
-
+
// For all remaining MessageTable rowid's, generate search table entry
foreach (int64 message_id in unindexed_message_ids) {
try {
Geary.Email.Field search_fields = Geary.Email.REQUIRED_FOR_MESSAGE |
Geary.Email.Field.ORIGINATORS | Geary.Email.Field.RECEIVERS |
Geary.Email.Field.SUBJECT;
-
+
Geary.Email.Field db_fields;
MessageRow row = Geary.ImapDB.Folder.do_fetch_message_row(
cx, message_id, search_fields, out db_fields, cancellable);
@@ -1539,32 +1539,32 @@ private class Geary.ImapDB.Account : BaseObject {
warning("Error adding message %s to the search table: %s", message_id.to_string(),
e.message);
}
-
+
++count;
}
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
if (count > 0) {
debug("%s: Found %d/%d missing indexed messages, %d remaining...",
account_information.id, count, limit, total_unindexed);
-
+
if (!search_index_monitor.is_in_progress) {
search_index_monitor.set_interval(0, total_unindexed);
search_index_monitor.notify_start();
}
-
+
search_index_monitor.increment(count);
}
-
+
return (count < limit);
}
-
+
//
// Transaction helper methods
//
-
+
private void do_delete_folder(Db.Connection cx, int64 folder_id, Cancellable? cancellable)
throws Error {
Db.Statement msg_loc_stmt = cx.prepare("""
@@ -1572,15 +1572,15 @@ private class Geary.ImapDB.Account : BaseObject {
WHERE folder_id = ?
""");
msg_loc_stmt.bind_rowid(0, folder_id);
-
+
msg_loc_stmt.exec(cancellable);
-
+
Db.Statement folder_stmt = cx.prepare("""
DELETE FROM FolderTable
WHERE id = ?
""");
folder_stmt.bind_rowid(0, folder_id);
-
+
folder_stmt.exec(cancellable);
}
@@ -1614,9 +1614,9 @@ private class Geary.ImapDB.Account : BaseObject {
stmt = cx.prepare("SELECT id FROM FolderTable WHERE parent_id IS NULL AND name=?");
stmt.bind_string(0, basename);
}
-
+
int64 id = Db.INVALID_ROWID;
-
+
Db.Result result = stmt.exec(cancellable);
if (!result.finished) {
id = result.rowid_at(0);
@@ -1628,25 +1628,25 @@ private class Geary.ImapDB.Account : BaseObject {
"INSERT INTO FolderTable (name, parent_id) VALUES (?, ?)");
create_stmt.bind_string(0, basename);
create_stmt.bind_rowid(1, parent_id);
-
+
id = create_stmt.exec_insert(cancellable);
}
-
+
// watch for path loops, real bad if it happens ... could be more thorough here, but at
// least one level of checking is better than none
if (id == parent_id) {
warning("Loop found in database: parent of %s is %s in FolderTable",
parent_id.to_string(), id.to_string());
-
+
return false;
}
-
+
parent_id = id;
}
-
+
// parent_id is now the folder being searched for
folder_id = parent_id;
-
+
return true;
}
@@ -1677,7 +1677,7 @@ private class Geary.ImapDB.Account : BaseObject {
Db.Result result = stmt.exec(cancellable);
return !result.finished;
}
-
+
// Turn the collection of folder paths into actual folder ids. As a
// special case, if "folderless" or orphan emails are to be blacklisted,
// set the out bool to true.
@@ -1685,7 +1685,7 @@ private class Geary.ImapDB.Account : BaseObject {
Db.Connection cx, out bool blacklist_folderless, Cancellable? cancellable) throws Error {
blacklist_folderless = false;
Gee.ArrayList<int64?> ids = new Gee.ArrayList<int64?>();
-
+
if (folder_blacklist != null) {
foreach (Geary.FolderPath? folder_path in folder_blacklist) {
if (folder_path == null) {
@@ -1698,10 +1698,10 @@ private class Geary.ImapDB.Account : BaseObject {
}
}
}
-
+
return ids;
}
-
+
// Return a parameterless SQL statement that selects any message ids that
// are in a blacklisted folder. This is used as a sub-select for the
// search query to omit results from blacklisted folders.
@@ -1710,7 +1710,7 @@ private class Geary.ImapDB.Account : BaseObject {
bool blacklist_folderless;
Gee.Collection<int64?> blacklisted_ids = do_get_blacklisted_folder_ids(
folder_blacklist, cx, out blacklist_folderless, cancellable);
-
+
StringBuilder sql = new StringBuilder();
if (blacklisted_ids.size > 0) {
sql.append("""
@@ -1721,7 +1721,7 @@ private class Geary.ImapDB.Account : BaseObject {
""");
sql_append_ids(sql, blacklisted_ids);
sql.append(")");
-
+
if (blacklist_folderless)
sql.append(" UNION ");
}
@@ -1736,10 +1736,10 @@ private class Geary.ImapDB.Account : BaseObject {
)
""");
}
-
+
return sql.str;
}
-
+
// For a message row id, return a set of all folders it's in, or null if
// it's not in any folders.
private Gee.Set<Geary.FolderPath>?
@@ -1754,20 +1754,20 @@ private class Geary.ImapDB.Account : BaseObject {
Db.Statement stmt = cx.prepare(sql);
stmt.bind_int64(0, message_id);
Db.Result result = stmt.exec(cancellable);
-
+
if (result.finished)
return null;
-
+
Gee.HashSet<Geary.FolderPath> folder_paths = new Gee.HashSet<Geary.FolderPath>();
while (!result.finished) {
int64 folder_id = result.int64_at(0);
Geary.FolderPath? path = do_find_folder_path(cx, folder_id, cancellable);
if (path != null)
folder_paths.add(path);
-
+
result.next(cancellable);
}
-
+
return (folder_paths.size == 0 ? null : folder_paths);
}
@@ -1786,10 +1786,10 @@ private class Geary.ImapDB.Account : BaseObject {
if (result.finished)
return null;
-
+
int64 parent_id = result.int64_at(0);
string name = result.nonnull_string_at(1);
-
+
// Here too, one level of loop detection is better than nothing.
if (folder_id == parent_id) {
warning("Loop found in database: parent of %s is %s in FolderTable",
@@ -1815,54 +1815,54 @@ private class Geary.ImapDB.Account : BaseObject {
unread_status) {
update_unread_async.begin(source, unread_status, null);
}
-
+
// Updates unread count on all folders.
private async void update_unread_async(ImapDB.Folder source, Gee.Map<ImapDB.EmailIdentifier, bool>
unread_status, Cancellable? cancellable) throws Error {
Gee.Map<Geary.FolderPath, int> unread_change = new Gee.HashMap<Geary.FolderPath, int>();
-
+
yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => {
foreach (ImapDB.EmailIdentifier id in unread_status.keys) {
Gee.Set<Geary.FolderPath>? paths = do_find_email_folders(
cx, id.message_id, true, cancellable);
if (paths == null)
continue;
-
+
// Remove the folder that triggered this event.
paths.remove(source.get_path());
if (paths.size == 0)
continue;
-
+
foreach (Geary.FolderPath path in paths) {
int current_unread = unread_change.has_key(path) ? unread_change.get(path) : 0;
current_unread += unread_status.get(id) ? 1 : -1;
unread_change.set(path, current_unread);
}
}
-
+
// Update each folder's unread count in the database.
foreach (Geary.FolderPath path in unread_change.keys) {
Geary.ImapDB.Folder? folder = get_local_folder(path);
if (folder == null)
continue;
-
+
folder.do_add_to_unread_count(cx, unread_change.get(path), cancellable);
}
-
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
-
+
// Update each folder's unread count property.
foreach (Geary.FolderPath path in unread_change.keys) {
Geary.ImapDB.Folder? folder = get_local_folder(path);
if (folder == null)
continue;
-
+
folder.get_properties().set_status_unseen(folder.get_properties().email_unread +
unread_change.get(path));
}
}
-
+
// Not using a MultiMap because when traversing want to process all values at once per iteration,
// not per key-value
public Gee.Map<ImapDB.EmailIdentifier, Gee.Set<string>>? do_get_search_matches(Db.Connection cx,
@@ -1870,11 +1870,11 @@ private class Geary.ImapDB.Account : BaseObject {
throws Error {
if (id_map.size == 0)
return null;
-
+
Gee.HashMap<string, string> query_phrases = get_query_phrases(query);
if (query_phrases.size == 0)
return null;
-
+
StringBuilder sql = new StringBuilder();
sql.append("""
SELECT docid, offsets(MessageSearchTable), *
@@ -1883,25 +1883,25 @@ private class Geary.ImapDB.Account : BaseObject {
""");
sql_append_ids(sql, id_map.keys);
sql.append(")");
-
+
StringBuilder condition = new StringBuilder("AND docid IN (");
sql_append_ids(condition, id_map.keys);
condition.append(")");
sql_add_query_phrases(sql, query_phrases, "UNION", "docid, offsets(MessageSearchTable), *",
condition.str);
-
+
Db.Statement stmt = cx.prepare(sql.str);
sql_bind_query_phrases(stmt, 0, query_phrases);
-
+
Gee.Map<ImapDB.EmailIdentifier, Gee.Set<string>> search_matches = new Gee.HashMap<
ImapDB.EmailIdentifier, Gee.Set<string>>();
-
+
Db.Result result = stmt.exec(cancellable);
while (!result.finished) {
int64 docid = result.rowid_at(0);
assert(id_map.has_key(docid));
ImapDB.EmailIdentifier id = id_map.get(docid);
-
+
// XXX Avoid a crash when "database disk image is
// malformed" error occurs. Remove this when the SQLite
// bug is fixed. See b.g.o #765515 for more info.
@@ -1910,36 +1910,36 @@ private class Geary.ImapDB.Account : BaseObject {
result.next(cancellable);
continue;
}
-
+
// offsets() function returns a list of 4 strings that are ints indicating position
// and length of match string in search table corpus
string[] offset_array = result.nonnull_string_at(1).split(" ");
-
+
Gee.Set<string> matches = new Gee.HashSet<string>();
-
+
int j = 0;
while (true) {
unowned string[] offset_string = offset_array[j:j+4];
-
+
int column = int.parse(offset_string[0]);
int byte_offset = int.parse(offset_string[2]);
int size = int.parse(offset_string[3]);
-
+
unowned string text = result.nonnull_string_at(column + 2);
matches.add(text[byte_offset : byte_offset + size].down());
-
+
j += 4;
if (j >= offset_array.length)
break;
}
-
+
if (search_matches.has_key(id))
matches.add_all(search_matches.get(id));
search_matches.set(id, matches);
-
+
result.next(cancellable);
}
-
+
return search_matches.size > 0 ? search_matches : null;
}
}
diff --git a/src/engine/imap-db/imap-db-contact.vala b/src/engine/imap-db/imap-db-contact.vala
index d6967ee9..7ed96b40 100644
--- a/src/engine/imap-db/imap-db-contact.vala
+++ b/src/engine/imap-db/imap-db-contact.vala
@@ -12,11 +12,11 @@ private Contact? do_fetch_contact(Db.Connection cx, string email, Cancellable? c
"SELECT real_name, highest_importance, normalized_email, flags FROM ContactTable "
+ "WHERE email=?");
stmt.bind_string(0, email);
-
+
Db.Result result = stmt.exec(cancellable);
if (result.finished)
return null;
-
+
return new Contact(email, result.string_at(0), result.int_at(1), result.string_at(2),
ContactFlags.deserialize(result.string_at(3)));
}
@@ -26,7 +26,7 @@ private Contact? do_fetch_contact(Db.Connection cx, string email, Cancellable? c
private void do_update_contact(Db.Connection connection, Contact contact,
Cancellable? cancellable) throws Error {
Contact? existing_contact = do_fetch_contact(connection, contact.email, cancellable);
-
+
// If not found, insert and done
if (existing_contact == null) {
Db.Statement stmt = connection.prepare(
@@ -37,12 +37,12 @@ private void do_update_contact(Db.Connection connection, Contact contact,
stmt.bind_string(2, contact.real_name);
stmt.bind_string(3, (contact.contact_flags != null) ? contact.contact_flags.serialize() : null);
stmt.bind_int(4, contact.highest_importance);
-
+
stmt.exec(cancellable);
-
+
return;
}
-
+
// merge two flags sets together
ContactFlags? merged_flags = contact.contact_flags;
if (existing_contact.contact_flags != null) {
@@ -51,7 +51,7 @@ private void do_update_contact(Db.Connection connection, Contact contact,
else
merged_flags = existing_contact.contact_flags;
}
-
+
// update remaining fields, careful not to overwrite non-null real_name with null (but
// using latest real_name if supplied) ... email is not updated (it's how existing_contact was
// keyed), normalized_email is inserted at the same time as email, leaving only real_name,
@@ -62,7 +62,7 @@ private void do_update_contact(Db.Connection connection, Contact contact,
stmt.bind_string(1, (merged_flags != null) ? merged_flags.serialize() : null);
stmt.bind_int(2, int.max(contact.highest_importance, existing_contact.highest_importance));
stmt.bind_string(3, contact.email);
-
+
stmt.exec(cancellable);
}
diff --git a/src/engine/imap-db/imap-db-database.vala b/src/engine/imap-db/imap-db-database.vala
index f00ff910..210800b3 100644
--- a/src/engine/imap-db/imap-db-database.vala
+++ b/src/engine/imap-db/imap-db-database.vala
@@ -405,29 +405,29 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
SELECT id, internaldate, fields
FROM MessageTable
""");
-
+
Gee.HashMap<int64?, Geary.Email.Field> invalid_ids = new Gee.HashMap<
int64?, Geary.Email.Field>();
-
+
Db.Result results = stmt.exec();
while (!results.finished) {
string? internaldate = results.string_at(1);
-
+
try {
if (!String.is_empty(internaldate))
Imap.InternalDate.decode(internaldate);
} catch (Error err) {
int64 invalid_id = results.rowid_at(0);
-
+
debug("Invalid INTERNALDATE \"%s\" found at row %s in %s: %s",
internaldate != null ? internaldate : "(null)",
invalid_id.to_string(), this.path, err.message);
invalid_ids.set(invalid_id, (Geary.Email.Field) results.int_at(2));
}
-
+
results.next();
}
-
+
// used prepared statement for iterating over list
stmt = cx.prepare("""
UPDATE MessageTable
@@ -437,13 +437,13 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
stmt.bind_null(1);
stmt.bind_null(2);
stmt.bind_null(3);
-
+
foreach (int64 invalid_id in invalid_ids.keys) {
stmt.bind_int(0, invalid_ids.get(invalid_id).clear(Geary.Email.Field.PROPERTIES));
stmt.bind_rowid(4, invalid_id);
-
+
stmt.exec();
-
+
// reuse statment, overwrite invalid_id, fields only
stmt.reset(Db.ResetScope.SAVE_BINDINGS);
}
@@ -461,15 +461,15 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
string email = result.string_at(1);
if (!RFC822.MailboxAddress.is_valid_address(email)) {
int64 id = result.rowid_at(0);
-
+
Db.Statement stmt = cx.prepare("DELETE FROM ContactTable WHERE id = ?");
stmt.bind_rowid(0, id);
stmt.exec();
}
-
+
result.next();
}
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
}
@@ -485,7 +485,7 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
""");
stmt.bind_int(0, Geary.Email.REQUIRED_FOR_MESSAGE);
stmt.bind_int(1, Geary.Email.REQUIRED_FOR_MESSAGE);
-
+
Db.Result results = stmt.exec();
if (results.finished)
return Db.TransactionOutcome.ROLLBACK;
@@ -529,7 +529,7 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
);
} catch (Error err) {
debug("Error saving attachments: %s", err.message);
-
+
// fallthrough
}
} while (results.next());
diff --git a/src/engine/imap-db/imap-db-email-identifier.vala
b/src/engine/imap-db/imap-db-email-identifier.vala
index e5dda7ef..734e5ea7 100644
--- a/src/engine/imap-db/imap-db-email-identifier.vala
+++ b/src/engine/imap-db/imap-db-email-identifier.vala
@@ -7,54 +7,54 @@
private class Geary.ImapDB.EmailIdentifier : Geary.EmailIdentifier {
public int64 message_id { get; private set; }
public Imap.UID? uid { get; private set; }
-
+
public EmailIdentifier(int64 message_id, Imap.UID? uid) {
assert(message_id != Db.INVALID_ROWID);
-
+
base (message_id.to_string());
-
+
this.message_id = message_id;
this.uid = uid;
}
-
+
// Used when a new message comes off the wire and doesn't have a rowid associated with it (yet)
// Requires a UID in order to find or create such an association
public EmailIdentifier.no_message_id(Imap.UID uid) {
base (Db.INVALID_ROWID.to_string());
-
+
message_id = Db.INVALID_ROWID;
this.uid = uid;
}
-
+
// Used to promote an id created with no_message_id to one that has a
// message id. Warning: this causes the hash value to change, so if you
// have any EmailIdentifiers in a hashed data structure, this will cause
// you not to be able to find them.
public void promote_with_message_id(int64 message_id) {
assert(this.message_id == Db.INVALID_ROWID);
-
+
unique = message_id.to_string();
this.message_id = message_id;
}
-
+
public bool has_uid() {
return (uid != null) && uid.is_valid();
}
-
+
public override int natural_sort_comparator(Geary.EmailIdentifier o) {
ImapDB.EmailIdentifier? other = o as ImapDB.EmailIdentifier;
if (other == null)
return 1;
-
+
if (uid == null)
return 1;
-
+
if (other.uid == null)
return -1;
-
+
return uid.compare_to(other.uid);
}
-
+
public override string to_string() {
return "[%s/%s]".printf(message_id.to_string(), (uid == null ? "null" : uid.to_string()));
}
@@ -65,7 +65,7 @@ private class Geary.ImapDB.EmailIdentifier : Geary.EmailIdentifier {
if (id.uid != null)
uids.add(id.uid);
}
-
+
return uids;
}
}
diff --git a/src/engine/imap-db/imap-db-folder.vala b/src/engine/imap-db/imap-db-folder.vala
index 8e22b5cf..24b22827 100644
--- a/src/engine/imap-db/imap-db-folder.vala
+++ b/src/engine/imap-db/imap-db-folder.vala
@@ -41,7 +41,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
private const int LIST_EMAIL_FIELDS_CHUNK_COUNT = 500;
private const int REMOVE_COMPLETE_LOCATIONS_CHUNK_COUNT = 500;
private const int CREATE_MERGE_EMAIL_CHUNK_COUNT = 25;
-
+
[Flags]
public enum ListFlags {
NONE = 0,
@@ -50,7 +50,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
INCLUDING_ID,
OLDEST_TO_NEWEST,
ONLY_INCOMPLETE;
-
+
public bool is_all_set(ListFlags flags) {
return (this & flags) == flags;
}
@@ -58,16 +58,16 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
public bool include_marked_for_remove() {
return is_all_set(INCLUDE_MARKED_FOR_REMOVE);
}
-
+
public static ListFlags from_folder_flags(Geary.Folder.ListFlags flags) {
ListFlags result = NONE;
-
+
if (flags.is_all_set(Geary.Folder.ListFlags.INCLUDING_ID))
result |= INCLUDING_ID;
-
+
if (flags.is_all_set(Geary.Folder.ListFlags.OLDEST_TO_NEWEST))
result |= OLDEST_TO_NEWEST;
-
+
return result;
}
}
@@ -101,7 +101,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
* saved locally.
*/
public signal void email_complete(Gee.Collection<Geary.EmailIdentifier> email_ids);
-
+
/**
* Fired when an email's unread (aka seen) status has changed. This allows the account to
* change the unread count for other folders that contain the email.
@@ -128,23 +128,23 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
public unowned Geary.FolderPath get_path() {
return path;
}
-
+
public Geary.Imap.FolderProperties get_properties() {
return properties;
}
-
+
internal void set_properties(Geary.Imap.FolderProperties properties) {
this.properties = properties;
}
-
+
public async int get_email_count_async(ListFlags flags, Cancellable? cancellable) throws Error {
int count = 0;
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
count = do_get_email_count(cx, flags, cancellable);
-
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
-
+
return count;
}
@@ -291,13 +291,13 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
GLib.Cancellable? cancellable)
throws GLib.Error {
Gee.HashMap<Geary.Email, bool> results = new Gee.HashMap<Geary.Email, bool>();
-
+
Gee.ArrayList<Geary.Email> list = traverse<Geary.Email>(emails).to_array_list();
int index = 0;
while (index < list.size) {
int stop = Numeric.int_ceiling(index + CREATE_MERGE_EMAIL_CHUNK_COUNT, list.size);
Gee.List<Geary.Email> slice = list.slice(index, stop);
-
+
Gee.ArrayList<Geary.EmailIdentifier> complete_ids = new Gee.ArrayList<Geary.EmailIdentifier>();
Gee.Collection<Contact> updated_contacts = new Gee.ArrayList<Contact>();
int total_unread_change = 0;
@@ -309,12 +309,12 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
int unread_change = 0;
bool created = do_create_or_merge_email(cx, email, out pre_fields,
out post_fields, out contacts_this_email, ref unread_change, cancellable);
-
+
if (contacts_this_email != null)
updated_contacts.add_all(contacts_this_email);
-
+
results.set(email, created);
-
+
// in essence, only fire the "email-completed" signal if the local version didn't
// have all the fields but after the create/merge now does
if (post_fields.is_all_set(Geary.Email.Field.ALL) &&
!pre_fields.is_all_set(Geary.Email.Field.ALL))
@@ -329,7 +329,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
return Db.TransactionOutcome.COMMIT;
}, cancellable);
-
+
if (updated_contacts.size > 0)
contact_store.update_contacts(updated_contacts);
@@ -342,25 +342,25 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
if (complete_ids.size > 0)
email_complete(complete_ids);
-
+
index = stop;
if (index < list.size)
yield Scheduler.sleep_ms_async(100);
}
-
+
return results;
}
-
+
public async Gee.List<Geary.Email>? list_email_by_id_async(ImapDB.EmailIdentifier? initial_id,
int count, Geary.Email.Field required_fields, ListFlags flags, Cancellable? cancellable)
throws Error {
if (count <= 0)
return null;
-
+
bool including_id = flags.is_all_set(ListFlags.INCLUDING_ID);
bool oldest_to_newest = flags.is_all_set(ListFlags.OLDEST_TO_NEWEST);
bool only_incomplete = flags.is_all_set(ListFlags.ONLY_INCOMPLETE);
-
+
// Break up work so all reading isn't done in single transaction that locks up the
// database ... first, gather locations of all emails in database
Gee.List<LocationIdentifier>? locations = null;
@@ -374,9 +374,9 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
if (location == null)
return Db.TransactionOutcome.DONE;
-
+
start_uid = location.uid;
-
+
// deal with exclusive searches
if (!including_id) {
if (oldest_to_newest)
@@ -389,55 +389,55 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
} else {
start_uid = new Imap.UID(Imap.UID.MAX);
}
-
+
if (!start_uid.is_valid())
return Db.TransactionOutcome.DONE;
-
+
StringBuilder sql = new StringBuilder("""
SELECT MessageLocationTable.message_id, ordering, remove_marker
FROM MessageLocationTable
WHERE folder_id = ?
""");
-
+
if (oldest_to_newest)
sql.append("AND ordering >= ? ");
else
sql.append("AND ordering <= ? ");
-
+
if (oldest_to_newest)
sql.append("ORDER BY ordering ASC ");
else
sql.append("ORDER BY ordering DESC ");
-
+
if (count != int.MAX)
sql.append("LIMIT ? ");
-
+
Db.Statement stmt = cx.prepare(sql.str);
stmt.bind_rowid(0, folder_id);
stmt.bind_int64(1, start_uid.value);
if (count != int.MAX)
stmt.bind_int(2, count);
-
+
locations = do_results_to_locations(stmt.exec(cancellable), count, flags, cancellable);
-
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
-
+
// remove complete locations (emails with all fields downloaded)
if (only_incomplete)
locations = yield remove_complete_locations_in_chunks_async(locations, cancellable);
-
+
// Next, read in email in chunks
return yield list_email_in_chunks_async(locations, required_fields, flags, cancellable);
}
-
+
// ListFlags.OLDEST_TO_NEWEST is ignored. INCLUDING_ID means including *both* identifiers.
// Without this flag, neither are considered as part of the range.
public async Gee.List<Geary.Email>? list_email_by_range_async(ImapDB.EmailIdentifier start_id,
ImapDB.EmailIdentifier end_id, Geary.Email.Field required_fields, ListFlags flags, Cancellable?
cancellable)
throws Error {
bool including_id = flags.is_all_set(ListFlags.INCLUDING_ID);
-
+
// Break up work so all reading isn't done in single transaction that locks up the
// database ... first, gather locations of all emails in database
Gee.List<LocationIdentifier>? locations = null;
@@ -448,25 +448,25 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
if (start_location == null)
return Db.TransactionOutcome.DONE;
-
+
Imap.UID start_uid = start_location.uid;
-
+
// see note above about INCLUDE_MARKED_FOR_REMOVE
LocationIdentifier? end_location = do_get_location_for_id(cx, end_id,
ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
if (end_location == null)
return Db.TransactionOutcome.DONE;
-
+
Imap.UID end_uid = end_location.uid;
-
+
if (!including_id) {
start_uid = start_uid.next(false);
end_uid = end_uid.previous(false);
}
-
+
if (!start_uid.is_valid() || !end_uid.is_valid() || start_uid.compare_to(end_uid) > 0)
return Db.TransactionOutcome.DONE;
-
+
Db.Statement stmt = cx.prepare("""
SELECT message_id, ordering, remove_marker
FROM MessageLocationTable
@@ -475,16 +475,16 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
stmt.bind_rowid(0, folder_id);
stmt.bind_int64(1, start_uid.value);
stmt.bind_int64(2, end_uid.value);
-
+
locations = do_results_to_locations(stmt.exec(cancellable), int.MAX, flags, cancellable);
-
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
-
+
// Next, read in email in chunks
return yield list_email_in_chunks_async(locations, required_fields, flags, cancellable);
}
-
+
// ListFlags.OLDEST_TO_NEWEST is ignored. INCLUDING_ID means including *both* identifiers.
// Without this flag, neither are considered as part of the range.
public async Gee.List<Geary.Email>? list_email_by_uid_range_async(Imap.UID start,
@@ -492,18 +492,18 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
throws Error {
bool including_id = flags.is_all_set(ListFlags.INCLUDING_ID);
bool only_incomplete = flags.is_all_set(ListFlags.ONLY_INCOMPLETE);
-
+
Imap.UID start_uid = start;
Imap.UID end_uid = end;
-
+
if (!including_id) {
start_uid = start_uid.next(false);
end_uid = end_uid.previous(false);
}
-
+
if (!start_uid.is_valid() || !end_uid.is_valid() || start_uid.compare_to(end_uid) > 0)
return null;
-
+
// Break up work so all reading isn't done in single transaction that locks up the
// database ... first, gather locations of all emails in database
Gee.List<LocationIdentifier>? locations = null;
@@ -512,34 +512,34 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
SELECT MessageLocationTable.message_id, ordering, remove_marker
FROM MessageLocationTable
""");
-
+
sql.append("WHERE folder_id = ? AND ordering >= ? AND ordering <= ? ");
-
+
Db.Statement stmt = cx.prepare(sql.str);
stmt.bind_rowid(0, folder_id);
stmt.bind_int64(1, start_uid.value);
stmt.bind_int64(2, end_uid.value);
-
+
locations = do_results_to_locations(stmt.exec(cancellable), int.MAX, flags, cancellable);
-
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
-
+
// remove complete locations (emails with all fields downloaded)
if (only_incomplete)
locations = yield remove_complete_locations_in_chunks_async(locations, cancellable);
-
+
// Next, read in email in chunks
return yield list_email_in_chunks_async(locations, required_fields, flags, cancellable);
}
-
+
public async Gee.List<Geary.Email>? list_email_by_sparse_id_async(Gee.Collection<ImapDB.EmailIdentifier>
ids,
Geary.Email.Field required_fields, ListFlags flags, Cancellable? cancellable) throws Error {
if (ids.size == 0)
return null;
-
+
bool only_incomplete = flags.is_all_set(ListFlags.ONLY_INCOMPLETE);
-
+
// Break up work so all reading isn't done in single transaction that locks up the
// database ... first, gather locations of all emails in database
Gee.List<LocationIdentifier> locations = new Gee.ArrayList<LocationIdentifier>();
@@ -549,19 +549,19 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
cancellable);
if (locs == null || locs.size == 0)
return Db.TransactionOutcome.DONE;
-
+
StringBuilder sql = new StringBuilder("""
SELECT MessageLocationTable.message_id, ordering, remove_marker
FROM MessageLocationTable
""");
-
+
if (locs.size != 1) {
sql.append("WHERE ordering IN (");
bool first = true;
foreach (LocationIdentifier location in locs) {
if (!first)
sql.append(",");
-
+
sql.append(location.uid.to_string());
first = false;
}
@@ -569,92 +569,92 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
} else {
sql.append_printf("WHERE ordering = '%s' ", locs[0].uid.to_string());
}
-
+
sql.append("AND folder_id = ? ");
-
+
Db.Statement stmt = cx.prepare(sql.str);
stmt.bind_rowid(0, folder_id);
-
+
locations = do_results_to_locations(stmt.exec(cancellable), int.MAX, flags, cancellable);
-
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
-
+
// remove complete locations (emails with all fields downloaded)
if (only_incomplete)
locations = yield remove_complete_locations_in_chunks_async(locations, cancellable);
-
+
// Next, read in email in chunks
return yield list_email_in_chunks_async(locations, required_fields, flags, cancellable);
}
-
+
private async Gee.List<LocationIdentifier>? remove_complete_locations_in_chunks_async(
Gee.List<LocationIdentifier>? locations, Cancellable? cancellable) throws Error {
if (locations == null || locations.size == 0)
return locations;
-
+
Gee.List<LocationIdentifier> incomplete_locations = new Gee.ArrayList<LocationIdentifier>();
-
+
// remove complete locations in chunks to avoid locking the database for long periods of
// time
int start = 0;
for (;;) {
if (start >= locations.size)
break;
-
+
int end = (start + REMOVE_COMPLETE_LOCATIONS_CHUNK_COUNT).clamp(0, locations.size);
Gee.List<LocationIdentifier> slice = locations.slice(start, end);
-
+
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
do_remove_complete_locations(cx, slice, cancellable);
-
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
-
+
incomplete_locations.add_all(slice);
-
+
start = end;
}
-
+
return (incomplete_locations.size > 0) ? incomplete_locations : null;
}
-
+
private async Gee.List<Geary.Email>? list_email_in_chunks_async(Gee.List<LocationIdentifier>? ids,
Geary.Email.Field required_fields, ListFlags flags, Cancellable? cancellable) throws Error {
if (ids == null || ids.size == 0)
return null;
-
+
// chunk count depends on whether or not the message -- body + headers -- is being fetched
int chunk_count = required_fields.requires_any(Email.Field.BODY | Email.Field.HEADER)
? LIST_EMAIL_WITH_MESSAGE_CHUNK_COUNT : LIST_EMAIL_METADATA_COUNT;
-
+
int length_rounded_up = Numeric.int_round_up(ids.size, chunk_count);
-
+
Gee.List<Geary.Email> results = new Gee.ArrayList<Geary.Email>();
for (int start = 0; start < length_rounded_up; start += chunk_count) {
// stop is the index *after* the end of the slice
int stop = Numeric.int_ceiling((start + chunk_count), ids.size);
-
+
Gee.List<LocationIdentifier>? slice = ids.slice(start, stop);
assert(slice != null && slice.size > 0);
-
+
Gee.List<Geary.Email>? list = null;
yield db.exec_transaction_async(Db.TransactionType.RO, (cx, cancellable) => {
list = do_list_email(cx, slice, required_fields, flags, cancellable);
-
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
-
+
if (list != null)
results.add_all(list);
}
-
+
if (results.size != ids.size)
debug("list_email_in_chunks_async: Requested %d email, returned %d", ids.size, results.size);
-
+
return (results.size > 0) ? results : null;
}
-
+
public async Geary.Email fetch_email_async(ImapDB.EmailIdentifier id,
Geary.Email.Field required_fields, ListFlags flags, Cancellable? cancellable) throws Error {
Geary.Email? email = null;
@@ -662,20 +662,20 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
LocationIdentifier? location = do_get_location_for_id(cx, id, flags, cancellable);
if (location == null)
return Db.TransactionOutcome.DONE;
-
+
email = do_location_to_email(cx, location, required_fields, flags, cancellable);
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
if (email == null) {
throw new EngineError.NOT_FOUND("No message ID %s in folder %s", id.to_string(),
to_string());
}
-
+
return email;
}
-
+
// Note that this does INCLUDES messages marked for removal
// TODO: Let the user request a SortedSet, or have them provide the Set to add to
public async Gee.Set<Imap.UID>? list_uids_by_range_async(Imap.UID first_uid, Imap.UID last_uid,
@@ -689,7 +689,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
start = last_uid;
end = first_uid;
}
-
+
Gee.Set<Imap.UID> uids = new Gee.HashSet<Imap.UID>();
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
Db.Statement stmt = cx.prepare("""
@@ -700,25 +700,25 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
stmt.bind_rowid(0, folder_id);
stmt.bind_int64(1, start.value);
stmt.bind_int64(2, end.value);
-
+
Db.Result result = stmt.exec(cancellable);
while (!result.finished) {
if (include_marked_for_removal || !result.bool_at(1))
uids.add(new Imap.UID(result.int64_at(0)));
-
+
result.next(cancellable);
}
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
return (uids.size > 0) ? uids : null;
}
-
+
// pos is 1-based. This method does not respect messages marked for removal.
public async ImapDB.EmailIdentifier? get_id_at_async(int64 pos, Cancellable? cancellable) throws Error {
assert(pos >= 1);
-
+
ImapDB.EmailIdentifier? id = null;
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
Db.Statement stmt = cx.prepare("""
@@ -731,17 +731,17 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
""");
stmt.bind_rowid(0, folder_id);
stmt.bind_int64(1, pos - 1);
-
+
Db.Result results = stmt.exec(cancellable);
if (!results.finished)
id = new ImapDB.EmailIdentifier(results.rowid_at(0), new Imap.UID(results.int64_at(1)));
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
return id;
}
-
+
public async Imap.UID? get_uid_async(ImapDB.EmailIdentifier id, ListFlags flags,
Cancellable? cancellable) throws Error {
// Always look up the UID rather than pull the one from the EmailIdentifier; it could be
@@ -751,13 +751,13 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
LocationIdentifier? location = do_get_location_for_id(cx, id, flags, cancellable);
if (location != null)
uid = location.uid;
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
return uid;
}
-
+
public async Gee.Set<Imap.UID>? get_uids_async(Gee.Collection<ImapDB.EmailIdentifier> ids,
ListFlags flags, Cancellable? cancellable) throws Error {
// Always look up the UID rather than pull the one from the EmailIdentifier; it could be
@@ -770,13 +770,13 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
foreach (LocationIdentifier location in locs)
uids.add(location.uid);
}
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
return (uids.size > 0) ? uids : null;
}
-
+
// Returns null if the UID is not found in this Folder.
public async ImapDB.EmailIdentifier? get_id_async(Imap.UID uid, ListFlags flags,
Cancellable? cancellable) throws Error {
@@ -786,13 +786,13 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
cancellable);
if (location != null)
id = location.email_id;
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
return id;
}
-
+
public async Gee.Set<ImapDB.EmailIdentifier>? get_ids_async(Gee.Collection<Imap.UID> uids,
ListFlags flags, Cancellable? cancellable) throws Error {
Gee.Set<ImapDB.EmailIdentifier> ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
@@ -803,23 +803,23 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
foreach (LocationIdentifier location in locs)
ids.add(location.email_id);
}
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
return (ids.size > 0) ? ids : null;
}
-
+
// This does not respect messages marked for removal.
public async ImapDB.EmailIdentifier? get_earliest_id_async(Cancellable? cancellable) throws Error {
return yield get_id_extremes_async(true, cancellable);
}
-
+
// This does not respect messages marked for removal.
public async ImapDB.EmailIdentifier? get_latest_id_async(Cancellable? cancellable) throws Error {
return yield get_id_extremes_async(false, cancellable);
}
-
+
private async ImapDB.EmailIdentifier? get_id_extremes_async(bool earliest, Cancellable? cancellable)
throws Error {
ImapDB.EmailIdentifier? id = null;
@@ -830,18 +830,18 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
else
stmt = cx.prepare("SELECT MAX(ordering), message_id FROM MessageLocationTable WHERE
folder_id=?");
stmt.bind_rowid(0, folder_id);
-
+
Db.Result results = stmt.exec(cancellable);
// MIN and MAX return NULL if the result set being examined is zero-length
if (!results.finished && !results.is_null_at(0))
id = new ImapDB.EmailIdentifier(results.rowid_at(1), new Imap.UID(results.int64_at(0)));
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
return id;
}
-
+
public async void detach_multiple_emails_async(Gee.Collection<ImapDB.EmailIdentifier> ids,
Cancellable? cancellable) throws Error {
int unread_count = 0;
@@ -854,10 +854,10 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
if (locs == null || locs.size == 0)
return Db.TransactionOutcome.DONE;
-
+
unread_count = do_get_unread_count_for_ids(cx, ids, cancellable);
do_add_to_unread_count(cx, -unread_count, cancellable);
-
+
StringBuilder sql = new StringBuilder("""
DELETE FROM MessageLocationTable WHERE message_id IN (
""");
@@ -868,31 +868,31 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
sql.append(", ");
}
sql.append(") AND folder_id=?");
-
+
Db.Statement stmt = cx.prepare(sql.str);
stmt.bind_rowid(0, folder_id);
-
+
stmt.exec(cancellable);
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
-
+
if (unread_count > 0)
properties.set_status_unseen(properties.email_unread - unread_count);
}
-
+
public async void detach_all_emails_async(Cancellable? cancellable) throws Error {
yield db.exec_transaction_async(Db.TransactionType.WO, (cx) => {
Db.Statement stmt = cx.prepare(
"DELETE FROM MessageLocationTable WHERE folder_id=?");
stmt.bind_rowid(0, folder_id);
-
+
stmt.exec(cancellable);
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
}
-
+
public async void mark_email_async(Gee.Collection<ImapDB.EmailIdentifier> to_mark,
Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove, Cancellable? cancellable)
throws Error {
@@ -904,32 +904,32 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
to_mark, cancellable);
if (map == null)
return Db.TransactionOutcome.COMMIT;
-
+
// update flags according to arguments
foreach (ImapDB.EmailIdentifier id in map.keys) {
Geary.Imap.EmailFlags flags = ((Geary.Imap.EmailFlags) map.get(id));
-
+
if (flags_to_add != null) {
foreach (Geary.NamedFlag flag in flags_to_add.get_all()) {
if (flags.contains(flag))
continue;
-
+
flags.add(flag);
-
+
if (flag.equal_to(Geary.EmailFlags.UNREAD)) {
unread_change++;
unread_status.set(id, true);
}
}
}
-
+
if (flags_to_remove != null) {
foreach (Geary.NamedFlag flag in flags_to_remove.get_all()) {
if (!flags.contains(flag))
continue;
-
+
flags.remove(flag);
-
+
if (flag.equal_to(Geary.EmailFlags.UNREAD)) {
unread_change--;
unread_status.set(id, false);
@@ -937,19 +937,19 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
}
}
}
-
+
// write them all back out
do_set_email_flags(cx, map, cancellable);
-
+
// Update unread count.
do_add_to_unread_count(cx, unread_change, cancellable);
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
-
+
// Update the email_unread properties.
properties.set_status_unseen((properties.email_unread + unread_change).clamp(0, int.MAX));
-
+
// Signal changes so other folders can be updated.
if (unread_status.size > 0)
unread_updated(unread_status);
@@ -960,30 +960,30 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
Gee.Map<EmailIdentifier, Geary.EmailFlags>? map = null;
yield db.exec_transaction_async(Db.TransactionType.RO, (cx, cancellable) => {
map = do_get_email_flags(cx, ids, cancellable);
-
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
-
+
return map;
}
-
+
public async void set_email_flags_async(Gee.Map<ImapDB.EmailIdentifier, Geary.EmailFlags> map,
Cancellable? cancellable) throws Error {
Error? error = null;
int unread_change = 0; // Negative means messages are read, positive means unread.
-
+
try {
yield db.exec_transaction_async(Db.TransactionType.RW, (cx, cancellable) => {
// TODO get current flags, compare to ones being set
Gee.Map<ImapDB.EmailIdentifier, Geary.EmailFlags>? existing_map =
do_get_email_flags(cx, map.keys, cancellable);
-
+
if (existing_map != null) {
foreach(ImapDB.EmailIdentifier id in map.keys) {
Geary.EmailFlags? existing_flags = existing_map.get(id);
if (existing_flags == null)
continue;
-
+
Geary.EmailFlags new_flags = map.get(id);
if (!existing_flags.contains(Geary.EmailFlags.UNREAD) &&
new_flags.contains(Geary.EmailFlags.UNREAD))
@@ -993,19 +993,19 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
unread_change--;
}
}
-
+
do_set_email_flags(cx, map, cancellable);
-
+
// Update unread count.
do_add_to_unread_count(cx, unread_change, cancellable);
-
+
// TODO set db unread count
return Db.TransactionOutcome.COMMIT;
}, cancellable);
} catch (Error e) {
error = e;
}
-
+
// Update the email_unread properties.
if (error == null) {
properties.set_status_unseen((properties.email_unread + unread_change).clamp(0, int.MAX));
@@ -1013,7 +1013,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
throw error;
}
}
-
+
public async void detach_single_email_async(ImapDB.EmailIdentifier id, Cancellable? cancellable,
out bool is_marked) throws Error {
bool internal_is_marked = false;
@@ -1025,27 +1025,27 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
throw new EngineError.NOT_FOUND("Message %s cannot be removed from %s: not found",
id.to_string(), to_string());
}
-
+
// Check to see if message is unread (this only affects non-marked emails.)
if (do_get_unread_count_for_ids(cx,
Geary.iterate<ImapDB.EmailIdentifier>(id).to_array_list(), cancellable) > 0) {
do_add_to_unread_count(cx, -1, cancellable);
was_unread = true;
}
-
+
internal_is_marked = location.marked_removed;
-
+
do_remove_association_with_folder(cx, location, cancellable);
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
-
+
is_marked = internal_is_marked;
-
+
if (was_unread)
properties.set_status_unseen(properties.email_unread - 1);
}
-
+
// Mark messages as removed (but not expunged) from the folder. Marked messages are skipped
// on most operations unless ListFlags.INCLUDE_MARKED_REMOVED is true. Use detach_email_async()
// to formally remove the messages from the folder.
@@ -1065,44 +1065,44 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
locs = do_get_locations_for_ids(cx, ids, ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
else
locs = do_get_all_locations(cx, ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
-
+
if (locs == null || locs.size == 0)
return Db.TransactionOutcome.DONE;
-
+
unread_count = do_get_unread_count_for_ids(cx, ids, cancellable);
-
+
Gee.HashSet<Imap.UID> uids = new Gee.HashSet<Imap.UID>();
foreach (LocationIdentifier location in locs) {
uids.add(location.uid);
removed_ids.add(location.email_id);
}
-
+
if (uids.size > 0)
do_mark_unmark_removed(cx, uids, mark_removed, cancellable);
-
+
do_add_to_unread_count(cx, -unread_count, cancellable);
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
if (unread_count > 0)
properties.set_status_unseen(properties.email_unread - unread_count);
-
+
return (removed_ids.size > 0) ? removed_ids : null;
}
-
+
// Returns the number of messages marked for removal in this folder
public async int get_marked_for_remove_count_async(Cancellable? cancellable) throws Error {
int count = 0;
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
count = do_get_marked_removed_count(cx, cancellable);
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
return count;
}
-
+
public async Gee.Set<ImapDB.EmailIdentifier>? get_marked_ids_async(Cancellable? cancellable)
throws Error {
Gee.Set<ImapDB.EmailIdentifier> ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
@@ -1114,20 +1114,20 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
""");
stmt.bind_rowid(0, folder_id);
stmt.bind_bool(1, false);
-
+
Db.Result results = stmt.exec(cancellable);
while (!results.finished) {
ids.add(new ImapDB.EmailIdentifier(results.rowid_at(0), new Imap.UID(results.int64_at(1))));
-
+
results.next(cancellable);
}
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
return ids.size > 0 ? ids : null;
}
-
+
// Clears all remove markers from the folder except those in the exceptions Collection
public async void clear_remove_markers_async(Gee.Collection<ImapDB.EmailIdentifier>? exceptions,
Cancellable? cancellable) throws Error {
@@ -1138,7 +1138,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
SET remove_marker=?
WHERE folder_id=? AND remove_marker <> ?
""");
-
+
if (exceptions != null && exceptions.size > 0) {
sql.append("""
AND message_id NOT IN (
@@ -1151,27 +1151,27 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
}
sql.append(")");
}
-
+
Db.Statement stmt = cx.prepare(sql.str);
stmt.bind_bool(0, false);
stmt.bind_rowid(1, folder_id);
stmt.bind_bool(2, false);
-
+
stmt.exec(cancellable);
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
}
-
+
public async Gee.Map<ImapDB.EmailIdentifier, Geary.Email.Field>? list_email_fields_by_id_async(
Gee.Collection<ImapDB.EmailIdentifier> ids, ListFlags flags, Cancellable? cancellable)
throws Error {
if (ids.size == 0)
return null;
-
+
Gee.HashMap<ImapDB.EmailIdentifier,Geary.Email.Field> map = new Gee.HashMap<
ImapDB.EmailIdentifier,Geary.Email.Field>();
-
+
// Break up the work
Gee.List<ImapDB.EmailIdentifier> list = new Gee.ArrayList<ImapDB.EmailIdentifier>();
Gee.Iterator<ImapDB.EmailIdentifier> iter = ids.iterator();
@@ -1179,71 +1179,71 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
list.add(iter.get());
if (list.size < LIST_EMAIL_FIELDS_CHUNK_COUNT && iter.has_next())
continue;
-
+
yield db.exec_transaction_async(Db.TransactionType.RO, (cx, cancellable) => {
Gee.List<LocationIdentifier>? locs = do_get_locations_for_ids(cx, ids, flags,
cancellable);
if (locs == null || locs.size == 0)
return Db.TransactionOutcome.DONE;
-
+
Db.Statement fetch_stmt = cx.prepare(
"SELECT fields FROM MessageTable WHERE id = ?");
-
+
// TODO: Unroll loop
foreach (LocationIdentifier location in locs) {
fetch_stmt.reset(Db.ResetScope.CLEAR_BINDINGS);
fetch_stmt.bind_rowid(0, location.message_id);
-
+
Db.Result results = fetch_stmt.exec(cancellable);
if (!results.finished)
map.set(location.email_id, (Geary.Email.Field) results.int_at(0));
}
-
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
-
+
list.clear();
}
assert(list.size == 0);
-
+
return (map.size > 0) ? map : null;
}
-
+
public string to_string() {
return path.to_string();
}
-
+
//
// Database transaction helper methods
// These should only be called from within a TransactionMethod.
//
-
+
private int do_get_email_count(Db.Connection cx, ListFlags flags, Cancellable? cancellable)
throws Error {
Db.Statement stmt = cx.prepare(
"SELECT COUNT(*) FROM MessageLocationTable WHERE folder_id=?");
stmt.bind_rowid(0, folder_id);
-
+
Db.Result results = stmt.exec(cancellable);
if (results.finished)
return 0;
-
+
int marked = !flags.include_marked_for_remove() ? do_get_marked_removed_count(cx, cancellable) : 0;
-
+
return Numeric.int_floor(results.int_at(0) - marked, 0);
}
-
+
private int do_get_marked_removed_count(Db.Connection cx, Cancellable? cancellable) throws Error {
Db.Statement stmt = cx.prepare(
"SELECT COUNT(*) FROM MessageLocationTable WHERE folder_id=? AND remove_marker <> ?");
stmt.bind_rowid(0, folder_id);
stmt.bind_bool(1, false);
-
+
Db.Result results = stmt.exec(cancellable);
-
+
return !results.finished ? results.int_at(0) : 0;
}
-
+
// TODO: Unroll loop
private void do_mark_unmark_removed(Db.Connection cx, Gee.Collection<Imap.UID> uids,
bool mark_removed, Cancellable? cancellable) throws Error {
@@ -1252,12 +1252,12 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
"UPDATE MessageLocationTable SET remove_marker=? WHERE folder_id=? AND ordering=?");
stmt.bind_bool(0, mark_removed);
stmt.bind_rowid(1, folder_id);
-
+
foreach (Imap.UID uid in uids) {
stmt.bind_int64(2, uid.value);
-
+
stmt.exec(cancellable);
-
+
// keep folder_id and mark_removed, replace UID each iteration
stmt.reset(Db.ResetScope.SAVE_BINDINGS);
}
@@ -1351,7 +1351,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
"DELETE FROM MessageLocationTable WHERE folder_id=? AND message_id=?");
stmt.bind_rowid(0, folder_id);
stmt.bind_int64(1, location.message_id);
-
+
stmt.exec(cancellable);
}
@@ -1553,11 +1553,11 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
Cancellable? cancellable) throws Error {
Db.Statement stmt = cx.prepare("SELECT 'TRUE' FROM MessageSearchTable WHERE docid=?");
stmt.bind_rowid(0, message_id);
-
+
Db.Result result = stmt.exec(cancellable);
return !result.finished;
}
-
+
private Gee.List<Geary.Email>? do_list_email(Db.Connection cx, Gee.List<LocationIdentifier> locations,
Geary.Email.Field required_fields, ListFlags flags, Cancellable? cancellable) throws Error {
Gee.List<Geary.Email> emails = new Gee.ArrayList<Geary.Email>();
@@ -1574,10 +1574,10 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
}
}
}
-
+
return (emails.size > 0) ? emails : null;
}
-
+
// Throws EngineError.NOT_FOUND if message_id is invalid. Note that this does not verify that
// the message is indeed in this folder.
internal static MessageRow do_fetch_message_row(Db.Connection cx, int64 message_id,
@@ -1586,26 +1586,26 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
Db.Statement stmt = cx.prepare(
"SELECT %s FROM MessageTable WHERE id=?".printf(fields_to_columns(requested_fields)));
stmt.bind_rowid(0, message_id);
-
+
Db.Result results = stmt.exec(cancellable);
if (results.finished)
throw new EngineError.NOT_FOUND("No message ID %s found in database", message_id.to_string());
-
+
db_fields = (Geary.Email.Field) results.int_for("fields");
return new MessageRow.from_result(requested_fields, results);
}
-
+
private Geary.Email do_location_to_email(Db.Connection cx, LocationIdentifier location,
Geary.Email.Field required_fields, ListFlags flags, Cancellable? cancellable) throws Error {
if (!flags.include_marked_for_remove() && location.marked_removed) {
throw new EngineError.NOT_FOUND("Message %s marked as removed in %s",
location.email_id.to_string(), to_string());
}
-
+
// look for perverse case
if (required_fields == Geary.Email.Field.NONE)
return new Geary.Email(location.email_id);
-
+
Geary.Email.Field db_fields;
MessageRow row = do_fetch_message_row(cx, location.message_id, required_fields,
out db_fields, cancellable);
@@ -1632,101 +1632,101 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
case Geary.Email.Field.DATE:
append = "date_field, date_time_t";
break;
-
+
case Geary.Email.Field.ORIGINATORS:
append = "from_field, sender, reply_to";
break;
-
+
case Geary.Email.Field.RECEIVERS:
append = "to_field, cc, bcc";
break;
-
+
case Geary.Email.Field.REFERENCES:
append = "message_id, in_reply_to, reference_ids";
break;
-
+
case Geary.Email.Field.SUBJECT:
append = "subject";
break;
-
+
case Geary.Email.Field.HEADER:
append = "header";
break;
-
+
case Geary.Email.Field.BODY:
append = "body";
break;
-
+
case Geary.Email.Field.PREVIEW:
append = "preview";
break;
-
+
case Geary.Email.Field.FLAGS:
append = "flags";
break;
-
+
case Geary.Email.Field.PROPERTIES:
append = "internaldate, internaldate_time_t, rfc822_size";
break;
}
}
-
+
if (append != null) {
builder.append(", ");
builder.append(append);
}
}
-
+
return builder.str;
}
-
+
private Gee.Map<ImapDB.EmailIdentifier, Geary.EmailFlags>? do_get_email_flags(Db.Connection cx,
Gee.Collection<ImapDB.EmailIdentifier> ids, Cancellable? cancellable) throws Error {
Gee.List<LocationIdentifier>? locs = do_get_locations_for_ids(cx, ids, ListFlags.NONE,
cancellable);
if (locs == null || locs.size == 0)
return null;
-
+
// prepare Statement for reuse
Db.Statement fetch_stmt = cx.prepare("SELECT flags FROM MessageTable WHERE id=?");
-
+
Gee.Map<ImapDB.EmailIdentifier, Geary.EmailFlags> map = new Gee.HashMap<
ImapDB.EmailIdentifier, Geary.EmailFlags>();
// TODO: Unroll this loop
foreach (LocationIdentifier location in locs) {
fetch_stmt.reset(Db.ResetScope.CLEAR_BINDINGS);
fetch_stmt.bind_rowid(0, location.message_id);
-
+
Db.Result results = fetch_stmt.exec(cancellable);
if (results.finished || results.is_null_at(0))
continue;
-
+
map.set(location.email_id,
new Geary.Imap.EmailFlags(Geary.Imap.MessageFlags.deserialize(results.string_at(0))));
}
-
+
return (map.size > 0) ? map : null;
}
-
- private Geary.EmailFlags? do_get_email_flags_single(Db.Connection cx, int64 message_id,
+
+ private Geary.EmailFlags? do_get_email_flags_single(Db.Connection cx, int64 message_id,
Cancellable? cancellable) throws Error {
Db.Statement fetch_stmt = cx.prepare("SELECT flags FROM MessageTable WHERE id=?");
fetch_stmt.bind_rowid(0, message_id);
-
+
Db.Result results = fetch_stmt.exec(cancellable);
-
+
if (results.finished || results.is_null_at(0))
return null;
-
+
return new Geary.Imap.EmailFlags(Geary.Imap.MessageFlags.deserialize(results.string_at(0)));
}
-
+
// TODO: Unroll loop
private void do_set_email_flags(Db.Connection cx, Gee.Map<ImapDB.EmailIdentifier, Geary.EmailFlags> map,
Cancellable? cancellable) throws Error {
Db.Statement update_stmt = cx.prepare(
"UPDATE MessageTable SET flags=?, fields = fields | ? WHERE id=?");
-
+
foreach (ImapDB.EmailIdentifier id in map.keys) {
LocationIdentifier? location = do_get_location_for_id(
cx,
@@ -1752,39 +1752,39 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
update_stmt.bind_string(0, flags.message_flags.serialize());
update_stmt.bind_int(1, Geary.Email.Field.FLAGS);
update_stmt.bind_rowid(2, id.message_id);
-
+
update_stmt.exec(cancellable);
}
}
-
+
private bool do_fetch_email_fields(Db.Connection cx, int64 message_id, out Geary.Email.Field fields,
Cancellable? cancellable) throws Error {
Db.Statement stmt = cx.prepare("SELECT fields FROM MessageTable WHERE id=?");
stmt.bind_rowid(0, message_id);
-
+
Db.Result results = stmt.exec(cancellable);
if (results.finished) {
fields = Geary.Email.Field.NONE;
-
+
return false;
}
-
+
fields = (Geary.Email.Field) results.int_at(0);
-
+
return true;
}
-
+
private void do_merge_message_row(Db.Connection cx, MessageRow row,
out Geary.Email.Field new_fields, out Gee.Collection<Contact> updated_contacts,
ref int unread_count_change, Cancellable? cancellable) throws Error {
-
+
// Initialize to an empty list, in case we return early.
updated_contacts = new Gee.LinkedList<Contact>();
-
+
Geary.Email.Field available_fields;
if (!do_fetch_email_fields(cx, row.id, out available_fields, cancellable))
throw new EngineError.NOT_FOUND("No message with ID %s found in database", row.id.to_string());
-
+
// This calculates the fields in the row that are not in the database already and then adds
// any available mutable fields provided by the caller
new_fields = (row.fields ^ available_fields) & row.fields;
@@ -1793,17 +1793,17 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
// nothing to add
return;
}
-
+
if (new_fields.is_any_set(Geary.Email.Field.DATE)) {
Db.Statement stmt = cx.prepare(
"UPDATE MessageTable SET date_field=?, date_time_t=? WHERE id=?");
stmt.bind_string(0, row.date);
stmt.bind_int64(1, row.date_time_t);
stmt.bind_rowid(2, row.id);
-
+
stmt.exec(cancellable);
}
-
+
if (new_fields.is_any_set(Geary.Email.Field.ORIGINATORS)) {
Db.Statement stmt = cx.prepare(
"UPDATE MessageTable SET from_field=?, sender=?, reply_to=? WHERE id=?");
@@ -1811,10 +1811,10 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
stmt.bind_string(1, row.sender);
stmt.bind_string(2, row.reply_to);
stmt.bind_rowid(3, row.id);
-
+
stmt.exec(cancellable);
}
-
+
if (new_fields.is_any_set(Geary.Email.Field.RECEIVERS)) {
Db.Statement stmt = cx.prepare(
"UPDATE MessageTable SET to_field=?, cc=?, bcc=? WHERE id=?");
@@ -1822,10 +1822,10 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
stmt.bind_string(1, row.cc);
stmt.bind_string(2, row.bcc);
stmt.bind_rowid(3, row.id);
-
+
stmt.exec(cancellable);
}
-
+
if (new_fields.is_any_set(Geary.Email.Field.REFERENCES)) {
Db.Statement stmt = cx.prepare(
"UPDATE MessageTable SET message_id=?, in_reply_to=?, reference_ids=? WHERE id=?");
@@ -1833,65 +1833,65 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
stmt.bind_string(1, row.in_reply_to);
stmt.bind_string(2, row.references);
stmt.bind_rowid(3, row.id);
-
+
stmt.exec(cancellable);
}
-
+
if (new_fields.is_any_set(Geary.Email.Field.SUBJECT)) {
Db.Statement stmt = cx.prepare(
"UPDATE MessageTable SET subject=? WHERE id=?");
stmt.bind_string(0, row.subject);
stmt.bind_rowid(1, row.id);
-
+
stmt.exec(cancellable);
}
-
+
if (new_fields.is_any_set(Geary.Email.Field.HEADER)) {
Db.Statement stmt = cx.prepare(
"UPDATE MessageTable SET header=? WHERE id=?");
stmt.bind_string_buffer(0, row.header);
stmt.bind_rowid(1, row.id);
-
+
stmt.exec(cancellable);
}
-
+
if (new_fields.is_any_set(Geary.Email.Field.BODY)) {
Db.Statement stmt = cx.prepare(
"UPDATE MessageTable SET body=? WHERE id=?");
stmt.bind_string_buffer(0, row.body);
stmt.bind_rowid(1, row.id);
-
+
stmt.exec(cancellable);
}
-
+
if (new_fields.is_any_set(Geary.Email.Field.PREVIEW)) {
Db.Statement stmt = cx.prepare(
"UPDATE MessageTable SET preview=? WHERE id=?");
stmt.bind_string(0, row.preview);
stmt.bind_rowid(1, row.id);
-
+
stmt.exec(cancellable);
}
-
+
if (new_fields.is_any_set(Geary.Email.Field.FLAGS)) {
// Fetch existing flags to update unread count
Geary.EmailFlags? old_flags = do_get_email_flags_single(cx, row.id, cancellable);
Geary.EmailFlags new_flags = new Geary.Imap.EmailFlags(
Geary.Imap.MessageFlags.deserialize(row.email_flags));
-
+
if (old_flags != null && (old_flags.is_unread() != new_flags.is_unread()))
unread_count_change += new_flags.is_unread() ? 1 : -1;
else if (new_flags.is_unread())
unread_count_change++;
-
+
Db.Statement stmt = cx.prepare(
"UPDATE MessageTable SET flags=? WHERE id=?");
stmt.bind_string(0, row.email_flags);
stmt.bind_rowid(1, row.id);
-
+
stmt.exec(cancellable);
}
-
+
if (new_fields.is_any_set(Geary.Email.Field.PROPERTIES)) {
Db.Statement stmt = cx.prepare(
"UPDATE MessageTable SET internaldate=?, internaldate_time_t=?, rfc822_size=? WHERE id=?");
@@ -1899,18 +1899,18 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
stmt.bind_int64(1, row.internaldate_time_t);
stmt.bind_int64(2, row.rfc822_size);
stmt.bind_rowid(3, row.id);
-
+
stmt.exec(cancellable);
}
-
+
// now merge the new fields in the row
Db.Statement stmt = cx.prepare(
"UPDATE MessageTable SET fields = fields | ? WHERE id=?");
stmt.bind_int(0, new_fields);
stmt.bind_rowid(1, row.id);
-
+
stmt.exec(cancellable);
-
+
// Update the autocompletion table.
MessageAddresses message_addresses =
new MessageAddresses.from_row(account_owner_email, row);
@@ -2004,10 +2004,10 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
out Gee.Collection<Contact> updated_contacts, ref int unread_count_change,
Cancellable? cancellable) throws Error {
assert(email.fields == Geary.Email.Field.FLAGS);
-
+
// no contacts were harmed in the production of this email
updated_contacts = new Gee.ArrayList<Contact>();
-
+
// fetch MessageRow and its fields, note that the fields now include FLAGS if they didn't
// already
MessageRow row = do_fetch_message_row(cx, location.message_id, Geary.Email.Field.FLAGS,
@@ -2048,7 +2048,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
Cancellable? cancellable) throws Error {
// Default to an empty list, in case we never call do_merge_message_row.
updated_contacts = new Gee.LinkedList<Contact>();
-
+
// fetch message from database and merge in this email
MessageRow row = do_fetch_message_row(cx, location.message_id,
email.fields | Email.REQUIRED_FOR_MESSAGE | Attachment.REQUIRED_FIELDS,
@@ -2056,17 +2056,17 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
Geary.Email.Field fetched_fields = row.fields;
post_fields = pre_fields | email.fields;
row.merge_from_remote(email);
-
+
if (email.fields == Geary.Email.Field.NONE)
return;
-
+
// Merge in any fields in the submitted email that aren't already in the database or are mutable
int new_unread_count = 0;
if (((fetched_fields & email.fields) != email.fields) ||
email.fields.is_any_set(Geary.Email.MUTABLE_FIELDS)) {
// Build the combined email from the merge, which will be used to save the attachments
Geary.Email combined_email = row.to_email(location.email_id);
-
+
// Update attachments if not already in the database
if (!fetched_fields.fulfills(Attachment.REQUIRED_FIELDS)
&& combined_email.fields.fulfills(Attachment.REQUIRED_FIELDS)) {
@@ -2084,7 +2084,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
Geary.Email.Field new_fields;
do_merge_message_row(cx, row, out new_fields, out updated_contacts,
ref new_unread_count, cancellable);
-
+
if (do_check_for_message_search_row(cx, location.message_id, cancellable))
do_merge_email_in_search_table(cx, location.message_id, new_fields, combined_email,
cancellable);
else
@@ -2096,7 +2096,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
if (combined_flags != null && combined_flags.is_unread())
new_unread_count = 1;
}
-
+
unread_count_change += new_unread_count;
}
@@ -2108,48 +2108,48 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
throws Error {
if (to_add == 0)
return; // Nothing to do.
-
+
Db.Statement update_stmt = cx.prepare(
"UPDATE FolderTable SET unread_count = CASE WHEN unread_count + ? < 0 THEN 0 ELSE " +
"unread_count + ? END WHERE id=?");
-
+
update_stmt.bind_int(0, to_add);
update_stmt.bind_int(1, to_add);
update_stmt.bind_rowid(2, folder_id);
-
+
update_stmt.exec(cancellable);
}
-
+
// Db.Result must include columns for "message_id", "ordering", and "remove_marker" from the
// MessageLocationTable
private Gee.List<LocationIdentifier> do_results_to_locations(Db.Result results, int count,
ListFlags flags, Cancellable? cancellable) throws Error {
Gee.List<LocationIdentifier> locations = new Gee.ArrayList<LocationIdentifier>();
-
+
if (results.finished)
return locations;
-
+
do {
LocationIdentifier location = new LocationIdentifier(results.rowid_for("message_id"),
new Imap.UID(results.int64_for("ordering")), results.bool_for("remove_marker"));
if (!flags.include_marked_for_remove() && location.marked_removed)
continue;
-
+
locations.add(location);
if (locations.size >= count)
break;
} while (results.next(cancellable));
-
+
return locations;
}
-
+
// Use a separate step to strip out complete emails because original implementation (using an
// INNER JOIN) was horribly slow under load
private void do_remove_complete_locations(Db.Connection cx, Gee.List<LocationIdentifier>? locations,
Cancellable? cancellable) throws Error {
if (locations == null || locations.size == 0)
return;
-
+
StringBuilder sql = new StringBuilder("""
SELECT id FROM MessageTable WHERE id IN (
""");
@@ -2157,37 +2157,37 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
foreach (LocationIdentifier location_id in locations) {
if (!first)
sql.append(",");
-
+
sql.append(location_id.message_id.to_string());
first = false;
}
sql.append(") AND fields <> ?");
-
+
Db.Statement stmt = cx.prepare(sql.str);
stmt.bind_int(0, Geary.Email.Field.ALL);
-
+
Db.Result results = stmt.exec(cancellable);
-
+
Gee.HashSet<int64?> incomplete_locations = new Gee.HashSet<int64?>(Collection.int64_hash_func,
Collection.int64_equal_func);
while (!results.finished) {
incomplete_locations.add(results.int64_at(0));
results.next(cancellable);
}
-
+
if (incomplete_locations.size == 0) {
locations.clear();
-
+
return;
}
-
+
Gee.Iterator<LocationIdentifier> iter = locations.iterator();
while (iter.next()) {
if (!incomplete_locations.contains(iter.get().message_id))
iter.remove();
}
}
-
+
private LocationIdentifier? do_get_location_for_id(Db.Connection cx, ImapDB.EmailIdentifier id,
ListFlags flags, Cancellable? cancellable) throws Error {
Db.Statement stmt = cx.prepare("""
@@ -2197,23 +2197,23 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
""");
stmt.bind_rowid(0, folder_id);
stmt.bind_rowid(1, id.message_id);
-
+
Db.Result result = stmt.exec(cancellable);
if (result.finished)
return null;
-
+
LocationIdentifier location = new LocationIdentifier(id.message_id,
new Imap.UID(result.int64_at(0)), result.bool_at(1));
-
+
return (!flags.include_marked_for_remove() && location.marked_removed) ? null : location;
}
-
+
private Gee.List<LocationIdentifier>? do_get_locations_for_ids(Db.Connection cx,
Gee.Collection<ImapDB.EmailIdentifier>? ids, ListFlags flags, Cancellable? cancellable)
throws Error {
if (ids == null || ids.size == 0)
return null;
-
+
StringBuilder sql = new StringBuilder("""
SELECT message_id, ordering, remove_marker
FROM MessageLocationTable
@@ -2224,20 +2224,20 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
if (!first)
sql.append(",");
sql.append_printf(id.message_id.to_string());
-
+
first = false;
}
sql.append(") AND folder_id = ?");
-
+
Db.Statement stmt = cx.prepare(sql.str);
stmt.bind_rowid(0, folder_id);
-
+
Gee.List<LocationIdentifier> locs = do_results_to_locations(stmt.exec(cancellable), int.MAX,
flags, cancellable);
-
+
return (locs.size > 0) ? locs : null;
}
-
+
private LocationIdentifier? do_get_location_for_uid(Db.Connection cx, Imap.UID uid,
ListFlags flags, Cancellable? cancellable) throws Error {
Db.Statement stmt = cx.prepare("""
@@ -2247,22 +2247,22 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
""");
stmt.bind_rowid(0, folder_id);
stmt.bind_int64(1, uid.value);
-
+
Db.Result result = stmt.exec(cancellable);
if (result.finished)
return null;
-
+
LocationIdentifier location = new LocationIdentifier(result.rowid_at(0), uid, result.bool_at(1));
-
+
return (!flags.include_marked_for_remove() && location.marked_removed) ? null : location;
}
-
+
private Gee.List<LocationIdentifier>? do_get_locations_for_uids(Db.Connection cx,
Gee.Collection<Imap.UID>? uids, ListFlags flags, Cancellable? cancellable)
throws Error {
if (uids == null || uids.size == 0)
return null;
-
+
StringBuilder sql = new StringBuilder("""
SELECT message_id, ordering, remove_marker
FROM MessageLocationTable
@@ -2273,20 +2273,20 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
if (!first)
sql.append(",");
sql.append(uid.value.to_string());
-
+
first = false;
}
sql.append(") AND folder_id = ?");
-
+
Db.Statement stmt = cx.prepare(sql.str);
stmt.bind_rowid(0, folder_id);
-
+
Gee.List<LocationIdentifier> locs = do_results_to_locations(stmt.exec(cancellable), int.MAX,
flags, cancellable);
-
+
return (locs.size > 0) ? locs : null;
}
-
+
private Gee.List<LocationIdentifier>? do_get_all_locations(Db.Connection cx, ListFlags flags,
Cancellable? cancellable) throws Error {
Db.Statement stmt = cx.prepare("""
@@ -2295,18 +2295,18 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
WHERE folder_id = ?
""");
stmt.bind_rowid(0, folder_id);
-
+
Gee.List<LocationIdentifier> locs = do_results_to_locations(stmt.exec(cancellable), int.MAX,
flags, cancellable);
-
+
return (locs.size > 0) ? locs : null;
}
-
+
private int do_get_unread_count_for_ids(Db.Connection cx,
Gee.Collection<ImapDB.EmailIdentifier>? ids, Cancellable? cancellable) throws Error {
if (ids == null || ids.size == 0)
return 0;
-
+
// Fetch flags for each email and update this folder's unread count.
// (Note that this only flags for emails which have NOT been marked for removal
// are included.)
@@ -2314,7 +2314,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
ids, cancellable);
if (flag_map != null)
return Geary.traverse<Geary.EmailFlags>(flag_map.values).count_matching(f => f.is_unread());
-
+
return 0;
}
diff --git a/src/engine/imap-db/imap-db-gc.vala b/src/engine/imap-db/imap-db-gc.vala
index 183bb2f8..7184ec18 100644
--- a/src/engine/imap-db/imap-db-gc.vala
+++ b/src/engine/imap-db/imap-db-gc.vala
@@ -28,33 +28,33 @@
private class Geary.ImapDB.GC {
// Maximum number of days between reaping runs.
private const int REAP_DAYS_SPAN = 10;
-
+
// Minimum number of days between vacuums.
private const int VACUUM_DAYS_SPAN = 30;
-
+
// Number of reaped messages since last vacuum indicating another vacuum should occur
private const int VACUUM_WHEN_REAPED_REACHES = 10000;
-
+
// Amount of disk space that must be saved to start a vacuum (500MB).
private const long VACUUM_WHEN_FREE_BYTES = 500 * 1024 * 1024;
-
+
// Days old from today an unlinked email message must be to be reaped by the garbage collector
private const int UNLINKED_DAYS = 30;
-
+
// Amount of time to sleep between various database-bound GC iterations to give other
// transactions a chance
private const uint SLEEP_MSEC = 15;
-
+
// Number of database operations to perform before sleeping (obviously this is a rough
// approximation, as not all operations are the same cost)
private const int OPS_PER_SLEEP_CYCLE = 10;
-
+
// Number of files to reap from the DeleteAttachmentFileTable per iteration
private const int REAP_ATTACHMENT_PER = 5;
-
+
// Number of files to enumerate per time when walking a directory's children
private const int ENUM_DIR_PER = 10;
-
+
/**
* Operation(s) recommended by {@link should_run_async}.
*/
@@ -100,7 +100,7 @@ private class Geary.ImapDB.GC {
int64 free_page_bytes;
yield fetch_gc_info_async(cancellable, out last_reap_time, out last_vacuum_time,
out reaped_messages_since_last_vacuum, out free_page_bytes);
-
+
debug("[%s] GC state: last_reap_time=%s last_vacuum_time=%s reaped_messages_since=%d
free_page_bytes=%s",
to_string(),
(last_reap_time != null) ? last_reap_time.to_string() : "never",
@@ -120,18 +120,18 @@ private class Geary.ImapDB.GC {
if (last_reap_time == null) {
// null means reaping has never executed
debug("[%s] Recommending reaping: never completed", to_string());
-
+
op |= RecommendedOperation.REAP;
} else if (elapsed_days(now, last_reap_time, out days) >= REAP_DAYS_SPAN) {
debug("[%s] Recommending reaping: %s days since last run", to_string(),
days.to_string());
-
+
op |= RecommendedOperation.REAP;
} else {
debug("[%s] Reaping last completed on %s (%s days ago)", to_string(),
last_reap_time.to_string(), days.to_string());
}
-
+
// VACUUM is not something we do regularly, but rather look for a lot of reaped messages
// as indicator it's time ... to prevent doing this a lot (which is annoying), still space
// it out over a minimum amount of time
@@ -140,20 +140,20 @@ private class Geary.ImapDB.GC {
if (last_vacuum_time == null) {
debug("[%s] Database never vacuumed (%d messages reaped)", to_string(),
reaped_messages_since_last_vacuum);
-
+
vacuum_permitted = true;
} else if (elapsed_days(now, last_vacuum_time, out days) >= VACUUM_DAYS_SPAN) {
debug("[%s] Database vacuuming permitted (%s days since last run, %d messages reaped since)",
to_string(), days.to_string(), reaped_messages_since_last_vacuum);
-
+
vacuum_permitted = true;
} else {
debug("[%s] Database vacuuming not permitted (%s days since last run, %d messages reaped since)",
to_string(), days.to_string(), reaped_messages_since_last_vacuum);
-
+
vacuum_permitted = false;
}
-
+
// VACUUM_DAYS_SPAN must have passed since last vacuum (unless no prior vacuum has occurred)
// *and* a certain number of messages have been reaped (indicating fragmentation is a
// possibility) or a certain amount of free space exists in the database (indicating they
@@ -163,19 +163,19 @@ private class Geary.ImapDB.GC {
if (vacuum_permitted && (fragmentation_exists || too_much_free_space)) {
debug("[%s] Recommending database vacuum: %d messages reaped since last vacuum %s days ago, %s
free bytes in file",
to_string(), reaped_messages_since_last_vacuum, days.to_string(),
free_page_bytes.to_string());
-
+
op |= RecommendedOperation.VACUUM;
}
-
+
return op;
}
-
+
private static int64 elapsed_days(DateTime end, DateTime start, out int64 days) {
days = end.difference(start) / TimeSpan.DAY;
-
+
return days;
}
-
+
/**
* Vacuum the database, reducing fragmentation and coalescing free space, to optimize access
* and reduce disk usage.
@@ -191,7 +191,7 @@ private class Geary.ImapDB.GC {
public async void vacuum_async(Cancellable? cancellable) throws Error {
if (is_running)
throw new EngineError.ALREADY_OPEN("Cannot vacuum %s: already running", to_string());
-
+
is_running = true;
try {
debug("[%s] Starting vacuum of IMAP database", to_string());
@@ -201,10 +201,10 @@ private class Geary.ImapDB.GC {
is_running = false;
}
}
-
+
private async void internal_vacuum_async(Cancellable? cancellable) throws Error {
DateTime? last_vacuum_time = null;
-
+
// NOTE: VACUUM cannot happen inside a transaction, so to avoid blocking the main thread,
// run a non-transacted command from a background thread
Geary.Db.Connection cx = yield db.open_connection(cancellable);
@@ -215,11 +215,11 @@ private class Geary.ImapDB.GC {
// of the next transaction is not instantaneous
last_vacuum_time = new DateTime.now_local();
}, cancellable);
-
+
// could assert here, but these calls really need to be bulletproof
if (last_vacuum_time == null)
last_vacuum_time = new DateTime.now_local();
-
+
// update last vacuum time and reset messages reaped since last vacuum ... don't allow this
// to be cancelled, really want to get this in stone so the user doesn't re-vacuum
// unnecessarily
@@ -231,13 +231,13 @@ private class Geary.ImapDB.GC {
""");
stmt.bind_int64(0, last_vacuum_time.to_unix());
stmt.bind_int(1, 0);
-
+
stmt.exec(cancellable);
-
+
return Db.TransactionOutcome.COMMIT;
}, null);
}
-
+
/**
* Run the garbage collector, which reaps unlinked messages from the database and deletes
* their on-disk attachments.
@@ -252,7 +252,7 @@ private class Geary.ImapDB.GC {
public async void reap_async(Cancellable? cancellable) throws Error {
if (is_running)
throw new EngineError.ALREADY_OPEN("Cannot garbage collect %s: already running", to_string());
-
+
is_running = true;
try {
debug("[%s] Starting garbage collection of IMAP database", to_string());
@@ -262,7 +262,7 @@ private class Geary.ImapDB.GC {
is_running = false;
}
}
-
+
private async void internal_reap_async(Cancellable? cancellable) throws Error {
//
// Find all messages unlinked from the location table and older than the GC reap date ...
@@ -282,14 +282,14 @@ private class Geary.ImapDB.GC {
// in the MessageTable but never downloaded. Since internaldate is the first thing
// downloaded, this is rare, but can happen, and this will reap those rows.
//
-
+
DateTime reap_date = new DateTime.now_local().add_days(0 - UNLINKED_DAYS);
debug("[%s] Garbage collector reaping date: %s (%s)", to_string(), reap_date.to_string(),
reap_date.to_unix().to_string());
-
+
Gee.HashSet<int64?> reap_message_ids = new Gee.HashSet<int64?>(Collection.int64_hash_func,
Collection.int64_equal_func);
-
+
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
Db.Statement stmt = cx.prepare("""
SELECT id
@@ -302,19 +302,19 @@ private class Geary.ImapDB.GC {
)
""");
stmt.bind_int64(0, reap_date.to_unix());
-
+
Db.Result result = stmt.exec(cancellable);
while (!result.finished) {
reap_message_ids.add(result.rowid_at(0));
-
+
result.next(cancellable);
}
-
+
return Db.TransactionOutcome.DONE;
}, cancellable);
-
+
message("[%s] Found %d email messages ready for reaping", to_string(), reap_message_ids.size);
-
+
//
// To prevent holding the database lock for long periods of time, reap each message one
// at a time, deleting it from the message table and subsidiary tables. Although slow, we
@@ -323,7 +323,7 @@ private class Geary.ImapDB.GC {
// without leaving the database in an incoherent state. gc can be resumed even if'
// interrupted.
//
-
+
int count = 0;
foreach (int64 reap_message_id in reap_message_ids) {
try {
@@ -332,59 +332,59 @@ private class Geary.ImapDB.GC {
} catch (Error err) {
if (err is IOError.CANCELLED)
throw err;
-
+
message("[%s] Unable to reap message #%s: %s", to_string(), reap_message_id.to_string(),
err.message);
}
-
+
if ((count % OPS_PER_SLEEP_CYCLE) == 0)
yield Scheduler.sleep_ms_async(SLEEP_MSEC);
-
+
if ((count % 5000) == 0)
debug("[%s] Reaped %d messages", to_string(), count);
}
-
+
message("[%s] Reaped completed: %d messages", to_string(), count);
-
+
//
// Now reap on-disk attachment files marked for deletion. Since they're added to the
// DeleteAttachmentFileTable as part of the reap_message_async() transaction, it's assured
// that they're ready for deletion (and, again, means this process is resumable)
//
-
+
count = 0;
for (;;) {
int reaped = yield reap_attachment_files_async(REAP_ATTACHMENT_PER, cancellable);
if (reaped == 0)
break;
-
+
count += reaped;
-
+
if ((count % OPS_PER_SLEEP_CYCLE) == 0)
yield Scheduler.sleep_ms_async(SLEEP_MSEC);
-
+
if ((count % 1000) == 0)
debug("[%s] Reaped %d attachment files", to_string(), count);
}
-
+
message("[%s] Completed: Reaped %d attachment files", to_string(), count);
-
+
//
// To be sure everything's clean, delete any empty directories in the attachment dir tree,
// as code (here and elsewhere) only removes files.
//
-
+
count = yield delete_empty_attachment_directories_async(null, cancellable, null);
-
+
message("[%s] Deleted %d empty attachment directories", to_string(), count);
-
+
//
// A full reap cycle completed -- store date for next time. By only storing when the full
// cycle is completed, even if the user closes the application through the cycle it will
// start the next time, assuring all reaped messages/attachments are dealt with in a timely
// manner.
//
-
+
yield db.exec_transaction_async(Db.TransactionType.WO, (cx) => {
Db.Statement stmt = cx.prepare("""
UPDATE GarbageCollectionTable
@@ -392,13 +392,13 @@ private class Geary.ImapDB.GC {
WHERE id = 0
""");
stmt.bind_int64(0, new DateTime.now_local().to_unix());
-
+
stmt.exec(cancellable);
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
}
-
+
private async void reap_message_async(int64 message_id, Cancellable? cancellable) throws Error {
yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => {
// Since there's a window of time between locating gc-able messages and removing them,
@@ -409,16 +409,16 @@ private class Geary.ImapDB.GC {
WHERE message_id = ?
""");
stmt.bind_rowid(0, message_id);
-
+
// If find one, then message is no longer unlinked
Db.Result result = stmt.exec(cancellable);
if (!result.finished) {
debug("[%s] Not reaping message #%s: found linked in MessageLocationTable",
to_string(), message_id.to_string());
-
+
return Db.TransactionOutcome.ROLLBACK;
}
-
+
//
// Fetch all on-disk attachments for this message
//
@@ -430,39 +430,39 @@ private class Geary.ImapDB.GC {
//
// Delete from search table
//
-
+
stmt = cx.prepare("""
DELETE FROM MessageSearchTable
WHERE docid = ?
""");
stmt.bind_rowid(0, message_id);
-
+
stmt.exec(cancellable);
-
+
//
// Delete from attachment table
//
-
+
stmt = cx.prepare("""
DELETE FROM MessageAttachmentTable
WHERE message_id = ?
""");
stmt.bind_rowid(0, message_id);
-
+
stmt.exec(cancellable);
-
+
//
// Delete from message table
//
-
+
stmt = cx.prepare("""
DELETE FROM MessageTable
WHERE id = ?
""");
stmt.bind_rowid(0, message_id);
-
+
stmt.exec(cancellable);
-
+
//
// Mark on-disk attachment files as ready for deletion (handled by
// reap_attachments_files_async). This two-step process assures that this transaction
@@ -482,25 +482,25 @@ private class Geary.ImapDB.GC {
//
// Increment the reap count since last vacuum
//
-
+
cx.exec("""
UPDATE GarbageCollectionTable
SET reaped_messages_since_last_vacuum = reaped_messages_since_last_vacuum + 1
WHERE id = 0
""");
-
+
//
// Done; other than on-disk attachment files, message is now garbage collected.
//
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
}
-
+
private async int reap_attachment_files_async(int limit, Cancellable? cancellable) throws Error {
if (limit <= 0)
return 0;
-
+
int deleted = 0;
yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => {
Db.Statement stmt = cx.prepare("""
@@ -509,61 +509,61 @@ private class Geary.ImapDB.GC {
LIMIT ?
""");
stmt.bind_int(0, limit);
-
+
// build SQL for removing file from table (whether it's deleted or not -- at this point,
// we're in best-attempt mode)
StringBuilder sql = new StringBuilder("""
DELETE FROM DeleteAttachmentFileTable
WHERE id IN (
""");
-
+
Db.Result result = stmt.exec(cancellable);
bool first = true;
while (!result.finished) {
int64 id = result.rowid_at(0);
File file = File.new_for_path(result.string_at(1));
-
+
// if it deletes, great; if not, we tried
try {
file.delete(cancellable);
} catch (Error err) {
if (err is IOError.CANCELLED)
throw err;
-
+
debug("[%s] Unable to delete reaped attachment file \"%s\": %s", to_string(),
file.get_path(), err.message);
}
-
+
if (!first)
sql.append(", ");
-
+
sql.append(id.to_string());
first = false;
-
+
deleted++;
-
+
result.next(cancellable);
}
-
+
sql.append(")");
-
+
// if any files were deleted, remove them from the table
if (deleted > 0)
cx.exec(sql.str);
-
+
return Db.TransactionOutcome.COMMIT;
}, cancellable);
-
+
return deleted;
}
-
+
private async int delete_empty_attachment_directories_async(File? current, Cancellable? cancellable,
out bool empty) throws Error {
File current_dir = current ?? db.attachments_path;
// directory is considered empty until file or non-deleted child directory is found
empty = true;
-
+
int deleted = 0;
FileEnumerator file_enum = yield current_dir.enumerate_children_async("*",
FileQueryInfoFlags.NOFOLLOW_SYMLINKS, priority, cancellable);
@@ -571,25 +571,25 @@ private class Geary.ImapDB.GC {
List<FileInfo> infos = yield file_enum.next_files_async(ENUM_DIR_PER, priority, cancellable);
if (infos.length() == 0)
break;
-
+
foreach (FileInfo info in infos) {
if (info.get_file_type() != FileType.DIRECTORY) {
empty = false;
-
+
continue;
}
-
+
File child = current_dir.get_child(info.get_name());
-
+
bool child_empty;
deleted += yield delete_empty_attachment_directories_async(child, cancellable,
out child_empty);
if (!child_empty) {
empty = false;
-
+
continue;
}
-
+
string? failure = null;
try {
if (!yield child.delete_async(priority, cancellable))
@@ -597,24 +597,24 @@ private class Geary.ImapDB.GC {
} catch (Error err) {
if (err is IOError.CANCELLED)
throw err;
-
+
failure = err.message;
}
-
+
if (failure == null) {
deleted++;
} else {
message("[%s] Unable to delete empty attachment directory \"%s\": %s",
to_string(), child.get_path(), failure);
-
+
// since it remains, directory not empty
empty = false;
}
}
}
-
+
yield file_enum.close_async(priority, cancellable);
-
+
return deleted;
}
@@ -648,21 +648,21 @@ private class Geary.ImapDB.GC {
FROM GarbageCollectionTable
WHERE id = 0
""");
-
+
if (result.finished)
return Db.TransactionOutcome.FAILURE;
-
+
// NULL indicates reaping/vacuum has not run
last_reap_time_t = !result.is_null_at(0) ? result.int64_at(0) : -1;
last_vacuum_time_t = !result.is_null_at(1) ? result.int64_at(1) : -1;
reaped_count = result.int_at(2);
-
+
free_page_count = cx.get_free_page_count();
page_size = cx.get_page_size();
-
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
-
+
last_reap_time = (last_reap_time_t >= 0) ? new DateTime.from_unix_local(last_reap_time_t) : null;
last_vacuum_time = (last_vacuum_time_t >= 0) ? new DateTime.from_unix_local(last_vacuum_time_t) :
null;
reaped_messages_since_last_vacuum = reaped_count;
diff --git a/src/engine/imap-db/imap-db-message-addresses.vala
b/src/engine/imap-db/imap-db-message-addresses.vala
index d260bdcf..54154f3c 100644
--- a/src/engine/imap-db/imap-db-message-addresses.vala
+++ b/src/engine/imap-db/imap-db-message-addresses.vala
@@ -7,13 +7,13 @@
private class Geary.ImapDB.MessageAddresses : BaseObject {
// Read-only view.
public Gee.Collection<Contact> contacts { get; private set; }
-
+
private RFC822.MailboxAddress? sender_address;
private RFC822.MailboxAddresses? from_addresses;
private RFC822.MailboxAddresses? to_addresses;
private RFC822.MailboxAddresses? cc_addresses;
private RFC822.MailboxAddresses? bcc_addresses;
-
+
private int from_importance;
private int to_importance;
private int cc_importance;
@@ -26,32 +26,32 @@ private class Geary.ImapDB.MessageAddresses : BaseObject {
this.to_addresses = to_addresses;
this.cc_addresses = cc_addresses;
this.bcc_addresses = bcc_addresses;
-
+
calculate_importance(account_owner_email);
contacts = build_contacts();
}
-
+
private MessageAddresses.from_strings(string account_owner_email, string? sender_field,
string? from_field, string? to_field, string? cc_field, string? bcc_field) {
this(account_owner_email, get_address_from_string(sender_field),
get_addresses_from_string(from_field), get_addresses_from_string(to_field),
get_addresses_from_string(cc_field), get_addresses_from_string(bcc_field));
}
-
+
public MessageAddresses.from_email(string account_owner_email, Geary.Email email) {
this(account_owner_email, email.sender, email.from, email.to, email.cc, email.bcc);
}
-
+
public MessageAddresses.from_row(string account_owner_email, MessageRow row) {
this.from_strings(account_owner_email, row.sender, row.from, row.to, row.cc, row.bcc);
}
-
+
public MessageAddresses.from_result(string account_owner_email, Db.Result result) {
this.from_strings(account_owner_email, get_string_or_null(result, "sender"),
get_string_or_null(result, "from_field"), get_string_or_null(result, "to_field"),
get_string_or_null(result, "cc"), get_string_or_null(result, "bcc"));
}
-
+
private static string? get_string_or_null(Db.Result result, string column) {
try {
return result.string_for(column);
@@ -84,30 +84,30 @@ private class Geary.ImapDB.MessageAddresses : BaseObject {
(from_addresses != null && from_addresses.contains_normalized(account_owner_email));
bool account_owner_in_to = to_addresses != null &&
to_addresses.contains_normalized(account_owner_email);
-
+
// If the account owner's address does not appear in any of these fields, we assume they
// were BCC'd.
bool account_owner_in_cc =
(cc_addresses != null && cc_addresses.contains_normalized(account_owner_email)) ||
(bcc_addresses != null && bcc_addresses.contains_normalized(account_owner_email)) ||
!(account_owner_in_from || account_owner_in_to);
-
+
from_importance = -1;
to_importance = -1;
cc_importance = -1;
-
+
if (account_owner_in_from) {
from_importance = int.max(from_importance, ContactImportance.FROM_FROM);
to_importance = int.max(to_importance, ContactImportance.FROM_TO);
cc_importance = int.max(cc_importance, ContactImportance.FROM_CC);
}
-
+
if (account_owner_in_to) {
from_importance = int.max(from_importance, ContactImportance.TO_FROM);
to_importance = int.max(to_importance, ContactImportance.TO_TO);
cc_importance = int.max(cc_importance, ContactImportance.TO_CC);
}
-
+
if (account_owner_in_cc) {
from_importance = int.max(from_importance, ContactImportance.CC_FROM);
to_importance = int.max(to_importance, ContactImportance.CC_TO);
@@ -133,16 +133,16 @@ private class Geary.ImapDB.MessageAddresses : BaseObject {
int importance) {
if (addresses == null)
return;
-
+
foreach (RFC822.MailboxAddress address in addresses)
add_contact(contacts_map, address, importance);
}
-
+
private void add_contact(Gee.Map<string, Contact> contacts_map, RFC822.MailboxAddress address,
int importance) {
if (!address.is_valid())
return;
-
+
Contact contact = new Contact.from_rfc822_address(address, importance);
Contact? old_contact = contacts_map[contact.normalized_email];
if (old_contact == null || old_contact.highest_importance < contact.highest_importance)
diff --git a/src/engine/imap-db/imap-db-message-row.vala b/src/engine/imap-db/imap-db-message-row.vala
index 5e52b2a6..6ed964ab 100644
--- a/src/engine/imap-db/imap-db-message-row.vala
+++ b/src/engine/imap-db/imap-db-message-row.vala
@@ -7,96 +7,96 @@
private class Geary.ImapDB.MessageRow {
public int64 id { get; set; default = Db.INVALID_ROWID; }
public Geary.Email.Field fields { get; set; default = Geary.Email.Field.NONE; }
-
+
public string? date { get; set; default = null; }
public time_t date_time_t { get; set; default = -1; }
-
+
public string? from { get; set; default = null; }
public string? sender { get; set; default = null; }
public string? reply_to { get; set; default = null; }
-
+
public string? to { get; set; default = null; }
public string? cc { get; set; default = null; }
public string? bcc { get; set; default = null; }
-
+
public string? message_id { get; set; default = null; }
public string? in_reply_to { get; set; default = null; }
public string? references { get; set; default = null; }
-
+
public string? subject { get; set; default = null; }
-
+
public Memory.Buffer? header { get; set; default = null; }
-
+
public Memory.Buffer? body { get; set; default = null; }
-
+
public string? preview { get; set; default = null; }
-
+
public string? email_flags { get; set; default = null; }
public string? internaldate { get; set; default = null; }
public time_t internaldate_time_t { get; set; default = -1; }
public int64 rfc822_size { get; set; default = -1; }
-
+
public MessageRow() {
}
-
+
public MessageRow.from_email(Geary.Email email) {
set_from_email(email);
}
-
+
// Converts the current row of the Result object into fields. It's vitally important that
// the columns specified in requested_fields be present in Result.
public MessageRow.from_result(Geary.Email.Field requested_fields, Db.Result results) throws Error {
id = results.int64_for("id");
-
+
// the available fields are an intersection of what's available in the database and
// what was requested
fields = requested_fields & results.int_for("fields");
-
+
if (fields.is_all_set(Geary.Email.Field.DATE)) {
date = results.string_for("date_field");
date_time_t = (time_t) results.int64_for("date_time_t");
}
-
+
if (fields.is_all_set(Geary.Email.Field.ORIGINATORS)) {
from = results.string_for("from_field");
sender = results.string_for("sender");
reply_to = results.string_for("reply_to");
}
-
+
if (fields.is_all_set(Geary.Email.Field.RECEIVERS)) {
to = results.string_for("to_field");
cc = results.string_for("cc");
bcc = results.string_for("bcc");
}
-
+
if (fields.is_all_set(Geary.Email.Field.REFERENCES)) {
message_id = results.string_for("message_id");
in_reply_to = results.string_for("in_reply_to");
references = results.string_for("reference_ids");
}
-
+
if (fields.is_all_set(Geary.Email.Field.SUBJECT))
subject = results.string_for("subject");
-
+
if (fields.is_all_set(Geary.Email.Field.HEADER))
header = results.string_buffer_for("header");
-
+
if (fields.is_all_set(Geary.Email.Field.BODY))
body = results.string_buffer_for("body");
-
+
if (fields.is_all_set(Geary.Email.Field.PREVIEW))
preview = results.string_for("preview");
-
+
if (fields.is_all_set(Geary.Email.Field.FLAGS))
email_flags = results.string_for("flags");
-
+
if (fields.is_all_set(Geary.Email.Field.PROPERTIES)) {
internaldate = results.string_for("internaldate");
internaldate_time_t = (time_t) results.int64_for("internaldate_time_t");
rfc822_size = results.int64_for("rfc822_size");
}
}
-
+
public Geary.Email to_email(ImapDB.EmailIdentifier id) throws Error {
// Important to set something in the Email object if the field bit is set ... for example,
// if the caller expects to see a DATE field, that field is set in the Email's bitmask,
@@ -131,131 +131,131 @@ private class Geary.ImapDB.MessageRow {
(in_reply_to != null) ? new RFC822.MessageIDList.from_rfc822_string(in_reply_to) : null,
(references != null) ? new RFC822.MessageIDList.from_rfc822_string(references) : null);
}
-
+
if (fields.is_all_set(Geary.Email.Field.SUBJECT))
email.set_message_subject(new RFC822.Subject.decode(subject ?? ""));
-
+
if (fields.is_all_set(Geary.Email.Field.HEADER))
email.set_message_header(new RFC822.Header(header ?? Memory.EmptyBuffer.instance));
-
+
if (fields.is_all_set(Geary.Email.Field.BODY))
email.set_message_body(new RFC822.Text(body ?? Memory.EmptyBuffer.instance));
-
+
if (fields.is_all_set(Geary.Email.Field.PREVIEW))
email.set_message_preview(new RFC822.PreviewText(new Geary.Memory.StringBuffer(preview ?? "")));
-
+
if (fields.is_all_set(Geary.Email.Field.FLAGS))
email.set_flags(get_generic_email_flags());
-
+
if (fields.is_all_set(Geary.Email.Field.PROPERTIES)) {
Imap.EmailProperties? properties = get_imap_email_properties();
if (properties != null)
email.set_email_properties(properties);
}
-
+
return email;
}
-
-
+
+
public Geary.Imap.EmailProperties? get_imap_email_properties() {
if (internaldate == null || rfc822_size < 0)
return null;
-
+
Imap.InternalDate? constructed = null;
try {
constructed = Imap.InternalDate.decode(internaldate);
} catch (Error err) {
debug("Unable to construct internaldate object from \"%s\": %s", internaldate,
err.message);
-
+
return null;
}
-
+
return new Geary.Imap.EmailProperties(constructed, new RFC822.Size(rfc822_size));
}
-
+
public Geary.EmailFlags? get_generic_email_flags() {
return (email_flags != null)
? new Geary.Imap.EmailFlags(Geary.Imap.MessageFlags.deserialize(email_flags))
: null;
}
-
+
public void merge_from_remote(Geary.Email email) {
set_from_email(email);
}
-
+
private void set_from_email(Geary.Email email) {
// Although the fields bitmask might indicate various fields are set, they may still be
// null if empty
-
+
if (email.fields.is_all_set(Geary.Email.Field.DATE)) {
date = (email.date != null) ? email.date.original : null;
date_time_t = (email.date != null) ? email.date.to_time_t() : -1;
-
+
fields = fields.set(Geary.Email.Field.DATE);
}
-
+
if (email.fields.is_all_set(Geary.Email.Field.ORIGINATORS)) {
from = flatten_addresses(email.from);
sender = flatten_address(email.sender);
reply_to = flatten_addresses(email.reply_to);
-
+
fields = fields.set(Geary.Email.Field.ORIGINATORS);
}
-
+
if (email.fields.is_all_set(Geary.Email.Field.RECEIVERS)) {
to = flatten_addresses(email.to);
cc = flatten_addresses(email.cc);
bcc = flatten_addresses(email.bcc);
-
+
fields = fields.set(Geary.Email.Field.RECEIVERS);
}
-
+
if (email.fields.is_all_set(Geary.Email.Field.REFERENCES)) {
message_id = (email.message_id != null) ? email.message_id.value : null;
in_reply_to = (email.in_reply_to != null) ? email.in_reply_to.to_rfc822_string() : null;
references = (email.references != null) ? email.references.to_rfc822_string() : null;
-
+
fields = fields.set(Geary.Email.Field.REFERENCES);
}
-
+
if (email.fields.is_all_set(Geary.Email.Field.SUBJECT)) {
subject = (email.subject != null) ? email.subject.original : null;
-
+
fields = fields.set(Geary.Email.Field.SUBJECT);
}
-
+
if (email.fields.is_all_set(Geary.Email.Field.HEADER)) {
header = (email.header != null) ? email.header.buffer : null;
-
+
fields = fields.set(Geary.Email.Field.HEADER);
}
-
+
if (email.fields.is_all_set(Geary.Email.Field.BODY)) {
body = (email.body != null) ? email.body.buffer : null;
-
+
fields = fields.set(Geary.Email.Field.BODY);
}
-
+
if (email.fields.is_all_set(Geary.Email.Field.PREVIEW)) {
preview = (email.preview != null) ? email.preview.buffer.to_string() : null;
-
+
fields = fields.set(Geary.Email.Field.PREVIEW);
}
-
+
if (email.fields.is_all_set(Geary.Email.Field.FLAGS)) {
Geary.Imap.EmailFlags? imap_flags = (Geary.Imap.EmailFlags) email.email_flags;
email_flags = (imap_flags != null) ? imap_flags.message_flags.serialize() : null;
-
+
fields = fields.set(Geary.Email.Field.FLAGS);
}
-
+
if (email.fields.is_all_set(Geary.Email.Field.PROPERTIES)) {
Geary.Imap.EmailProperties? imap_properties = (Geary.Imap.EmailProperties) email.properties;
internaldate = (imap_properties != null) ? imap_properties.internaldate.serialize() : null;
internaldate_time_t = (imap_properties != null) ? imap_properties.internaldate.to_time_t() : -1;
rfc822_size = (imap_properties != null) ? imap_properties.rfc822_size.value : -1;
-
+
fields = fields.set(Geary.Email.Field.PROPERTIES);
}
}
diff --git a/src/engine/imap-db/search/imap-db-search-email-identifier.vala
b/src/engine/imap-db/search/imap-db-search-email-identifier.vala
index a8b6e7c6..0a014851 100644
--- a/src/engine/imap-db/search/imap-db-search-email-identifier.vala
+++ b/src/engine/imap-db/search/imap-db-search-email-identifier.vala
@@ -7,33 +7,33 @@
private class Geary.ImapDB.SearchEmailIdentifier : ImapDB.EmailIdentifier,
Gee.Comparable<SearchEmailIdentifier> {
public DateTime? date_received { get; private set; }
-
+
public SearchEmailIdentifier(int64 message_id, DateTime? date_received) {
base(message_id, null);
-
+
this.date_received = date_received;
}
-
+
public static int compare_descending(SearchEmailIdentifier a, SearchEmailIdentifier b) {
return b.compare_to(a);
}
-
+
public static Gee.ArrayList<SearchEmailIdentifier> array_list_from_results(
Gee.Collection<Geary.EmailIdentifier>? results) {
Gee.ArrayList<SearchEmailIdentifier> r = new Gee.ArrayList<SearchEmailIdentifier>();
-
+
if (results != null) {
foreach (Geary.EmailIdentifier id in results) {
SearchEmailIdentifier? search_id = id as SearchEmailIdentifier;
-
+
assert(search_id != null);
r.add(search_id);
}
}
-
+
return r;
}
-
+
// Searches for a generic EmailIdentifier in a collection of SearchEmailIdentifiers.
public static SearchEmailIdentifier? collection_get_email_identifier(
Gee.Collection<SearchEmailIdentifier> collection, Geary.EmailIdentifier id) {
@@ -43,31 +43,31 @@ private class Geary.ImapDB.SearchEmailIdentifier : ImapDB.EmailIdentifier,
}
return null;
}
-
+
public override int natural_sort_comparator(Geary.EmailIdentifier o) {
ImapDB.SearchEmailIdentifier? other = o as ImapDB.SearchEmailIdentifier;
if (other == null)
return 1;
-
+
return compare_to(other);
}
-
+
public virtual int compare_to(SearchEmailIdentifier other) {
// if both have date received, compare on that, using stable sort if the same
if (date_received != null && other.date_received != null) {
int compare = date_received.compare(other.date_received);
-
+
return (compare != 0) ? compare : stable_sort_comparator(other);
}
-
+
// if neither have date received, fall back on stable sort
if (date_received == null && other.date_received == null)
return stable_sort_comparator(other);
-
+
// put identifiers with no date ahead of those with
return (date_received == null ? -1 : 1);
}
-
+
public override string to_string() {
return "[%s/null/%s]".printf(message_id.to_string(),
(date_received == null ? "null" : date_received.to_string()));
diff --git a/src/engine/imap-db/search/imap-db-search-folder-properties.vala
b/src/engine/imap-db/search/imap-db-search-folder-properties.vala
index 13f84124..870a55ef 100644
--- a/src/engine/imap-db/search/imap-db-search-folder-properties.vala
+++ b/src/engine/imap-db/search/imap-db-search-folder-properties.vala
@@ -8,7 +8,7 @@ private class Geary.ImapDB.SearchFolderProperties : Geary.FolderProperties {
public SearchFolderProperties(int total, int unread) {
base(total, unread, Trillian.FALSE, Trillian.FALSE, Trillian.TRUE, true, true, false);
}
-
+
public void set_total(int total) {
this.email_total = total;
}
diff --git a/src/engine/imap-db/search/imap-db-search-folder.vala
b/src/engine/imap-db/search/imap-db-search-folder.vala
index 5f590635..5a2b84e1 100644
--- a/src/engine/imap-db/search/imap-db-search-folder.vala
+++ b/src/engine/imap-db/search/imap-db-search-folder.vala
@@ -36,20 +36,20 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
account.folders_available_unavailable.connect(on_folders_available_unavailable);
account.email_locally_complete.connect(on_email_locally_complete);
account.email_removed.connect(on_account_email_removed);
-
+
clear_search_results();
-
+
// We always want to exclude emails that don't live anywhere from
// search results.
exclude_orphan_emails();
}
-
+
~SearchFolder() {
account.folders_available_unavailable.disconnect(on_folders_available_unavailable);
account.email_locally_complete.disconnect(on_email_locally_complete);
account.email_removed.disconnect(on_account_email_removed);
}
-
+
private void on_folders_available_unavailable(Gee.Collection<Geary.Folder>? available,
Gee.Collection<Geary.Folder>? unavailable) {
if (available != null) {
@@ -63,20 +63,20 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
private async void append_new_email_async(Geary.SearchQuery query, Geary.Folder folder,
Gee.Collection<Geary.EmailIdentifier> ids, Cancellable? cancellable) throws Error {
int result_mutex_token = yield result_mutex.claim_async();
-
+
Error? error = null;
try {
yield do_search_async(query, ids, null, cancellable);
} catch(Error e) {
error = e;
}
-
+
result_mutex.release(ref result_mutex_token);
-
+
if (error != null)
throw error;
}
-
+
private void on_append_new_email_complete(Object? source, AsyncResult result) {
try {
append_new_email_async.end(result);
@@ -84,17 +84,17 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
debug("Error appending new email to search results: %s", e.message);
}
}
-
+
private void on_email_locally_complete(Geary.Folder folder,
Gee.Collection<Geary.EmailIdentifier> ids) {
if (search_query != null)
append_new_email_async.begin(search_query, folder, ids, null, on_append_new_email_complete);
}
-
+
private async void handle_removed_email_async(Geary.SearchQuery query, Geary.Folder folder,
Gee.Collection<Geary.EmailIdentifier> ids, Cancellable? cancellable) throws Error {
int result_mutex_token = yield result_mutex.claim_async();
-
+
Error? error = null;
try {
Gee.ArrayList<ImapDB.SearchEmailIdentifier> relevant_ids
@@ -102,19 +102,19 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
.map_nonnull<ImapDB.SearchEmailIdentifier>(
id => ImapDB.SearchEmailIdentifier.collection_get_email_identifier(search_results, id))
.to_array_list();
-
+
if (relevant_ids.size > 0)
yield do_search_async(query, null, relevant_ids, cancellable);
} catch(Error e) {
error = e;
}
-
+
result_mutex.release(ref result_mutex_token);
-
+
if (error != null)
throw error;
}
-
+
private void on_handle_removed_email_complete(Object? source, AsyncResult result) {
try {
handle_removed_email_async.end(result);
@@ -122,13 +122,13 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
debug("Error removing removed email from search results: %s", e.message);
}
}
-
+
private void on_account_email_removed(Geary.Folder folder,
Gee.Collection<Geary.EmailIdentifier> ids) {
if (search_query != null)
handle_removed_email_async.begin(search_query, folder, ids, null,
on_handle_removed_email_complete);
}
-
+
/**
* Clears the search query and results.
*/
@@ -137,20 +137,20 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
clear_search_results();
notify_email_removed(local_results);
notify_email_count_changed(0, Geary.Folder.CountChangeReason.REMOVED);
-
+
if (search_query != null) {
search_query = null;
notify_search_query_changed(null);
}
}
-
+
/**
* Sets the keyword string for this search.
*/
public override void search(string query, Geary.SearchQuery.Strategy strategy, Cancellable? cancellable
= null) {
set_search_query_async.begin(query, strategy, cancellable, on_set_search_query_complete);
}
-
+
private void on_set_search_query_complete(Object? source, AsyncResult result) {
try {
set_search_query_async.end(result);
@@ -158,29 +158,29 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
debug("Search error: %s", e.message);
}
}
-
+
private async void set_search_query_async(string query, Geary.SearchQuery.Strategy strategy,
Cancellable? cancellable) throws Error {
Geary.SearchQuery search_query = account.open_search(query, strategy);
-
+
int result_mutex_token = yield result_mutex.claim_async();
-
+
Error? error = null;
try {
yield do_search_async(search_query, null, null, cancellable);
} catch(Error e) {
error = e;
}
-
+
result_mutex.release(ref result_mutex_token);
-
+
this.search_query = search_query;
notify_search_query_changed(search_query);
-
+
if (error != null)
throw error;
}
-
+
// NOTE: you must call this ONLY after locking result_mutex_token.
// If both *_ids parameters are null, the results of this search are
// considered to be the full new set. If non-null, the results are
@@ -194,7 +194,7 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
// remove_ids is null, and 3) remove from result set, where just
// add_ids is null. We can't add and remove at the same time.
assert(add_ids == null || remove_ids == null);
-
+
// TODO: don't limit this to MAX_RESULT_EMAILS. Instead, we could be
// smarter about only fetching the search results in list_email_async()
// etc., but this leads to some more complications when redoing the
@@ -202,12 +202,12 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
Gee.ArrayList<ImapDB.SearchEmailIdentifier> results
= ImapDB.SearchEmailIdentifier.array_list_from_results(yield account.local_search_async(
query, MAX_RESULT_EMAILS, 0, exclude_folders, add_ids ?? remove_ids, cancellable));
-
+
Gee.List<ImapDB.SearchEmailIdentifier> added
= Gee.List.empty<ImapDB.SearchEmailIdentifier>();
Gee.List<ImapDB.SearchEmailIdentifier> removed
= Gee.List.empty<ImapDB.SearchEmailIdentifier>();
-
+
if (remove_ids == null) {
added = Geary.traverse<ImapDB.SearchEmailIdentifier>(results)
.filter(id => !(id in search_results))
@@ -218,17 +218,17 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
.filter(id => !(id in results))
.to_array_list();
}
-
+
search_results.remove_all(removed);
search_results.add_all(added);
-
+
((ImapDB.SearchFolderProperties) properties).set_total(search_results.size);
-
+
// Note that we probably shouldn't be firing these signals from inside
// our mutex lock. Keep an eye on it, and if there's ever a case where
// it might cause problems, it shouldn't be too hard to move the
// firings outside.
-
+
Geary.Folder.CountChangeReason reason = CountChangeReason.NONE;
if (added.size > 0) {
// TODO: we'd like to be able to use APPENDED here when applicable,
@@ -245,16 +245,16 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
if (reason != CountChangeReason.NONE)
notify_email_count_changed(search_results.size, reason);
}
-
+
public override async Gee.List<Geary.Email>? list_email_by_id_async(Geary.EmailIdentifier? initial_id,
int count, Geary.Email.Field required_fields, Geary.Folder.ListFlags flags, Cancellable? cancellable
= null)
throws Error {
if (count <= 0)
return null;
-
+
// TODO: as above, this is incomplete and inefficient.
int result_mutex_token = yield result_mutex.claim_async();
-
+
Geary.EmailIdentifier[] ids = new Geary.EmailIdentifier[search_results.size];
int initial_index = 0;
int i = 0;
@@ -263,10 +263,10 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
initial_index = i;
ids[i++] = id;
}
-
+
if (initial_id == null && flags.is_all_set(Geary.Folder.ListFlags.OLDEST_TO_NEWEST))
initial_index = ids.length - 1;
-
+
Gee.List<Geary.Email> results = new Gee.ArrayList<Geary.Email>();
Error? fetch_err = null;
if (initial_index >= 0) {
@@ -275,7 +275,7 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
if (!flags.is_including_id() && initial_id != null)
i += increment;
int end = i + (count * increment);
-
+
for (; i >= 0 && i < search_results.size && i != end; i += increment) {
try {
results.add(yield fetch_email_async(ids[i], required_fields, flags, cancellable));
@@ -284,21 +284,21 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
// different symantics from fetch
if (!(err is EngineError.NOT_FOUND) && !(err is EngineError.INCOMPLETE_MESSAGE)) {
fetch_err = err;
-
+
break;
}
}
}
}
-
+
result_mutex.release(ref result_mutex_token);
-
+
if (fetch_err != null)
throw fetch_err;
-
+
return (results.size == 0 ? null : results);
}
-
+
public override async Gee.List<Geary.Email>? list_email_by_sparse_id_async(
Gee.Collection<Geary.EmailIdentifier> ids, Geary.Email.Field required_fields,
Geary.Folder.ListFlags flags, Cancellable? cancellable = null) throws Error {
@@ -306,17 +306,17 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
Gee.List<Geary.Email> result = new Gee.ArrayList<Geary.Email>();
foreach(Geary.EmailIdentifier id in ids)
result.add(yield fetch_email_async(id, required_fields, flags, cancellable));
-
+
return (result.size == 0 ? null : result);
}
-
+
public override async Gee.Map<Geary.EmailIdentifier, Geary.Email.Field>? list_local_email_fields_async(
Gee.Collection<Geary.EmailIdentifier> ids, Cancellable? cancellable = null) throws Error {
// TODO: This method is not currently called, but is required by the interface. Before completing
- // this feature, it should either be implemented either here or in AbstractLocalFolder.
+ // this feature, it should either be implemented either here or in AbstractLocalFolder.
error("Search folder does not implement list_local_email_fields_async");
}
-
+
public override async Geary.Email fetch_email_async(Geary.EmailIdentifier id,
Geary.Email.Field required_fields, Geary.Folder.ListFlags flags,
Cancellable? cancellable = null) throws Error {
@@ -331,19 +331,19 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
= yield account.get_containing_folders_async(email_ids, cancellable);
if (ids_to_folders == null)
return;
-
+
Gee.MultiMap<Geary.FolderPath, Geary.EmailIdentifier> folders_to_ids
= Geary.Collection.reverse_multi_map<Geary.EmailIdentifier, Geary.FolderPath>(ids_to_folders);
-
+
foreach (Geary.FolderPath path in folders_to_ids.get_keys()) {
Geary.Folder folder = yield account.fetch_folder_async(path, cancellable);
Geary.FolderSupport.Remove? remove = folder as Geary.FolderSupport.Remove;
if (remove == null)
continue;
-
+
Gee.Collection<Geary.EmailIdentifier> ids = folders_to_ids.get(path);
assert(ids.size > 0);
-
+
debug("Search folder removing %d emails from %s", ids.size, folder.to_string());
bool open = false;
@@ -374,18 +374,18 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
Gee.Collection<Geary.EmailIdentifier> ids, Cancellable? cancellable = null) throws Error {
if (search_query == null)
return null;
-
+
return yield account.get_search_matches_async(search_query, ids, cancellable);
}
-
+
private void exclude_folder(Geary.Folder folder) {
exclude_folders.add(folder.path);
}
-
+
private void exclude_orphan_emails() {
exclude_folders.add(null);
}
-
+
private void clear_search_results() {
search_results = new Gee.TreeSet<ImapDB.SearchEmailIdentifier>(
ImapDB.SearchEmailIdentifier.compare_descending);
diff --git a/src/engine/imap-db/search/imap-db-search-query.vala
b/src/engine/imap-db/search/imap-db-search-query.vala
index 0218500f..5e43eac4 100644
--- a/src/engine/imap-db/search/imap-db-search-query.vala
+++ b/src/engine/imap-db/search/imap-db-search-query.vala
@@ -75,9 +75,9 @@ private class Geary.ImapDB.SearchQuery : Geary.SearchQuery {
public SearchQuery(ImapDB.Account account, string query, Geary.SearchQuery.Strategy strategy) {
base (query, strategy);
-
+
this.account = account;
-
+
switch (strategy) {
case Strategy.EXACT:
allow_stemming = false;
@@ -85,49 +85,49 @@ private class Geary.ImapDB.SearchQuery : Geary.SearchQuery {
max_difference_term_stem_lengths = 0;
max_difference_match_stem_lengths = 0;
break;
-
+
case Strategy.CONSERVATIVE:
allow_stemming = true;
min_term_length_for_stemming = 6;
max_difference_term_stem_lengths = 2;
max_difference_match_stem_lengths = 2;
break;
-
+
case Strategy.AGGRESSIVE:
allow_stemming = true;
min_term_length_for_stemming = 4;
max_difference_term_stem_lengths = 4;
max_difference_match_stem_lengths = 3;
break;
-
+
case Strategy.HORIZON:
allow_stemming = true;
min_term_length_for_stemming = 0;
max_difference_term_stem_lengths = int.MAX;
max_difference_match_stem_lengths = int.MAX;
break;
-
+
default:
assert_not_reached();
}
}
-
+
public void add_search_term(string? field, SearchTerm term) {
if (!field_map.has_key(field))
field_map.set(field, new Gee.ArrayList<SearchTerm>());
-
+
field_map.get(field).add(term);
all.add(term);
}
-
+
public Gee.Collection<string?> get_fields() {
return field_map.keys;
}
-
+
public Gee.List<SearchTerm>? get_search_terms(string? field) {
return field_map.has_key(field) ? field_map.get(field) : null;
}
-
+
public Gee.List<SearchTerm>? get_all_terms() {
return all;
}
diff --git a/src/engine/imap-db/search/imap-db-search-term.vala
b/src/engine/imap-db/search/imap-db-search-term.vala
index 4d2e4400..8427b25f 100644
--- a/src/engine/imap-db/search/imap-db-search-term.vala
+++ b/src/engine/imap-db/search/imap-db-search-term.vala
@@ -15,14 +15,14 @@ private class Geary.ImapDB.SearchTerm : BaseObject {
* For example, punctuation might be removed, but no casefolding has occurred.
*/
public string original { get; private set; }
-
+
/**
* The parsed tokenized search term.
*
* Casefolding and other normalizing text operations have been performed.
*/
public string parsed { get; private set; }
-
+
/**
* The stemmed search term.
*
@@ -30,7 +30,7 @@ private class Geary.ImapDB.SearchTerm : BaseObject {
* term.
*/
public string? stemmed { get; private set; }
-
+
/**
* A list of terms ready for binding to an SQLite statement.
*
@@ -38,23 +38,23 @@ private class Geary.ImapDB.SearchTerm : BaseObject {
* are guaranteed not to be null or empty strings.
*/
public Gee.List<string> sql { get; private set; default = new Gee.ArrayList<string>(); }
-
+
/**
* Returns true if the {@link parsed} term is exact-match only (i.e. starts with quotes) and
* there is no {@link stemmed} variant.
*/
public bool is_exact { get { return parsed.has_prefix("\"") && stemmed == null; } }
-
+
public SearchTerm(string original, string parsed, string? stemmed, string? sql_parsed, string?
sql_stemmed) {
this.original = original;
this.parsed = parsed;
this.stemmed = stemmed;
-
+
// for now, only two variations: the parsed string and the stemmed; since stem is usually
// shorter (and will be first in the OR statement), include it first
if (!String.is_empty(sql_stemmed))
sql.add(sql_stemmed);
-
+
if (!String.is_empty(sql_parsed))
sql.add(sql_parsed);
}
diff --git a/src/engine/imap-engine/gmail/imap-engine-gmail-folder.vala
b/src/engine/imap-engine/gmail/imap-engine-gmail-folder.vala
index dc8eb3fe..bddcb9d9 100644
--- a/src/engine/imap-engine/gmail/imap-engine-gmail-folder.vala
+++ b/src/engine/imap-engine/gmail/imap-engine-gmail-folder.vala
@@ -29,12 +29,12 @@ private class Geary.ImapEngine.GmailFolder : MinimalFolder, FolderSupport.Archiv
Geary.Folder? all_mail = account.get_special_folder(Geary.SpecialFolderType.ALL_MAIL);
if (all_mail != null)
return yield move_email_async(email_ids, all_mail.path, cancellable);
-
+
// although this shouldn't happen, fall back on our traditional archive, which is simply
// to remove the message from this label
message("%s: Unable to perform revokable archive: All Mail not found", to_string());
yield expunge_email_async(email_ids, cancellable);
-
+
return null;
}
@@ -63,14 +63,14 @@ private class Geary.ImapEngine.GmailFolder : MinimalFolder, FolderSupport.Archiv
Geary.Folder? trash = folder.account.get_special_folder(SpecialFolderType.TRASH);
if (trash == null)
throw new EngineError.NOT_FOUND("%s: Trash folder not found for removal", folder.to_string());
-
+
// Copy to Trash, collect UIDs (note that copying to Trash is like a move; the copied
// messages are removed from all labels)
Gee.Set<Imap.UID>? uids = yield folder.copy_email_uids_async(email_ids, trash.path, cancellable);
if (uids == null || uids.size == 0) {
debug("%s: Can't true-remove %d emails, no COPYUIDs returned", folder.to_string(),
email_ids.size);
-
+
return;
}
diff --git a/src/engine/imap-engine/imap-engine-contact-store.vala
b/src/engine/imap-engine/imap-engine-contact-store.vala
index 1687aec1..75cb618c 100644
--- a/src/engine/imap-engine/imap-engine-contact-store.vala
+++ b/src/engine/imap-engine/imap-engine-contact-store.vala
@@ -3,26 +3,26 @@
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
-
+
internal class Geary.ImapEngine.ContactStore : Geary.ContactStore {
private weak ImapDB.Account account;
-
+
internal ContactStore(ImapDB.Account account) {
this.account = account;
}
-
+
public override async void mark_contacts_async(Gee.Collection<Contact> contacts, ContactFlags? to_add,
ContactFlags? to_remove) throws Error{
foreach (Contact contact in contacts) {
if (contact.contact_flags == null)
contact.contact_flags = new Geary.ContactFlags();
-
+
if (to_add != null)
contact.contact_flags.add_all(to_add);
-
+
if (to_remove != null)
contact.contact_flags.remove_all(to_remove);
-
+
yield account.update_contact_flags_async(contact, null);
}
}
diff --git a/src/engine/imap-engine/imap-engine-email-prefetcher.vala
b/src/engine/imap-engine/imap-engine-email-prefetcher.vala
index 919843ef..2bffda31 100644
--- a/src/engine/imap-engine/imap-engine-email-prefetcher.vala
+++ b/src/engine/imap-engine/imap-engine-email-prefetcher.vala
@@ -13,10 +13,10 @@
*/
private class Geary.ImapEngine.EmailPrefetcher : Geary.BaseObject {
public const int PREFETCH_DELAY_SEC = 1;
-
+
private const Geary.Email.Field PREFETCH_FIELDS = Geary.Email.Field.ALL;
private const int PREFETCH_CHUNK_BYTES = 32 * 1024;
-
+
public Nonblocking.CountingSemaphore active_sem { get; private set;
default = new Nonblocking.CountingSemaphore(null); }
@@ -142,10 +142,10 @@ private class Geary.ImapEngine.EmailPrefetcher : Geary.BaseObject {
if (!(err is IOError.CANCELLED))
debug("Error while prefetching emails for %s: %s", folder.to_string(), err.message);
}
-
+
// this round is done
active_sem.blind_notify();
-
+
if (token != Nonblocking.Mutex.INVALID_TOKEN) {
try {
mutex.release(ref token);
@@ -154,15 +154,15 @@ private class Geary.ImapEngine.EmailPrefetcher : Geary.BaseObject {
}
}
}
-
+
private async void do_prefetch_batch_async() throws Error {
// snarf up all requested Emails for this round
Gee.TreeSet<Geary.Email> emails = prefetch_emails;
prefetch_emails = new Gee.TreeSet<Geary.Email>(Email.compare_recv_date_descending);
-
+
if (emails.size == 0)
return;
-
+
debug("do_prefetch_batch_async %s start_total=%d", folder.to_string(), emails.size);
// Big TODO: The engine needs to be able to synthesize
@@ -177,52 +177,52 @@ private class Geary.ImapEngine.EmailPrefetcher : Geary.BaseObject {
Gee.HashSet<Geary.EmailIdentifier> ids = new Gee.HashSet<Geary.EmailIdentifier>();
int64 chunk_bytes = 0;
int count = 0;
-
+
while (emails.size > 0) {
// dequeue emails by date received, newest to oldest
Geary.Email email = emails.first();
-
+
// only add to this chunk if the email is smaller than one chunk or there's nothing
// in this chunk so far ... this means an oversized email will be pulled all by itself
// in the next round if there's stuff already ahead of it
if (email.properties.total_bytes < PREFETCH_CHUNK_BYTES || ids.size == 0) {
bool removed = emails.remove(email);
assert(removed);
-
+
ids.add(email.id);
chunk_bytes += email.properties.total_bytes;
count++;
-
+
// if not enough stuff is in this chunk, keep going
if (chunk_bytes < PREFETCH_CHUNK_BYTES)
continue;
}
-
+
bool keep_going = yield do_prefetch_email_async(ids, chunk_bytes);
-
+
// clear out for next chunk ... this also prevents the final prefetch_async() from trying
// to pull twice if !keep_going
ids.clear();
chunk_bytes = 0;
-
+
if (!keep_going)
break;
-
+
yield Scheduler.sleep_ms_async(200);
}
-
+
// get any remaining
if (ids.size > 0)
yield do_prefetch_email_async(ids, chunk_bytes);
-
+
debug("finished do_prefetch_batch_async %s end_total=%d", folder.to_string(), count);
}
-
+
// Return true to continue, false to stop prefetching (cancelled or not open)
private async bool do_prefetch_email_async(Gee.Collection<Geary.EmailIdentifier> ids, int64 chunk_bytes)
{
debug("do_prefetch_email_async: %s prefetching %d emails (%sb)", folder.to_string(),
ids.size, chunk_bytes.to_string());
-
+
try {
yield folder.list_email_by_sparse_id_async(ids, PREFETCH_FIELDS, Folder.ListFlags.NONE,
cancellable);
@@ -236,7 +236,7 @@ private class Geary.ImapEngine.EmailPrefetcher : Geary.BaseObject {
return false;
}
}
-
+
return true;
}
}
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala
b/src/engine/imap-engine/imap-engine-generic-account.vala
index 165254d0..b9b36fff 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -246,9 +246,9 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
public override async void rebuild_async(Cancellable? cancellable = null) throws Error {
if (open)
throw new EngineError.ALREADY_OPEN("Account cannot be open during rebuild");
-
+
message("%s: Rebuilding account local data", to_string());
-
+
// get all the storage locations associated with this Account
File db_file;
File attachments_dir;
@@ -434,7 +434,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
Gee.HashSet<Geary.Folder> all_folders = new Gee.HashSet<Geary.Folder>();
all_folders.add_all(folder_map.values);
all_folders.add_all(local_only.values);
-
+
return all_folders;
}
@@ -510,25 +510,25 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
private void on_email_sent(Geary.RFC822.Message rfc822) {
notify_email_sent(rfc822);
}
-
+
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());
-
+
return imapdb_id;
}
-
+
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());
}
-
+
return (Gee.Collection<ImapDB.EmailIdentifier>) ids;
}
-
+
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,
@@ -536,25 +536,25 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
return yield local.search_message_id_async(
message_id, requested_fields, partial_ok, folder_blacklist, flag_blacklist, cancellable);
}
-
+
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);
}
-
+
public override Geary.SearchQuery open_search(string query, SearchQuery.Strategy strategy) {
return new ImapDB.SearchQuery(local, query, strategy);
}
-
+
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");
-
+
return yield local.search_async(query, limit, offset, folder_blacklist, search_ids, cancellable);
}
-
+
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);
diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala
b/src/engine/imap-engine/imap-engine-minimal-folder.vala
index d5827733..e6e4e3d7 100644
--- a/src/engine/imap-engine/imap-engine-minimal-folder.vala
+++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala
@@ -37,15 +37,15 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
public override Account account { get { return _account; } }
-
+
public override FolderProperties properties { get { return _properties; } }
-
+
public override FolderPath path {
get {
return local_folder.get_path();
}
}
-
+
private SpecialFolderType _special_folder_type;
public override SpecialFolderType special_folder_type {
get {
@@ -96,7 +96,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
* Note that this is ''not'' fired if the queue is not being flushed.
*/
public signal void closing(Gee.List<ReplayOperation> final_ops);
-
+
/**
* Fired when an {@link EmailIdentifier} that was marked for removal is actually reported as
* removed (expunged) from the server.
@@ -109,7 +109,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
* operation. They need to know if the message is removed by another client, however.
*/
public signal void marked_email_removed(Gee.Collection<Geary.EmailIdentifier> removed);
-
+
/** Emitted to notify the account that some problem has occurred. */
internal signal void report_problem(Geary.ProblemReport problem);
@@ -150,32 +150,32 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
protected virtual void notify_closing(Gee.List<ReplayOperation> final_ops) {
closing(final_ops);
}
-
+
/*
* These signal notifiers are marked public (note this is a private class) so the various
* ReplayOperations can directly fire the associated signals while within the queue.
*/
-
+
public void replay_notify_email_inserted(Gee.Collection<Geary.EmailIdentifier> ids) {
notify_email_inserted(ids);
}
-
+
public void replay_notify_email_locally_inserted(Gee.Collection<Geary.EmailIdentifier> ids) {
notify_email_locally_inserted(ids);
}
-
+
public void replay_notify_email_removed(Gee.Collection<Geary.EmailIdentifier> ids) {
notify_email_removed(ids);
}
-
+
public void replay_notify_email_count_changed(int new_count, Folder.CountChangeReason reason) {
notify_email_count_changed(new_count, reason);
}
-
+
public void replay_notify_email_flags_changed(Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> flag_map)
{
notify_email_flags_changed(flag_map);
}
-
+
public void set_special_folder_type(SpecialFolderType new_type) {
SpecialFolderType old_type = _special_folder_type;
_special_folder_type = new_type;
@@ -279,9 +279,9 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
private async void detach_all_emails_async(Cancellable? cancellable) throws Error {
Gee.List<Email>? all = yield local_folder.list_email_by_id_async(null, -1,
Geary.Email.Field.NONE, ImapDB.Folder.ListFlags.NONE, cancellable);
-
+
yield local_folder.detach_all_emails_async(cancellable);
-
+
if (all != null && all.size > 0) {
Gee.List<EmailIdentifier> ids =
traverse<Email>(all).map<EmailIdentifier>((email) => email.id).to_array_list();
@@ -356,7 +356,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
// any flags that may have changed
ImapDB.EmailIdentifier? local_earliest_id = yield local_folder.get_earliest_id_async(cancellable);
ImapDB.EmailIdentifier? local_latest_id = yield local_folder.get_latest_id_async(cancellable);
-
+
// verify still open; this is required throughout after each yield, as a close_async() can
// come in ay any time since this does not run in the context of open_async()
check_open("normalize_folders (local earliest/latest UID)");
@@ -389,7 +389,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
// a full normalize works from the highest possible UID on the remote and work down to the lowest
UID on
// the local; this covers all messages appended since last seen as well as any removed
Imap.UID last_uid = remote_properties.uid_next.previous(true);
-
+
// if either local UID is out of range of the current highest UID, then something very wrong
// has occurred; the only recourse is to wipe all associations and start over
if (local_earliest_id.uid.compare_to(last_uid) > 0 || local_latest_id.uid.compare_to(last_uid) > 0) {
@@ -406,7 +406,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
// if UIDNEXT has changed, that indicates messages have been appended (and possibly removed)
int64 uidnext_diff = remote_properties.uid_next.value - local_properties.uid_next.value;
-
+
int local_message_count = (local_properties.select_examine_messages >= 0)
? local_properties.select_examine_messages : 0;
int remote_message_count = (remote_properties.select_examine_messages >= 0)
@@ -430,44 +430,44 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
Imap.UID first_uid;
if (!is_dirty && uidnext_diff == (remote_message_count - local_message_count)) {
first_uid = local_latest_id.uid.next(true);
-
+
debug("%s: Messages only appended (local/remote UIDNEXT=%s/%s total=%d/%d diff=%s), gathering
mail UIDs %s:%s",
to_string(), local_properties.uid_next.to_string(), remote_properties.uid_next.to_string(),
local_properties.select_examine_messages, remote_properties.select_examine_messages,
uidnext_diff.to_string(),
first_uid.to_string(), last_uid.to_string());
} else {
first_uid = local_earliest_id.uid;
-
+
debug("%s: Messages appended/removed (local/remote UIDNEXT=%s/%s total=%d/%d diff=%s), gathering
mail UIDs %s:%s",
to_string(), local_properties.uid_next.to_string(), remote_properties.uid_next.to_string(),
local_properties.select_examine_messages, remote_properties.select_examine_messages,
uidnext_diff.to_string(),
first_uid.to_string(), last_uid.to_string());
}
-
+
// get all the UIDs in said range from the local store, sorted; convert to non-null
// for ease of use later
Gee.Set<Imap.UID>? local_uids = yield local_folder.list_uids_by_range_async(
first_uid, last_uid, true, cancellable);
if (local_uids == null)
local_uids = new Gee.HashSet<Imap.UID>();
-
+
check_open("normalize_folders (list local)");
-
+
// Do the same on the remote ... make non-null for ease of use later
Gee.Set<Imap.UID>? remote_uids = yield session.list_uids_async(
new Imap.MessageSet.uid_range(first_uid, last_uid), cancellable);
if (remote_uids == null)
remote_uids = new Gee.HashSet<Imap.UID>();
-
+
check_open("normalize_folders (list remote)");
-
+
debug("%s: Loaded local (%d) and remote (%d) UIDs, normalizing...", to_string(),
local_uids.size, remote_uids.size);
-
+
Gee.HashSet<Imap.UID> removed_uids = new Gee.HashSet<Imap.UID>();
Gee.HashSet<Imap.UID> appended_uids = new Gee.HashSet<Imap.UID>();
Gee.HashSet<Imap.UID> inserted_uids = new Gee.HashSet<Imap.UID>();
-
+
// Because the number of UIDs being processed can be immense in large folders, process
// in a background thread
yield Nonblocking.Concurrent.global.schedule_async(() => {
@@ -478,7 +478,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
if (!remote_uids.remove(local_uid))
removed_uids.add(local_uid);
}
-
+
// everything remaining in remote has been added since folder last seen ... whether they're
// discovered (inserted) or appended depends on the highest local UID
foreach (Imap.UID remote_uid in remote_uids) {
@@ -487,19 +487,19 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
else
inserted_uids.add(remote_uid);
}
-
+
// the UIDs marked for removal are going to be re-inserted into the vector once they're
// cleared, so add them here as well
if (already_marked_ids != null) {
foreach (ImapDB.EmailIdentifier id in already_marked_ids) {
assert(id.has_uid());
-
+
if (!appended_uids.contains(id.uid))
inserted_uids.add(id.uid);
}
}
}, cancellable);
-
+
debug("%s: changes since last seen: removed=%d appended=%d inserted=%d", to_string(),
removed_uids.size, appended_uids.size, inserted_uids.size);
@@ -521,9 +521,9 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
to_create.add_all(list);
}
}
-
+
check_open("normalize_folders (list remote appended/inserted required fields)");
-
+
// store new messages and add IDs to the appended/discovered EmailIdentifier buckets
Gee.Set<ImapDB.EmailIdentifier> appended_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
Gee.Set<ImapDB.EmailIdentifier> locally_appended_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
@@ -545,28 +545,28 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
foreach (Email email in created_or_merged.keys) {
ImapDB.EmailIdentifier id = (ImapDB.EmailIdentifier) email.id;
bool created = created_or_merged.get(email);
-
+
// report all appended email, but separate out email never seen before (created)
// as locally-appended
if (appended_uids.contains(id.uid)) {
appended_ids.add(id);
-
+
if (created)
locally_appended_ids.add(id);
} else if (inserted_uids.contains(id.uid)) {
inserted_ids.add(id);
-
+
if (created)
locally_inserted_ids.add(id);
}
}
}, cancellable);
-
+
debug("%s: Finished creating/merging %d emails", to_string(), created_or_merged.size);
}
-
+
check_open("normalize_folders (created/merged appended/inserted emails)");
-
+
// Convert removed UIDs into EmailIdentifiers and detach immediately
Gee.Set<ImapDB.EmailIdentifier>? removed_ids = null;
if (removed_uids.size > 0) {
@@ -576,9 +576,9 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
yield local_folder.detach_multiple_emails_async(removed_ids, cancellable);
}
}
-
+
check_open("normalize_folders (removed emails)");
-
+
// remove any extant remove markers, as everything is accounted for now, except for those
// waiting to be removed in the queue
yield local_folder.clear_remove_markers_async(to_be_removed, cancellable);
@@ -593,55 +593,55 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
*/
Folder.CountChangeReason count_change_reason = Folder.CountChangeReason.NONE;
-
+
if (removed_ids != null && removed_ids.size > 0) {
// there may be operations pending on the remote queue for these removed emails; notify
// operations that the email has shuffled off this mortal coil
replay_queue.notify_remote_removed_ids(removed_ids);
-
+
// notify subscribers about emails that have been removed
debug("%s: Notifying of %d removed emails since last opened", to_string(), removed_ids.size);
notify_email_removed(removed_ids);
-
+
count_change_reason |= Folder.CountChangeReason.REMOVED;
}
-
+
// notify inserted (new email located somewhere inside the local vector)
if (inserted_ids.size > 0) {
debug("%s: Notifying of %d inserted emails since last opened", to_string(), inserted_ids.size);
notify_email_inserted(inserted_ids);
-
+
count_change_reason |= Folder.CountChangeReason.INSERTED;
}
-
+
// notify inserted (new email located somewhere inside the local vector that had to be
// created, i.e. no portion was stored locally)
if (locally_inserted_ids.size > 0) {
debug("%s: Notifying of %d locally inserted emails since last opened", to_string(),
locally_inserted_ids.size);
notify_email_locally_inserted(locally_inserted_ids);
-
+
count_change_reason |= Folder.CountChangeReason.INSERTED;
}
-
+
// notify appended (new email added since the folder was last opened)
if (appended_ids.size > 0) {
debug("%s: Notifying of %d appended emails since last opened", to_string(), appended_ids.size);
notify_email_appended(appended_ids);
-
+
count_change_reason |= Folder.CountChangeReason.APPENDED;
}
-
+
// notify locally appended (new email never seen before added since the folder was last
// opened)
if (locally_appended_ids.size > 0) {
debug("%s: Notifying of %d locally appended emails since last opened", to_string(),
locally_appended_ids.size);
notify_email_locally_appended(locally_appended_ids);
-
+
count_change_reason |= Folder.CountChangeReason.APPENDED;
}
-
+
if (count_change_reason != Folder.CountChangeReason.NONE) {
debug("%s: Notifying of %Xh count change reason (%d remote messages)", to_string(),
count_change_reason, remote_message_count);
@@ -1132,7 +1132,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
//
// list email variants
//
-
+
public override async Gee.List<Geary.Email>? list_email_by_id_async(Geary.EmailIdentifier? initial_id,
int count, Geary.Email.Field required_fields, Folder.ListFlags flags,
Cancellable? cancellable = null) throws Error {
@@ -1140,50 +1140,50 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
check_flags("list_email_by_id_async", flags);
if (initial_id != null)
check_id("list_email_by_id_async", initial_id);
-
+
if (count == 0)
return null;
-
+
// Schedule list operation and wait for completion.
ListEmailByID op = new ListEmailByID(this, (ImapDB.EmailIdentifier) initial_id, count,
required_fields, flags, cancellable);
replay_queue.schedule(op);
-
+
yield op.wait_for_ready_async(cancellable);
-
+
return !op.accumulator.is_empty ? op.accumulator : null;
}
-
+
public async override Gee.List<Geary.Email>? list_email_by_sparse_id_async(
Gee.Collection<Geary.EmailIdentifier> ids, Geary.Email.Field required_fields, Folder.ListFlags flags,
Cancellable? cancellable = null) throws Error {
check_open("list_email_by_sparse_id_async");
check_flags("list_email_by_sparse_id_async", flags);
check_ids("list_email_by_sparse_id_async", ids);
-
+
if (ids.size == 0)
return null;
-
+
// Schedule list operation and wait for completion.
// TODO: Break up requests to avoid hogging the queue
ListEmailBySparseID op = new ListEmailBySparseID(this, (Gee.Collection<ImapDB.EmailIdentifier>) ids,
required_fields, flags, cancellable);
replay_queue.schedule(op);
-
+
yield op.wait_for_ready_async(cancellable);
-
+
return !op.accumulator.is_empty ? op.accumulator : null;
}
-
+
public override async Gee.Map<Geary.EmailIdentifier, Geary.Email.Field>? list_local_email_fields_async(
Gee.Collection<Geary.EmailIdentifier> ids, Cancellable? cancellable = null) throws Error {
check_open("list_local_email_fields_async");
check_ids("list_local_email_fields_async", ids);
-
+
return yield local_folder.list_email_fields_by_id_async(
(Gee.Collection<Geary.ImapDB.EmailIdentifier>) ids, ImapDB.Folder.ListFlags.NONE, cancellable);
}
-
+
public override async Geary.Email fetch_email_async(Geary.EmailIdentifier id,
Geary.Email.Field required_fields, Geary.Folder.ListFlags flags, Cancellable? cancellable = null)
throws Error {
@@ -1217,36 +1217,36 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
RemoveEmail remove = new RemoveEmail(this, (Gee.List<ImapDB.EmailIdentifier>) email_ids,
cancellable);
replay_queue.schedule(remove);
-
+
yield remove.wait_for_ready_async(cancellable);
}
-
+
protected async void expunge_all_async(Cancellable? cancellable = null) throws Error {
check_open("expunge_all_async");
-
+
EmptyFolder empty_folder = new EmptyFolder(this, cancellable);
replay_queue.schedule(empty_folder);
-
+
yield empty_folder.wait_for_ready_async(cancellable);
}
-
+
private void check_open(string method) throws EngineError {
if (open_count == 0)
throw new EngineError.OPEN_REQUIRED("%s failed: folder %s is not open", method, to_string());
}
-
+
private void check_flags(string method, Folder.ListFlags flags) throws EngineError {
if (flags.is_all_set(Folder.ListFlags.LOCAL_ONLY) &&
flags.is_all_set(Folder.ListFlags.FORCE_UPDATE)) {
throw new EngineError.BAD_PARAMETERS("%s %s failed: LOCAL_ONLY and FORCE_UPDATE are mutually
exclusive",
to_string(), method);
}
}
-
+
private void check_id(string method, EmailIdentifier id) throws EngineError {
if (!(id is ImapDB.EmailIdentifier))
throw new EngineError.BAD_PARAMETERS("Email ID %s is not IMAP Email ID", id.to_string());
}
-
+
private void check_ids(string method, Gee.Collection<EmailIdentifier> ids) throws EngineError {
foreach (EmailIdentifier id in ids)
check_id(method, id);
@@ -1291,12 +1291,12 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
// watch for copying to this folder, which is treated as a no-op
if (destination.equal_to(path))
return null;
-
+
CopyEmail copy = new CopyEmail(this, (Gee.List<ImapDB.EmailIdentifier>) to_copy, destination);
replay_queue.schedule(copy);
-
+
yield copy.wait_for_ready_async(cancellable);
-
+
return copy.destination_uids.size > 0 ? copy.destination_uids : null;
}
@@ -1311,13 +1311,13 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
// watch for moving to this folder, which is treated as a no-op
if (destination.equal_to(path))
return null;
-
+
MoveEmailPrepare prepare = new MoveEmailPrepare(this, (Gee.List<ImapDB.EmailIdentifier>) to_move,
cancellable);
replay_queue.schedule(prepare);
-
+
yield prepare.wait_for_ready_async(cancellable);
-
+
if (prepare.prepared_for_move == null || prepare.prepared_for_move.size == 0)
return null;
@@ -1329,10 +1329,10 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
public void schedule_op(ReplayOperation op) throws Error {
check_open("schedule_op");
-
+
replay_queue.schedule(op);
}
-
+
public async void exec_op_async(ReplayOperation op, Cancellable? cancellable) throws Error {
schedule_op(op);
yield op.wait_for_ready_async(cancellable);
@@ -1363,10 +1363,10 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
check_open("find_earliest_email_async");
if (before_id != null)
check_id("find_earliest_email_async", before_id);
-
+
Imap.SearchCriteria criteria = new Imap.SearchCriteria();
criteria.is_(Imap.SearchCriterion.since_internaldate(new
Imap.InternalDate.from_date_time(datetime)));
-
+
// if before_id available, only search for messages before it
if (before_id != null) {
Imap.UID? before_uid = yield local_folder.get_uid_async((ImapDB.EmailIdentifier) before_id,
@@ -1375,19 +1375,19 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
throw new EngineError.NOT_FOUND("before_id %s not found in %s", before_id.to_string(),
to_string());
}
-
+
criteria.and(Imap.SearchCriterion.message_set(
new Imap.MessageSet.uid_range(new Imap.UID(Imap.UID.MIN), before_uid.previous(true))));
}
ServerSearchEmail op = new ServerSearchEmail(this, criteria, Geary.Email.Field.NONE,
cancellable);
-
+
// need to check again due to the yield in the above conditional block
check_open("find_earliest_email_async.schedule operation");
-
+
replay_queue.schedule(op);
-
+
yield op.wait_for_ready_async(cancellable);
// find earliest ID; because all Email comes from Folder, UID should always be present
@@ -1409,14 +1409,14 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
check_open("create_email_async");
if (id != null)
check_id("create_email_async", id);
-
+
Error? cancel_error = null;
Geary.EmailIdentifier? ret = null;
try {
CreateEmail create = new CreateEmail(this, rfc822, flags, date_received, cancellable);
replay_queue.schedule(create);
yield create.wait_for_ready_async(cancellable);
-
+
ret = create.created_id;
} catch (Error e) {
if (e is IOError.CANCELLED)
@@ -1424,17 +1424,17 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
else
throw e;
}
-
+
Geary.FolderSupport.Remove? remove_folder = this as Geary.FolderSupport.Remove;
-
+
// Remove old message.
if (id != null && remove_folder != null)
yield remove_folder.remove_email_async(iterate<EmailIdentifier>(id).to_array_list());
-
+
// If the user cancelled the operation, throw the error here.
if (cancel_error != null)
throw cancel_error;
-
+
// If the caller cancelled during the remove operation, delete the newly created message to
// safely back out.
if (cancellable != null && cancellable.is_cancelled() && ret != null && remove_folder != null)
diff --git a/src/engine/imap-engine/imap-engine-replay-operation.vala
b/src/engine/imap-engine/imap-engine-replay-operation.vala
index ab2d891d..a410e64c 100644
--- a/src/engine/imap-engine/imap-engine-replay-operation.vala
+++ b/src/engine/imap-engine/imap-engine-replay-operation.vala
@@ -52,9 +52,9 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject, Gee.
public int remote_retry_count { get; set; default = 0; }
public Error? err { get; private set; default = null; }
public bool notified { get { return semaphore.can_pass; } }
-
+
private Nonblocking.Semaphore semaphore = new Nonblocking.Semaphore();
-
+
public ReplayOperation(string name, Scope scope, OnError on_remote_error = OnError.THROW) {
this.name = name;
this.scope = scope;
@@ -169,38 +169,38 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject, Gee.
*/
public async void wait_for_ready_async(Cancellable? cancellable = null) throws Error {
yield semaphore.wait_async(cancellable);
-
+
if (err != null)
throw err;
}
-
+
// Can only be called once
internal void notify_ready(Error? err) {
assert(!semaphore.can_pass);
-
+
this.err = err;
-
+
try {
semaphore.notify();
} catch (Error notify_err) {
debug("Unable to notify replay operation as ready: [%s] %s", name, notify_err.message);
}
}
-
+
public abstract string describe_state();
-
+
// The Comparable interface is merely to ensure the ReplayQueue sorts operations by their
// submission order, ensuring that retry operations are retried in order of submissions
public int compare_to(ReplayOperation other) {
assert(submission_number >= 0);
assert(other.submission_number >= 0);
-
+
return (int) (submission_number - other.submission_number).clamp(-1, 1);
}
-
+
public string to_string() {
string state = describe_state();
-
+
return String.is_empty(state)
? "[%s] %s remote_retry_count=%d".printf(submission_number.to_string(), name, remote_retry_count)
: "[%s] %s: %s remote_retry_count=%d".printf(submission_number.to_string(), name, state,
diff --git a/src/engine/imap-engine/imap-engine-replay-queue.vala
b/src/engine/imap-engine/imap-engine-replay-queue.vala
index ca64bdc1..b95ebc32 100644
--- a/src/engine/imap-engine/imap-engine-replay-queue.vala
+++ b/src/engine/imap-engine/imap-engine-replay-queue.vala
@@ -59,15 +59,15 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
);
}
}
-
+
public int local_count { get {
return local_queue.size;
} }
-
+
public int remote_count { get {
return remote_queue.size;
} }
-
+
private weak MinimalFolder owner;
private Nonblocking.Queue<ReplayOperation> local_queue =
new Nonblocking.Queue<ReplayOperation>.fifo();
@@ -94,55 +94,55 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::locally-executing: %s", to_string(),
op.to_string());
}
-
+
public virtual signal void locally_executed(ReplayOperation op, bool continuing) {
Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::locally-executed: %s continuing=%s",
to_string(), op.to_string(), continuing.to_string());
}
-
+
public virtual signal void remotely_executing(ReplayOperation op) {
Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::remotely-executing: %s", to_string(),
op.to_string());
}
-
+
public virtual signal void remotely_executed(ReplayOperation op) {
Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::remotely-executed: %s", to_string(),
op.to_string());
}
-
+
public virtual signal void backing_out(ReplayOperation op, Error? err) {
Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::backout-out: %s err=%s",
to_string(), op.to_string(), (err != null) ? err.message : "(null)");
}
-
+
public virtual signal void backed_out(ReplayOperation op, Error? err) {
Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::backed-out: %s err=%s",
to_string(), op.to_string(), (err != null) ? err.message : "(null)");
}
-
+
public virtual signal void backout_failed(ReplayOperation op, Error? backout_err) {
Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::backout-failed: %s err=%s", to_string(),
op.to_string(), (backout_err != null) ? backout_err.message : "(null)");
}
-
+
public virtual signal void completed(ReplayOperation op) {
Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::completed: %s", to_string(),
op.to_string());
}
-
+
public virtual signal void failed(ReplayOperation op) {
Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::failed: %s", to_string(),
op.to_string());
}
-
+
public virtual signal void closing() {
Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::closing", to_string());
}
-
+
public virtual signal void closed() {
Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::closed", to_string());
}
-
+
/**
* ReplayQueue accepts a NonblockingReportingSemaphore<bool> which, when signaled, returns
* true if the remote folder is ready and open, false if not (closing or closed), and
@@ -152,17 +152,17 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
*/
public ReplayQueue(MinimalFolder owner) {
this.owner = owner;
-
+
// fire off background queue processors
do_replay_local_async.begin();
do_replay_remote_async.begin();
}
-
+
~ReplayQueue() {
if (notification_timer != null)
notification_timer.cancel();
}
-
+
/**
* Returns false if the operation was not schedule (queue already closed).
*/
@@ -171,14 +171,14 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
if (state != State.OPEN && !(op is CloseReplayQueue)) {
debug("Unable to schedule replay operation %s on %s: replay queue closed", op.to_string(),
to_string());
-
+
return false;
}
-
+
// assign a submission number to operation ... this *must* happen before it's submitted to
// any Mailbox
op.submission_number = next_submission_number++;
-
+
// note that in order for this to work (i.e. for sent and received operations to be handled
// in order), it's *vital* that even REMOTE_ONLY operations go through the local queue,
// only being scheduled on the remote queue *after* local operations ahead of it have
@@ -186,10 +186,10 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
bool is_scheduled = local_queue.send(op);
if (is_scheduled)
scheduled(op);
-
+
return is_scheduled;
}
-
+
/**
* Schedules a ReplayOperation created due to a server notification.
*
@@ -218,25 +218,25 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
op.to_string(), to_string());
return false;
}
-
+
notification_queue.add(op);
-
+
// reschedule timeout every time new operation is added
if (notification_timer != null)
notification_timer.cancel();
-
+
notification_timer = Scheduler.after_msec(NOTIFICATION_QUEUE_WAIT_MSEC, on_notification_timeout);
-
+
return true;
}
-
+
private bool on_notification_timeout() {
if (notification_queue.size == 0)
return false;
-
+
debug("%s: Scheduling %d held server notification operations", owner.to_string(),
notification_queue.size);
-
+
// no new operations in timeout span, add them all to the "real" queue
foreach (ReplayOperation notification_op in notification_queue) {
if (!schedule(notification_op)) {
@@ -244,12 +244,12 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
to_string());
}
}
-
+
notification_queue.clear();
-
+
return false;
}
-
+
/**
* This call gives all enqueued remote replay operations a chance to update their own state
* due to a message being removed due to an unsolicited notification from the server)
@@ -261,16 +261,16 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
notify_remote_removed_position_collection(local_queue.get_all(), local_op_active, pos);
notify_remote_removed_position_collection(remote_queue.get_all(), remote_op_active, pos);
}
-
+
private void notify_remote_removed_position_collection(Gee.Collection<ReplayOperation> replay_ops,
ReplayOperation? active, Imap.SequenceNumber pos) {
foreach (ReplayOperation replay_op in replay_ops)
replay_op.notify_remote_removed_position(pos);
-
+
if (active != null)
active.notify_remote_removed_position(pos);
}
-
+
/**
* This call gives all enqueued remote replay operations a chance to update their own state
* due to a message being removed (either during normalization or an unsolicited notification
@@ -283,16 +283,16 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
notify_remote_removed_ids_collection(local_queue.get_all(), local_op_active, ids);
notify_remote_removed_ids_collection(remote_queue.get_all(), remote_op_active, ids);
}
-
+
private void notify_remote_removed_ids_collection(Gee.Collection<ReplayOperation> replay_ops,
ReplayOperation? active, Gee.Collection<ImapDB.EmailIdentifier> ids) {
foreach (ReplayOperation replay_op in replay_ops)
replay_op.notify_remote_removed_ids(ids);
-
+
if (active != null)
active.notify_remote_removed_ids(ids);
}
-
+
/**
* Returns all ImapDb.EmailIdentifiers for enqueued ReplayOperations waiting for
* replay_remote_async() that are planning to be removed on the server.
@@ -300,11 +300,11 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
public void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
foreach (ReplayOperation replay_op in remote_queue.get_all())
replay_op.get_ids_to_be_remote_removed(ids);
-
+
if (remote_op_active != null)
remote_op_active.get_ids_to_be_remote_removed(ids);
}
-
+
/**
* Closes the {@link ReplayQueue}.
*
@@ -319,15 +319,15 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
public async void close_async(bool flush_pending, Cancellable? cancellable = null) throws Error {
if (state != State.OPEN)
return;
-
+
// cancel notification queue timeout
if (notification_timer != null)
notification_timer.cancel();
-
+
// piggyback on the notification timer callback to flush notification operations
if (flush_pending)
on_notification_timeout();
-
+
// mark as closed now to prevent further scheduling ... ReplayClose gets special
// consideration in schedule()
state = State.CLOSING;
@@ -345,28 +345,28 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
CloseReplayQueue close_op = new CloseReplayQueue();
bool is_scheduled = schedule(close_op);
assert(is_scheduled);
-
+
yield close_op.wait_for_ready_async(cancellable);
-
+
state = State.CLOSED;
closed();
}
-
+
private async void clear_pending_async(Cancellable? cancellable) {
// note that this merely clears the queue; disabling the timer is performed in close_async
notification_queue.clear();
-
+
// clear the local queue; nothing more to do there
local_queue.clear();
-
+
// have to backout elements that have executed locally but not remotely
// clear the remote queue before backing out, otherwise the queue might proceed while
// yielding
Gee.List<ReplayOperation> remote_list = new Gee.ArrayList<ReplayOperation>();
remote_list.add_all(remote_queue.get_all());
-
+
remote_queue.clear();
-
+
foreach (ReplayOperation op in remote_list) {
try {
yield op.backout_local_async();
@@ -375,7 +375,7 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
}
}
}
-
+
private async void do_replay_local_async() {
bool queue_running = true;
while (queue_running) {
@@ -385,16 +385,16 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
} catch (Error recv_err) {
debug("Unable to receive next replay operation on local queue %s: %s", to_string(),
recv_err.message);
-
+
break;
}
-
+
local_op_active = op;
-
+
// If this is a Close operation, shut down the queue after processing it
if (op is CloseReplayQueue)
queue_running = false;
-
+
bool local_execute = false;
bool remote_enqueue = false;
switch (op.scope) {
@@ -402,24 +402,24 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
local_execute = true;
remote_enqueue = true;
break;
-
+
case ReplayOperation.Scope.LOCAL_ONLY:
local_execute = true;
remote_enqueue = false;
break;
-
+
case ReplayOperation.Scope.REMOTE_ONLY:
local_execute = false;
remote_enqueue = true;
break;
-
+
default:
assert_not_reached();
}
-
+
if (local_execute) {
locally_executing(op);
-
+
try {
switch (yield op.replay_local_async()) {
case ReplayOperation.Status.COMPLETED:
@@ -427,26 +427,26 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
remote_enqueue = false;
op.notify_ready(null);
break;
-
+
case ReplayOperation.Status.CONTINUE:
// don't touch remote_enqueue; if already false, CONTINUE is treated as
// COMPLETED.
if (!remote_enqueue)
op.notify_ready(null);
break;
-
+
default:
assert_not_reached();
}
} catch (Error replay_err) {
debug("Replay local error for %s on %s: %s", op.to_string(), to_string(),
replay_err.message);
-
+
op.notify_ready(replay_err);
remote_enqueue = false;
}
}
-
+
if (remote_enqueue) {
if (!remote_queue.send(op)) {
debug("Unable to enqueue operation %s for %s remote operation", op.to_string(),
@@ -457,23 +457,23 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
// next stage
assert(op.notified);
}
-
+
if (local_execute)
locally_executed(op, remote_enqueue);
-
+
if (!remote_enqueue) {
if (op.err == null)
completed(op);
else
failed(op);
}
-
+
local_op_active = null;
}
-
+
debug("ReplayQueue.do_replay_local_async %s exiting", to_string());
}
-
+
private async void do_replay_remote_async() {
bool folder_opened = true;
bool queue_running = true;
@@ -485,18 +485,18 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
} catch (Error recv_err) {
debug("Unable to receive next replay operation on remote queue %s: %s", to_string(),
recv_err.message);
-
+
break;
}
-
+
remote_op_active = op;
-
+
// ReplayClose means this queue (and the folder) are closing, so handle errors a little
// differently
bool is_close_op = op is CloseReplayQueue;
if (is_close_op)
queue_running = false;
-
+
// wait until the remote folder is opened (or throws an exception, in which case closed)
Imap.FolderSession? remote = null;
try {
@@ -511,10 +511,10 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
// not open
folder_opened = false;
-
+
// fall through
}
-
+
remotely_executing(op);
Error? remote_err = null;
@@ -535,7 +535,7 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
is_recoverable_failure(replay_err) &&
state == State.OPEN) {
debug("Schedule op retry %s on %s", op.to_string(), to_string());
-
+
// the Folder will disconnect and reconnect due to the hard error and
// wait_for_open_async() will block this command until reconnected and
// normalized
@@ -558,37 +558,37 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
} else if (!is_close_op) {
remote_err = new EngineError.SERVER_UNAVAILABLE("Folder %s not available",
owner.to_string());
}
-
+
// COMPLETED == CONTINUE, only exception of interest here if not closing
if (remote_err != null && !is_close_op) {
try {
backing_out(op, remote_err);
-
+
yield op.backout_local_async();
-
+
backed_out(op, remote_err);
} catch (Error backout_err) {
backout_failed(op, backout_err);
}
}
-
+
// use the remote error (not the backout error) for the operation's completion
// state
op.notify_ready(remote_err);
-
+
remotely_executed(op);
-
+
if (op.err == null)
completed(op);
else
failed(op);
-
+
remote_op_active = null;
}
-
+
debug("ReplayQueue.do_replay_remote_async %s exiting", to_string());
}
-
+
public string to_string() {
return "ReplayQueue:%s (notification=%d local=%d local_active=%s remote=%d remote_active=%s)".printf(
owner.to_string(), notification_queue.size, local_queue.size, (local_op_active !=
null).to_string(),
diff --git a/src/engine/imap-engine/imap-engine-revokable-committed-move.vala
b/src/engine/imap-engine/imap-engine-revokable-committed-move.vala
index e5a9c55b..54431035 100644
--- a/src/engine/imap-engine/imap-engine-revokable-committed-move.vala
+++ b/src/engine/imap-engine/imap-engine-revokable-committed-move.vala
@@ -14,7 +14,7 @@ private class Geary.ImapEngine.RevokableCommittedMove : Revokable {
private FolderPath source;
private FolderPath destination;
private Gee.Set<Imap.UID> destination_uids;
-
+
public RevokableCommittedMove(GenericAccount account, FolderPath source, FolderPath destination,
Gee.Set<Imap.UID> destination_uids) {
this.account = account;
diff --git a/src/engine/imap-engine/imap-engine-revokable-move.vala
b/src/engine/imap-engine/imap-engine-revokable-move.vala
index 25f18aa3..9490191c 100644
--- a/src/engine/imap-engine/imap-engine-revokable-move.vala
+++ b/src/engine/imap-engine/imap-engine-revokable-move.vala
@@ -15,38 +15,38 @@
private class Geary.ImapEngine.RevokableMove : Revokable {
private const int COMMIT_TIMEOUT_SEC = 5;
-
+
private GenericAccount account;
private MinimalFolder source;
private Geary.Folder destination;
private Gee.Set<ImapDB.EmailIdentifier> move_ids;
-
+
public RevokableMove(GenericAccount account, MinimalFolder source, Geary.Folder destination,
Gee.Set<ImapDB.EmailIdentifier> move_ids) {
base (COMMIT_TIMEOUT_SEC);
-
+
this.account = account;
this.source = source;
this.destination = destination;
this.move_ids = move_ids;
-
+
account.folders_available_unavailable.connect(on_folders_available_unavailable);
source.email_removed.connect(on_source_email_removed);
source.marked_email_removed.connect(on_source_email_removed);
source.closing.connect(on_source_closing);
}
-
+
~RevokableMove() {
account.folders_available_unavailable.disconnect(on_folders_available_unavailable);
source.email_removed.disconnect(on_source_email_removed);
source.marked_email_removed.disconnect(on_source_email_removed);
source.closing.disconnect(on_source_closing);
-
+
// if still valid, schedule operation so its executed
if (valid && source.get_open_state() != Folder.OpenState.CLOSED) {
debug("Freeing revokable, scheduling move %d emails from %s to %s", move_ids.size,
source.path.to_string(), destination.to_string());
-
+
try {
source.schedule_op(new MoveEmailCommit(source, move_ids, destination.path, null));
} catch (Error err) {
@@ -109,10 +109,10 @@ private class Geary.ImapEngine.RevokableMove : Revokable {
// one-way switch
if (!valid)
return;
-
+
foreach (EmailIdentifier id in ids)
move_ids.remove((ImapDB.EmailIdentifier) id);
-
+
if (move_ids.size <= 0)
set_invalid();
}
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
b/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
index 9285f0fa..de072056 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
@@ -79,34 +79,34 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
// The accumulated Email from the list operation. Should only be accessed once the operation
// has completed.
public Gee.List<Geary.Email> accumulator = new Gee.ArrayList<Geary.Email>();
-
+
protected MinimalFolder owner;
protected Geary.Email.Field required_fields;
protected Cancellable? cancellable;
protected Folder.ListFlags flags;
-
+
private Gee.HashMap<Imap.UID, Geary.Email.Field> unfulfilled = new Gee.HashMap<Imap.UID,
Geary.Email.Field>();
-
+
public AbstractListEmail(string name, MinimalFolder owner, Geary.Email.Field required_fields,
Folder.ListFlags flags, Cancellable? cancellable) {
base(name, OnError.IGNORE_REMOTE);
-
+
this.owner = owner;
this.required_fields = required_fields;
this.cancellable = cancellable;
this.flags = flags;
}
-
+
protected void add_unfulfilled_fields(Imap.UID? uid, Geary.Email.Field unfulfilled_fields) {
assert(uid != null);
assert(uid.is_valid());
-
+
if (!unfulfilled.has_key(uid))
unfulfilled.set(uid, unfulfilled_fields);
else
unfulfilled.set(uid, unfulfilled.get(uid) | unfulfilled_fields);
}
-
+
protected void add_many_unfulfilled_fields(Gee.Collection<Imap.UID>? uids,
Geary.Email.Field unfulfilled_fields) {
if (uids != null) {
@@ -114,18 +114,18 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
add_unfulfilled_fields(uid, unfulfilled_fields);
}
}
-
+
protected int get_unfulfilled_count() {
return unfulfilled.size;
}
-
+
public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
// remove email already picked up from local store ... for email reported via the
// callback, too late
Collection.remove_if<Geary.Email>(accumulator, (email) => {
return ids.contains((ImapDB.EmailIdentifier) email.id);
});
-
+
// remove from unfulfilled list, as there's now nothing to fetch from the server
// NOTE: Requires UID to work; this *should* always work, as the EmailIdentifier should
// be originating from the database, not the Imap.Folder layer
@@ -147,7 +147,7 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
int fetches_avoided = yield remove_fulfilled_uids_async();
if (fetches_avoided > 0) {
total_fetches_avoided += fetches_avoided;
-
+
debug("[%s] %d previously-fulfilled fetches avoided in list operation, %d total",
owner.to_string(), fetches_avoided, total_fetches_avoided);
@@ -187,23 +187,23 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
yield batch.execute_all_async(cancellable);
batch.throw_first_exception();
-
+
Gee.ArrayList<Geary.Email> result_list = new Gee.ArrayList<Geary.Email>();
Gee.HashSet<Geary.EmailIdentifier> created_ids = new Gee.HashSet<Geary.EmailIdentifier>();
foreach (int batch_id in batch.get_ids()) {
Gee.List<Geary.Email>? list = (Gee.List<Geary.Email>?) batch.get_result(batch_id);
if (list != null && list.size > 0) {
result_list.add_all(list);
-
+
RemoteBatchOperation op = (RemoteBatchOperation) batch.get_operation(batch_id);
created_ids.add_all(op.created_ids);
}
}
-
+
// report merged emails
if (result_list.size > 0)
accumulator.add_all(result_list);
-
+
// signal
if (created_ids.size > 0) {
owner.replay_notify_email_inserted(created_ids);
@@ -311,7 +311,7 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
// is called) that the fields are downloaded and added to the database
foreach (Geary.Email email in list)
uids.add(((ImapDB.EmailIdentifier) email.id).uid);
-
+
// remove any already stored locally
Gee.Collection<ImapDB.EmailIdentifier>? ids =
yield owner.local_folder.get_ids_async(uids,
ImapDB.Folder.ListFlags.INCLUDE_MARKED_FOR_REMOVE,
@@ -322,42 +322,42 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
uids.remove(id.uid);
}
}
-
+
// for the remainder (not in local store), fetch the required fields
add_many_unfulfilled_fields(uids, ImapDB.Folder.REQUIRED_FIELDS);
}
-
+
debug("%s: Vector expansion completed (%d new email)", owner.to_string(),
(uids != null) ? uids.size : 0);
-
+
return uids != null && uids.size > 0 ? uids : null;
}
-
+
private async int remove_fulfilled_uids_async() throws Error {
// if the update is forced, don't rely on cached database, have to go to the horse's mouth
if (flags.is_force_update())
return 0;
-
+
ImapDB.Folder.ListFlags list_flags = ImapDB.Folder.ListFlags.from_folder_flags(flags);
-
+
Gee.Set<ImapDB.EmailIdentifier>? unfulfilled_ids = yield owner.local_folder.get_ids_async(
unfulfilled.keys, list_flags, cancellable);
if (unfulfilled_ids == null || unfulfilled_ids.size == 0)
return 0;
-
+
Gee.Map<ImapDB.EmailIdentifier, Email.Field>? local_fields =
yield owner.local_folder.list_email_fields_by_id_async(unfulfilled_ids, list_flags,
cancellable);
if (local_fields == null || local_fields.size == 0)
return 0;
-
+
// For each identifier, if now fulfilled in the database, fetch it, add it to the accumulator,
// and remove it from the unfulfilled map -- one network operation saved
int fetch_avoided = 0;
foreach (ImapDB.EmailIdentifier id in local_fields.keys) {
if (!local_fields.get(id).fulfills(required_fields))
continue;
-
+
try {
Email email = yield owner.local_folder.fetch_email_async(id, required_fields, list_flags,
cancellable);
@@ -365,16 +365,16 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
} catch (Error err) {
if (err is IOError.CANCELLED)
throw err;
-
+
// some problem locally, do the network round-trip
continue;
}
-
+
// got it, don't fetch from remote
unfulfilled.unset(id.uid);
fetch_avoided++;
}
-
+
return fetch_avoided;
}
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
b/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
index b5140525..de2c61b0 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
@@ -6,18 +6,18 @@
private class Geary.ImapEngine.CopyEmail : Geary.ImapEngine.SendReplayOperation {
public Gee.Set<Imap.UID> destination_uids = new Gee.HashSet<Imap.UID>();
-
+
private MinimalFolder engine;
private Gee.HashSet<ImapDB.EmailIdentifier> to_copy = new Gee.HashSet<ImapDB.EmailIdentifier>();
private Geary.FolderPath destination;
private Cancellable? cancellable;
- public CopyEmail(MinimalFolder engine, Gee.List<ImapDB.EmailIdentifier> to_copy,
+ public CopyEmail(MinimalFolder engine, Gee.List<ImapDB.EmailIdentifier> to_copy,
Geary.FolderPath destination, Cancellable? cancellable = null) {
base("CopyEmail", OnError.RETRY);
-
+
this.engine = engine;
-
+
this.to_copy.add_all(to_copy);
this.destination = destination;
this.cancellable = cancellable;
@@ -30,7 +30,7 @@ private class Geary.ImapEngine.CopyEmail : Geary.ImapEngine.SendReplayOperation
public override async ReplayOperation.Status replay_local_async() throws Error {
if (to_copy.size == 0)
return ReplayOperation.Status.COMPLETED;
-
+
// The local DB will be updated when the remote folder is opened and we see a new message
// existing there.
return ReplayOperation.Status.CONTINUE;
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
b/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
index 9062c235..2f9a325d 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
@@ -6,19 +6,19 @@
private class Geary.ImapEngine.CreateEmail : Geary.ImapEngine.SendReplayOperation {
public Geary.EmailIdentifier? created_id { get; private set; default = null; }
-
+
private MinimalFolder engine;
private RFC822.Message? rfc822;
private Geary.EmailFlags? flags;
private DateTime? date_received;
private Cancellable? cancellable;
-
+
public CreateEmail(MinimalFolder engine, RFC822.Message rfc822, Geary.EmailFlags? flags,
DateTime? date_received, Cancellable? cancellable) {
base.only_remote("CreateEmail", OnError.RETRY);
-
+
this.engine = engine;
-
+
this.rfc822 = rfc822;
this.flags = flags;
this.date_received = date_received;
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
b/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
index 8a045ecc..6c9f6711 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
@@ -14,10 +14,10 @@ private class Geary.ImapEngine.EmptyFolder : Geary.ImapEngine.SendReplayOperatio
private Cancellable? cancellable;
private Gee.Set<ImapDB.EmailIdentifier>? removed_ids = null;
private int original_count = 0;
-
+
public EmptyFolder(MinimalFolder engine, Cancellable? cancellable) {
base("EmptyFolder", OnError.RETRY);
-
+
this.engine = engine;
this.cancellable = cancellable;
}
@@ -30,20 +30,20 @@ private class Geary.ImapEngine.EmptyFolder : Geary.ImapEngine.SendReplayOperatio
// mark everything in the folder as removed
removed_ids = yield engine.local_folder.mark_removed_async(null, true, cancellable);
-
+
// if local folder is not empty, report all as being removed
if (removed_ids != null) {
if (removed_ids.size > 0)
engine.replay_notify_email_removed(removed_ids);
-
+
int new_count = Numeric.int_floor(original_count - removed_ids.size, 0);
if (new_count != original_count)
engine.replay_notify_email_count_changed(new_count, Geary.Folder.CountChangeReason.REMOVED);
}
-
+
return ReplayOperation.Status.CONTINUE;
}
-
+
public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
if (removed_ids != null)
ids.add_all(removed_ids);
@@ -62,10 +62,10 @@ private class Geary.ImapEngine.EmptyFolder : Geary.ImapEngine.SendReplayOperatio
yield engine.local_folder.mark_removed_async(removed_ids, false, cancellable);
engine.replay_notify_email_inserted(removed_ids);
}
-
+
engine.replay_notify_email_count_changed(original_count, Geary.Folder.CountChangeReason.INSERTED);
}
-
+
public override string describe_state() {
return "removed_ids.size=%d".printf((removed_ids != null) ? removed_ids.size : 0);
}
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
b/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
index 5ecdc200..3edf4347 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
@@ -6,7 +6,7 @@
private class Geary.ImapEngine.FetchEmail : Geary.ImapEngine.SendReplayOperation {
public Email? email = null;
-
+
private MinimalFolder engine;
private ImapDB.EmailIdentifier id;
private Email.Field required_fields;
@@ -15,26 +15,26 @@ private class Geary.ImapEngine.FetchEmail : Geary.ImapEngine.SendReplayOperation
private Cancellable? cancellable;
private Imap.UID? uid = null;
private bool remote_removed = false;
-
+
public FetchEmail(MinimalFolder engine, ImapDB.EmailIdentifier id, Email.Field required_fields,
Folder.ListFlags flags, Cancellable? cancellable) {
// Unlike the list operations, fetch needs to retry remote
base ("FetchEmail", OnError.RETRY);
-
+
this.engine = engine;
this.id = id;
this.required_fields = required_fields;
this.flags = flags;
this.cancellable = cancellable;
-
+
// always fetch the required fields unless a modified list, in which case want to do exactly
// what's required, no more and no less
if (!flags.is_all_set(Folder.ListFlags.LOCAL_ONLY) &&
!flags.is_all_set(Folder.ListFlags.FORCE_UPDATE))
this.required_fields |= ImapDB.Folder.REQUIRED_FIELDS;
-
+
remaining_fields = required_fields;
}
-
+
public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
remote_removed = ids.contains(id);
}
@@ -81,17 +81,17 @@ private class Geary.ImapEngine.FetchEmail : Geary.ImapEngine.SendReplayOperation
remaining_fields = required_fields.clear(email.fields);
else
remaining_fields = required_fields;
-
+
assert(remaining_fields != 0);
-
+
if (email != null)
uid = ((ImapDB.EmailIdentifier) email.id).uid;
else
uid = yield engine.local_folder.get_uid_async(id, ImapDB.Folder.ListFlags.NONE, cancellable);
-
+
if (uid == null)
throw new EngineError.NOT_FOUND("Unable to find %s in %s", id.to_string(), engine.to_string());
-
+
return ReplayOperation.Status.CONTINUE;
}
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-id.vala
b/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-id.vala
index b14625af..04c28197 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-id.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-id.vala
@@ -9,11 +9,11 @@ private class Geary.ImapEngine.ListEmailByID : Geary.ImapEngine.AbstractListEmai
private int count;
private int fulfilled_count = 0;
private Imap.UID? initial_uid = null;
-
+
public ListEmailByID(MinimalFolder owner, ImapDB.EmailIdentifier? initial_id, int count,
Geary.Email.Field required_fields, Folder.ListFlags flags, Cancellable? cancellable) {
base ("ListEmailByID", owner, required_fields, flags, cancellable);
-
+
this.initial_id = initial_id;
this.count = count;
}
@@ -70,7 +70,7 @@ private class Geary.ImapEngine.ListEmailByID : Geary.ImapEngine.AbstractListEmai
fulfilled_count = fulfilled.size;
if (fulfilled_count > 0)
accumulator.add_all(fulfilled);
-
+
// determine if everything was listed
bool finished = false;
if (flags.is_local_only()) {
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-sparse-id.vala
b/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-sparse-id.vala
index 81e95388..dfd153c7 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-sparse-id.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-sparse-id.vala
@@ -6,49 +6,49 @@
private class Geary.ImapEngine.ListEmailBySparseID : Geary.ImapEngine.AbstractListEmail {
private Gee.HashSet<ImapDB.EmailIdentifier> ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
-
+
public ListEmailBySparseID(MinimalFolder owner, Gee.Collection<ImapDB.EmailIdentifier> ids,
Geary.Email.Field required_fields, Folder.ListFlags flags, Cancellable? cancellable) {
base ("ListEmailBySparseID", owner, required_fields, flags, cancellable);
-
+
this.ids.add_all(ids);
}
-
+
public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> removed_ids) {
ids.remove_all(removed_ids);
-
+
base.notify_remote_removed_ids(removed_ids);
}
-
+
public override async ReplayOperation.Status replay_local_async() throws Error {
if (flags.is_force_update()) {
Gee.Set<Imap.UID>? uids = yield owner.local_folder.get_uids_async(ids,
ImapDB.Folder.ListFlags.NONE,
cancellable);
add_many_unfulfilled_fields(uids, required_fields);
-
+
return ReplayOperation.Status.CONTINUE;
}
-
+
Gee.List<Geary.Email>? local_list = yield owner.local_folder.list_email_by_sparse_id_async(ids,
required_fields, ImapDB.Folder.ListFlags.PARTIAL_OK, cancellable);
-
+
// Build list of emails fully fetched from local store and table of remaining emails by
// their lack of completeness
Gee.List<Geary.Email> fulfilled = new Gee.ArrayList<Geary.Email>();
if (local_list != null && local_list.size > 0) {
Gee.Map<Geary.EmailIdentifier, Geary.Email>? map = Email.emails_to_map(local_list);
assert(map != null);
-
+
// walk list of *requested* IDs to ensure that unknown are considering unfulfilled
foreach (ImapDB.EmailIdentifier id in ids) {
Geary.Email? email = map.get(id);
-
+
// if non-null, then the local_folder should've supplied a UID; if null, then
// it's simply not present in the local folder (since PARTIAL_OK is spec'd), so
// we have no way of referring to it on the server
if (email == null)
continue;
-
+
// if completely unknown, make sure duplicate detection fields are included; otherwise,
// if known, then they were pulled down during folder normalization and during
// vector expansion
@@ -60,20 +60,20 @@ private class Geary.ImapEngine.ListEmailBySparseID : Geary.ImapEngine.AbstractLi
}
}
}
-
+
if (fulfilled.size > 0)
accumulator.add_all(fulfilled);
-
+
if (flags.is_local_only() || get_unfulfilled_count() == 0)
return ReplayOperation.Status.COMPLETED;
-
+
return ReplayOperation.Status.CONTINUE;
}
-
+
public override async void backout_local_async() throws Error {
// R/O, nothing to backout
}
-
+
public override string describe_state() {
return "ids.size=%d required_fields=%Xh flags=%Xh".printf(ids.size, required_fields, flags);
}
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala
b/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala
index 2330ecc6..ddb0ed91 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala
@@ -11,20 +11,20 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
private Geary.EmailFlags? flags_to_remove;
private Gee.Map<ImapDB.EmailIdentifier, Geary.EmailFlags>? original_flags = null;
private Cancellable? cancellable;
-
- public MarkEmail(MinimalFolder engine, Gee.List<ImapDB.EmailIdentifier> to_mark,
- Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove,
+
+ public MarkEmail(MinimalFolder engine, Gee.List<ImapDB.EmailIdentifier> to_mark,
+ Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove,
Cancellable? cancellable = null) {
base("MarkEmail", OnError.RETRY);
-
+
this.engine = engine;
-
+
this.to_mark.add_all(to_mark);
this.flags_to_add = flags_to_add;
this.flags_to_remove = flags_to_remove;
this.cancellable = cancellable;
}
-
+
public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
// don't bother updating on server or backing out locally
if (original_flags != null)
@@ -34,7 +34,7 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
public override async ReplayOperation.Status replay_local_async() throws Error {
if (to_mark.size == 0)
return ReplayOperation.Status.COMPLETED;
-
+
// Save original flags, then set new ones.
// TODO: Make this atomic (otherwise there stands a chance backout_local_async() will
// reapply the wrong flags): should get the original flags and the new flags in the same
@@ -42,16 +42,16 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
original_flags = yield engine.local_folder.get_email_flags_async(to_mark, cancellable);
if (original_flags == null || original_flags.size == 0)
return ReplayOperation.Status.COMPLETED;
-
+
yield engine.local_folder.mark_email_async(original_flags.keys, flags_to_add, flags_to_remove,
cancellable);
-
+
// Notify using flags from DB.
Gee.Map<EmailIdentifier, Geary.EmailFlags>? map = yield engine.local_folder.get_email_flags_async(
original_flags.keys, cancellable);
if (map != null && map.size > 0)
engine.replay_notify_email_flags_changed(map);
-
+
return ReplayOperation.Status.CONTINUE;
}
@@ -73,7 +73,7 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
if (original_flags != null)
yield engine.local_folder.set_email_flags_async(original_flags, cancellable);
}
-
+
public override string describe_state() {
return "to_mark=%d flags_to_add=%s flags_to_remove=%s".printf(to_mark.size,
(flags_to_add != null) ? flags_to_add.to_string() : "(none)",
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
b/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
index 1fae27e3..5652fcd4 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
@@ -10,24 +10,24 @@
private class Geary.ImapEngine.MoveEmailCommit : Geary.ImapEngine.SendReplayOperation {
public Gee.Set<Imap.UID> destination_uids = new Gee.HashSet<Imap.UID>();
-
+
private MinimalFolder engine;
private Gee.List<ImapDB.EmailIdentifier> to_move = new Gee.ArrayList<ImapDB.EmailIdentifier>();
private Geary.FolderPath destination;
private Cancellable? cancellable;
private Gee.List<Imap.MessageSet>? remaining_msg_sets = null;
-
+
public MoveEmailCommit(MinimalFolder engine, Gee.Collection<ImapDB.EmailIdentifier> to_move,
Geary.FolderPath destination, Cancellable? cancellable) {
base.only_remote("MoveEmailCommit", OnError.RETRY);
-
+
this.engine = engine;
-
+
this.to_move.add_all(to_move);
this.destination = destination;
this.cancellable = cancellable;
}
-
+
public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
to_move.remove_all(ids);
}
@@ -80,7 +80,7 @@ private class Geary.ImapEngine.MoveEmailCommit : Geary.ImapEngine.SendReplayOper
);
if (map != null)
destination_uids.add_all(map.values);
-
+
yield remote.remove_email_async(msg_set.to_list(), null);
// completed successfully, remove from list in case of retry
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala
b/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala
index b0efd5d9..498de990 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala
@@ -14,25 +14,25 @@
*/
private class Geary.ImapEngine.MoveEmailPrepare : Geary.ImapEngine.SendReplayOperation {
public Gee.Set<ImapDB.EmailIdentifier>? prepared_for_move = null;
-
+
private MinimalFolder engine;
private Cancellable? cancellable;
private Gee.List<ImapDB.EmailIdentifier> to_move = new Gee.ArrayList<ImapDB.EmailIdentifier>();
-
+
public MoveEmailPrepare(MinimalFolder engine, Gee.Collection<ImapDB.EmailIdentifier> to_move,
Cancellable? cancellable) {
base.only_local("MoveEmailPrepare", OnError.RETRY);
-
+
this.engine = engine;
this.to_move.add_all(to_move);
this.cancellable = cancellable;
}
-
+
public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
if (prepared_for_move != null)
prepared_for_move.remove_all(ids);
}
-
+
public override async ReplayOperation.Status replay_local_async() throws Error {
if (to_move.size <= 0)
return ReplayOperation.Status.COMPLETED;
@@ -45,13 +45,13 @@ private class Geary.ImapEngine.MoveEmailPrepare : Geary.ImapEngine.SendReplayOpe
prepared_for_move = yield engine.local_folder.mark_removed_async(to_move, true, cancellable);
if (prepared_for_move == null || prepared_for_move.size == 0)
return ReplayOperation.Status.COMPLETED;
-
+
engine.replay_notify_email_removed(prepared_for_move);
-
+
engine.replay_notify_email_count_changed(
Numeric.int_floor(count - prepared_for_move.size, 0),
Folder.CountChangeReason.REMOVED);
-
+
return ReplayOperation.Status.COMPLETED;
}
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala
b/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala
index e2e673dc..5cecd822 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala
@@ -12,25 +12,25 @@ private class Geary.ImapEngine.MoveEmailRevoke : Geary.ImapEngine.SendReplayOper
private MinimalFolder engine;
private Gee.List<ImapDB.EmailIdentifier> to_revoke = new Gee.ArrayList<ImapDB.EmailIdentifier>();
private Cancellable? cancellable;
-
+
public MoveEmailRevoke(MinimalFolder engine, Gee.Collection<ImapDB.EmailIdentifier> to_revoke,
Cancellable? cancellable) {
base.only_local("MoveEmailRevoke", OnError.RETRY);
-
+
this.engine = engine;
-
+
this.to_revoke.add_all(to_revoke);
this.cancellable = cancellable;
}
-
+
public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
to_revoke.remove_all(ids);
}
-
+
public override async ReplayOperation.Status replay_local_async() throws Error {
if (to_revoke.size == 0)
return ReplayOperation.Status.COMPLETED;
-
+
Gee.Set<ImapDB.EmailIdentifier>? revoked = yield engine.local_folder.mark_removed_async(
to_revoke, false, cancellable);
if (revoked == null || revoked.size == 0)
@@ -44,7 +44,7 @@ private class Geary.ImapEngine.MoveEmailRevoke : Geary.ImapEngine.SendReplayOper
engine.replay_notify_email_inserted(revoked);
engine.replay_notify_email_count_changed(count + revoked.size,
Geary.Folder.CountChangeReason.INSERTED);
-
+
return ReplayOperation.Status.COMPLETED;
}
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala
b/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala
index cad3e574..429fb259 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala
@@ -10,22 +10,22 @@ private class Geary.ImapEngine.RemoveEmail : Geary.ImapEngine.SendReplayOperatio
private Cancellable? cancellable;
private Gee.Set<ImapDB.EmailIdentifier>? removed_ids = null;
private int original_count = 0;
-
+
public RemoveEmail(MinimalFolder engine, Gee.List<ImapDB.EmailIdentifier> to_remove,
Cancellable? cancellable = null) {
base("RemoveEmail", OnError.RETRY);
-
+
this.engine = engine;
-
+
this.to_remove.add_all(to_remove);
this.cancellable = cancellable;
}
-
+
public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
if (removed_ids != null)
removed_ids.remove_all(ids);
}
-
+
public override async ReplayOperation.Status replay_local_async() throws Error {
// if performing a full expunge, need to move on to replay_remote_async() for that
if (this.to_remove.size <= 0)
@@ -39,15 +39,15 @@ private class Geary.ImapEngine.RemoveEmail : Geary.ImapEngine.SendReplayOperatio
removed_ids = yield engine.local_folder.mark_removed_async(to_remove, true, cancellable);
if (removed_ids == null || removed_ids.size == 0)
return ReplayOperation.Status.COMPLETED;
-
+
engine.replay_notify_email_removed(removed_ids);
-
+
engine.replay_notify_email_count_changed(Numeric.int_floor(original_count - removed_ids.size, 0),
Geary.Folder.CountChangeReason.REMOVED);
-
+
return ReplayOperation.Status.CONTINUE;
}
-
+
public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
if (removed_ids != null)
ids.add_all(removed_ids);
@@ -71,10 +71,10 @@ private class Geary.ImapEngine.RemoveEmail : Geary.ImapEngine.SendReplayOperatio
yield engine.local_folder.mark_removed_async(removed_ids, false, cancellable);
engine.replay_notify_email_inserted(removed_ids);
}
-
+
engine.replay_notify_email_count_changed(original_count, Geary.Folder.CountChangeReason.INSERTED);
}
-
+
public override string describe_state() {
return "to_remove.size=%d removed_ids.size=%d".printf(to_remove.size,
(removed_ids != null) ? removed_ids.size : 0);
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
b/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
index 1908c64f..1c2479b1 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
@@ -34,18 +34,18 @@ private class Geary.ImapEngine.ReplayAppend : Geary.ImapEngine.ReplayOperation {
Gee.List<Imap.SequenceNumber> new_positions = new Gee.ArrayList<Imap.SequenceNumber>();
foreach (Imap.SequenceNumber? position in positions) {
Imap.SequenceNumber old_position = position;
-
+
// adjust depending on relation to removed message
position = position.shift_for_removed(removed);
if (position != null)
new_positions.add(position);
-
+
debug("%s: ReplayAppend remote unsolicited remove: %s -> %s", owner.to_string(),
old_position.to_string(), (position != null) ? position.to_string() : "(null)");
}
-
+
positions = new_positions;
-
+
// DON'T update remote_count, it is intended to report the remote count at the time the
// appended messages arrived
}
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-replay-removal.vala
b/src/engine/imap-engine/replay-ops/imap-engine-replay-removal.vala
index fba48c27..6b8a0b8a 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-replay-removal.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-replay-removal.vala
@@ -18,25 +18,25 @@ private class Geary.ImapEngine.ReplayRemoval : Geary.ImapEngine.ReplayOperation
public ReplayRemoval(MinimalFolder owner, int remote_count, Imap.SequenceNumber position) {
// remote error will cause folder to reconnect and re-normalize, making this remove moot
base ("Removal", Scope.LOCAL_AND_REMOTE, OnError.IGNORE_REMOTE);
-
+
this.owner = owner;
this.remote_count = remote_count;
this.position = position;
}
-
+
public override void notify_remote_removed_position(Imap.SequenceNumber removed) {
// although using positional addressing, don't update state; EXPUNGEs that happen after
// other EXPUNGEs have no affect on those ahead of it
}
-
+
public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
// this operation deals only in positional addressing
}
-
+
public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
// this ReplayOperation doesn't do remote removes, it reacts to them
}
-
+
public override async ReplayOperation.Status replay_local_async() throws Error {
// Although technically a local-only operation, must treat as remote to ensure it's
// processed in-order with ReplayAppend operations
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-replay-update.vala
b/src/engine/imap-engine/replay-ops/imap-engine-replay-update.vala
index 574293c4..eb0bc3e7 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-replay-update.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-replay-update.vala
@@ -52,7 +52,7 @@ private class Geary.ImapEngine.ReplayUpdate : Geary.ImapEngine.ReplayOperation {
}
if (id != null) {
- Gee.Map<Geary.ImapDB.EmailIdentifier, Geary.EmailFlags> changed_map =
+ Gee.Map<Geary.ImapDB.EmailIdentifier, Geary.EmailFlags> changed_map =
new Gee.HashMap<Geary.ImapDB.EmailIdentifier, Geary.EmailFlags>();
changed_map.set(id, new Imap.EmailFlags(message_flags));
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
b/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
index 2615e6bb..af989933 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
@@ -12,16 +12,16 @@
*/
private class Geary.ImapEngine.ServerSearchEmail : Geary.ImapEngine.AbstractListEmail {
private Imap.SearchCriteria criteria;
-
+
public ServerSearchEmail(MinimalFolder owner, Imap.SearchCriteria criteria, Geary.Email.Field
required_fields,
Cancellable? cancellable) {
// OLDEST_TO_NEWEST used for vector expansion, if necessary
base ("ServerSearchEmail", owner, required_fields, Geary.Folder.ListFlags.OLDEST_TO_NEWEST,
cancellable);
-
+
// unlike list, need to retry this as there's no local component to return
on_remote_error = OnError.RETRY;
-
+
this.criteria = criteria;
}
@@ -58,17 +58,17 @@ private class Geary.ImapEngine.ServerSearchEmail : Geary.ImapEngine.AbstractList
if (id != null)
local_ids.add(id);
}
-
+
Gee.List<Geary.Email>? local_list = yield owner.local_folder.list_email_by_sparse_id_async(
local_ids, required_fields, ImapDB.Folder.ListFlags.PARTIAL_OK, cancellable);
-
+
// Build list of local email
Gee.Map<ImapDB.EmailIdentifier, Geary.Email> map = new Gee.HashMap<ImapDB.EmailIdentifier,
Geary.Email>();
if (local_list != null) {
foreach (Geary.Email email in local_list)
map.set((ImapDB.EmailIdentifier) email.id, email);
}
-
+
// Convert into fulfilled and unfulfilled email for the base class to complete
foreach (ImapDB.EmailIdentifier id in map.keys) {
Geary.Email? email = map.get(id);
diff --git a/src/engine/imap/api/imap-account-session.vala b/src/engine/imap/api/imap-account-session.vala
index 45358e3f..ff335262 100644
--- a/src/engine/imap/api/imap-account-session.vala
+++ b/src/engine/imap/api/imap-account-session.vala
@@ -396,9 +396,9 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
status_results,
cancellable
);
-
+
assert(responses.size == 1);
-
+
return Geary.Collection.get_first(responses.values);
}
diff --git a/src/engine/imap/api/imap-email-flags.vala b/src/engine/imap/api/imap-email-flags.vala
index 1d9bb13d..08ea7ac8 100644
--- a/src/engine/imap/api/imap-email-flags.vala
+++ b/src/engine/imap/api/imap-email-flags.vala
@@ -6,26 +6,26 @@
public class Geary.Imap.EmailFlags : Geary.EmailFlags {
public MessageFlags message_flags { get; private set; }
-
+
public EmailFlags(MessageFlags flags) {
message_flags = flags;
-
+
if (!flags.contains(MessageFlag.SEEN))
add(UNREAD);
-
+
if (flags.contains(MessageFlag.FLAGGED))
add(FLAGGED);
-
+
if (flags.contains(MessageFlag.LOAD_REMOTE_IMAGES))
add(LOAD_REMOTE_IMAGES);
-
+
if (flags.contains(MessageFlag.DRAFT))
add(DRAFT);
if (flags.contains(MessageFlag.DELETED))
add(DELETED);
}
-
+
/**
* Converts a generic {@link Geary.EmailFlags} to IMAP's internal representation of them.
*
@@ -35,66 +35,66 @@ public class Geary.Imap.EmailFlags : Geary.EmailFlags {
Imap.EmailFlags? imap_flags = api_flags as Imap.EmailFlags;
if (imap_flags != null)
return imap_flags;
-
+
Gee.List<MessageFlag> msg_flags_add;
Gee.List<MessageFlag> msg_flags_remove;
Geary.Imap.MessageFlag.from_email_flags(api_flags, null, out msg_flags_add,
out msg_flags_remove);
-
+
Gee.ArrayList<MessageFlag> msg_flags = new Gee.ArrayList<MessageFlag>();
-
+
foreach(MessageFlag mf in msg_flags_add)
msg_flags.add(mf);
-
+
// This is a special case, since it's read and seen are opposites.
if (!api_flags.is_unread())
msg_flags.add(MessageFlag.SEEN);
-
+
foreach(MessageFlag mf in msg_flags_remove)
msg_flags.remove(mf);
-
+
return new Imap.EmailFlags(new MessageFlags(msg_flags));
}
-
+
protected override void notify_added(Gee.Collection<NamedFlag> added) {
foreach (NamedFlag flag in added) {
if (flag.equal_to(UNREAD))
message_flags.remove(MessageFlag.SEEN);
-
+
if (flag.equal_to(FLAGGED))
message_flags.add(MessageFlag.FLAGGED);
-
+
if (flag.equal_to(LOAD_REMOTE_IMAGES))
message_flags.add(MessageFlag.LOAD_REMOTE_IMAGES);
-
+
if (flag.equal_to(DRAFT))
message_flags.add(MessageFlag.DRAFT);
-
- if (flag.equal_to(DELETED))
+
+ if (flag.equal_to(DELETED))
message_flags.add(MessageFlag.DELETED);
}
-
+
base.notify_added(added);
}
-
+
protected override void notify_removed(Gee.Collection<NamedFlag> removed) {
foreach (NamedFlag flag in removed) {
if (flag.equal_to(UNREAD))
message_flags.add(MessageFlag.SEEN);
-
+
if (flag.equal_to(FLAGGED))
message_flags.remove(MessageFlag.FLAGGED);
-
+
if (flag.equal_to(LOAD_REMOTE_IMAGES))
message_flags.remove(MessageFlag.LOAD_REMOTE_IMAGES);
-
+
if (flag.equal_to(DRAFT))
message_flags.remove(MessageFlag.DRAFT);
if (flag.equal_to(DELETED))
message_flags.remove(MessageFlag.DELETED);
}
-
+
base.notify_removed(removed);
}
}
diff --git a/src/engine/imap/api/imap-email-properties.vala b/src/engine/imap/api/imap-email-properties.vala
index b1a19372..6ef84852 100644
--- a/src/engine/imap/api/imap-email-properties.vala
+++ b/src/engine/imap/api/imap-email-properties.vala
@@ -7,33 +7,33 @@
public class Geary.Imap.EmailProperties : Geary.EmailProperties, Gee.Hashable<Geary.Imap.EmailProperties> {
public InternalDate? internaldate { get; private set; }
public RFC822.Size? rfc822_size { get; private set; }
-
+
public EmailProperties(InternalDate? internaldate, RFC822.Size? rfc822_size) {
base (internaldate.value, rfc822_size.value);
-
+
this.internaldate = internaldate;
this.rfc822_size = rfc822_size;
}
-
+
public bool equal_to(Geary.Imap.EmailProperties other) {
if (this == other)
return true;
-
+
// for simplicity and robustness, internaldate and rfc822_size must be present in both
// to be considered equal
if (internaldate == null || other.internaldate == null)
return false;
-
+
if (rfc822_size == null || other.rfc822_size == null)
return false;
-
+
return true;
}
-
+
public uint hash() {
return to_string().hash();
}
-
+
public override string to_string() {
return "internaldate:%s/size:%s".printf((internaldate != null) ? internaldate.to_string() : "(none)",
(rfc822_size != null) ? rfc822_size.to_string() : "(none)");
diff --git a/src/engine/imap/api/imap-folder-properties.vala b/src/engine/imap/api/imap-folder-properties.vala
index 4dc4b275..54b21ce3 100644
--- a/src/engine/imap/api/imap-folder-properties.vala
+++ b/src/engine/imap/api/imap-folder-properties.vala
@@ -37,7 +37,7 @@
* The base class' email_total is updated when either *_messages is updated; however, SELECT/EXAMINE
* is considered more authoritative than STATUS.
*/
-
+
public class Geary.Imap.FolderProperties : Geary.FolderProperties {
/**
* -1 if the Folder was not opened via SELECT or EXAMINE. Updated as EXISTS server data
@@ -155,19 +155,19 @@ public class Geary.Imap.FolderProperties : Geary.FolderProperties {
if (uid_next != null && other.uid_next != null && !uid_next.equal_to(other.uid_next)) {
debug("%s FolderProperties changed: UIDNEXT=%s other.UIDNEXT=%s", name,
uid_next.to_string(), other.uid_next.to_string());
-
+
return true;
}
-
+
// UIDVALIDITY changes indicate the entire folder's contents have potentially altered and
// the client needs to reset its local vector
if (uid_validity != null && other.uid_validity != null &&
!uid_validity.equal_to(other.uid_validity)) {
debug("%s FolderProperties changed: UIDVALIDITY=%s other.UIDVALIDITY=%s", name,
uid_validity.to_string(), other.uid_validity.to_string());
-
+
return true;
}
-
+
// Gmail includes Chat messages in STATUS results but not in SELECT/EXAMINE
// results, so message count comparison has to be from the same origin ... use SELECT/EXAMINE
// first, as it's more authoritative in many ways
@@ -176,21 +176,21 @@ public class Geary.Imap.FolderProperties : Geary.FolderProperties {
if (diff != 0) {
debug("%s FolderProperties changed: SELECT/EXAMINE=%d other.SELECT/EXAMINE=%d diff=%d",
name, select_examine_messages, other.select_examine_messages, diff);
-
+
return true;
}
}
-
+
if (status_messages >= 0 && other.status_messages >= 0) {
int diff = status_messages - other.status_messages;
if (diff != 0) {
debug("%s FolderProperties changed: STATUS=%d other.STATUS=%d diff=%d", name,
status_messages, other.status_messages, diff);
-
+
return true;
}
}
-
+
return false;
}
@@ -207,39 +207,39 @@ public class Geary.Imap.FolderProperties : Geary.FolderProperties {
uid_validity = status.uid_validity;
uid_next = status.uid_next;
}
-
+
public void set_status_message_count(int messages, bool force) {
if (messages < 0)
return;
-
+
status_messages = messages;
-
+
// select/examine more authoritative than status, unless the caller knows otherwise
if (force || (select_examine_messages < 0))
email_total = messages;
}
-
+
public void set_select_examine_message_count(int messages) {
if (messages < 0)
return;
-
+
select_examine_messages = messages;
-
+
// select/examine more authoritative than status
email_total = messages;
}
-
+
public void set_status_unseen(int count) {
// drop unknown counts, especially if known is held here
if (count < 0)
return;
-
+
unseen = count;
-
+
// update base class value (which clients see)
email_unread = count;
}
-
+
public void set_from_session_capabilities(Capabilities capabilities) {
create_never_returns_id = !capabilities.supports_uidplus();
}
diff --git a/src/engine/imap/api/imap-folder-session.vala b/src/engine/imap/api/imap-folder-session.vala
index 1b9e11a8..af013a9a 100644
--- a/src/engine/imap/api/imap-folder-session.vala
+++ b/src/engine/imap/api/imap-folder-session.vala
@@ -346,11 +346,11 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
private bool retry_bad_header_fields_response(Command cmd, StatusResponse response) {
if (response.status != Status.BAD)
return false;
-
+
FetchCommand? fetch = cmd as FetchCommand;
if (fetch == null)
return false;
-
+
foreach (FetchBodyDataSpecifier body_specifier in fetch.for_body_data_specifiers) {
switch (body_specifier.section_part) {
case FetchBodyDataSpecifier.SectionPart.HEADER_FIELDS:
@@ -363,21 +363,21 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
break;
}
}
-
+
return false;
}
-
+
private void throw_on_failed_status(StatusResponse response, Command cmd) throws Error {
assert(response.is_completion);
-
+
switch (response.status) {
case Status.OK:
return;
-
+
case Status.NO:
throw new ImapError.SERVER_ERROR("Request %s failed on %s: %s", cmd.to_string(),
to_string(), response.to_string());
-
+
case Status.BAD: {
// if a FetchBodyDataSpecifier is used to request for a header field BAD is returned,
// could be a specific formatting mistake some servers make of not allowing a space
@@ -388,20 +388,20 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
// If so, then enable a hack to work around this and retry the FETCH
if (retry_bad_header_fields_response(cmd, response)) {
imap_header_fields_hack = true;
-
+
throw new FolderError.RETRY("BAD response to header.fields FETCH BODY, retry with hack");
}
-
+
throw new ImapError.INVALID("Bad request %s on %s: %s", cmd.to_string(),
to_string(), response.to_string());
}
-
+
default:
throw new ImapError.NOT_SUPPORTED("Unknown response status to %s on %s: %s",
cmd.to_string(), to_string(), response.to_string());
}
}
-
+
// Utility method for listing UIDs on the remote within the supplied range
public async Gee.Set<Imap.UID>? list_uids_async(MessageSet msg_set, Cancellable? cancellable)
throws Error {
@@ -420,7 +420,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
return (search_results.size > 0) ? search_results : null;
}
-
+
private Gee.Collection<FetchCommand> assemble_list_commands(Imap.MessageSet msg_set,
Geary.Email.Field fields, out FetchBodyDataSpecifier? header_specifier,
out FetchBodyDataSpecifier? body_specifier, out FetchBodyDataSpecifier? preview_specifier,
@@ -428,7 +428,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
// getting all the fields can require multiple FETCH commands (some servers don't handle
// well putting every required data item into single command), so aggregate FetchCommands
Gee.Collection<FetchCommand> cmds = new Gee.ArrayList<FetchCommand>();
-
+
// if not a UID FETCH, request UIDs for all messages so their EmailIdentifier can be
// created without going back to the database (assuming the messages have already been
// pulled down, not a guarantee); if request is for NONE, that guarantees that the
@@ -436,30 +436,30 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
// listing a range for contents: UID FETCH x:y UID)
if (!msg_set.is_uid || fields == Geary.Email.Field.NONE)
cmds.add(new FetchCommand.data_type(msg_set, FetchDataSpecifier.UID));
-
+
// convert bulk of the "basic" fields into a one or two FETCH commands (some servers have
// exhibited bugs or return NO when too many FETCH data types are combined on a single
// command)
if (fields.requires_any(BASIC_FETCH_FIELDS)) {
Gee.List<FetchDataSpecifier> data_types = new Gee.ArrayList<FetchDataSpecifier>();
fields_to_fetch_data_types(fields, data_types, out header_specifier);
-
+
// Add all simple data types as one FETCH command
if (data_types.size > 0)
cmds.add(new FetchCommand(msg_set, data_types, null));
-
+
// Add all body data types as separate FETCH command
if (header_specifier != null)
cmds.add(new FetchCommand.body_data_type(msg_set, header_specifier));
} else {
header_specifier = null;
}
-
+
// RFC822 BODY is a separate command
if (fields.require(Email.Field.BODY)) {
body_specifier = new FetchBodyDataSpecifier.peek(FetchBodyDataSpecifier.SectionPart.TEXT,
null, -1, -1, null);
-
+
cmds.add(new FetchCommand.body_data_type(msg_set, body_specifier));
} else {
body_specifier = null;
@@ -493,21 +493,21 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
// PROPERTIES and FLAGS are a separate command
if (fields.requires_any(Email.Field.PROPERTIES | Email.Field.FLAGS)) {
Gee.List<FetchDataSpecifier> data_types = new Gee.ArrayList<FetchDataSpecifier>();
-
+
if (fields.require(Geary.Email.Field.PROPERTIES)) {
data_types.add(FetchDataSpecifier.INTERNALDATE);
data_types.add(FetchDataSpecifier.RFC822_SIZE);
}
-
+
if (fields.require(Geary.Email.Field.FLAGS))
data_types.add(FetchDataSpecifier.FLAGS);
-
+
cmds.add(new FetchCommand(msg_set, data_types, null));
}
-
+
return cmds;
}
-
+
// Returns a no-message-id ImapDB.EmailIdentifier with the UID stored in it.
public async Gee.List<Geary.Email>? list_email_async(MessageSet msg_set,
Geary.Email.Field fields,
@@ -527,20 +527,20 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
throw new ImapError.INVALID("No FETCH commands generate for list request %s %s",
msg_set.to_string(), fields.to_list_string());
}
-
+
// Commands prepped, do the fetch and accumulate all the responses
try {
yield exec_commands_async(cmds, fetched, null, cancellable);
} catch (Error err) {
if (err is FolderError.RETRY) {
debug("Retryable server failure detected for %s: %s", to_string(), err.message);
-
+
continue;
}
-
+
throw err;
}
-
+
break;
}
@@ -553,27 +553,27 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
yield Nonblocking.Concurrent.global.schedule_async(() => {
foreach (SequenceNumber seq_num in fetched.keys) {
FetchedData fetched_data = fetched.get(seq_num);
-
+
// the UID should either have been fetched (if using positional addressing) or should
// have come back with the response (if using UID addressing)
UID? uid = fetched_data.data_map.get(FetchDataSpecifier.UID) as UID;
if (uid == null) {
message("Unable to list message #%s on %s: No UID returned from server",
seq_num.to_string(), to_string());
-
+
continue;
}
-
+
try {
Geary.Email email = fetched_data_to_email(to_string(), uid, fetched_data, fields,
header_specifier, body_specifier, preview_specifier, preview_charset_specifier);
if (!email.fields.fulfills(fields)) {
message("%s: %s missing=%s fetched=%s", to_string(), email.id.to_string(),
fields.clear(email.fields).to_list_string(), fetched_data.to_string());
-
+
continue;
}
-
+
email_list.add(email);
} catch (Error err) {
debug("%s: Unable to convert email for %s %s: %s", to_string(), uid.to_string(),
@@ -581,7 +581,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
}
}
}, cancellable);
-
+
return (email_list.size > 0) ? email_list : null;
}
@@ -624,18 +624,18 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
ClientSession session = claim_session();
Gee.List<MessageFlag> flags = new Gee.ArrayList<MessageFlag>();
flags.add(MessageFlag.DELETED);
-
+
Gee.List<Command> cmds = new Gee.ArrayList<Command>();
-
+
// Build STORE command for all MessageSets, see if all are UIDs so we can use UID EXPUNGE
bool all_uid = true;
foreach (MessageSet msg_set in msg_sets) {
if (!msg_set.is_uid)
all_uid = false;
-
+
cmds.add(new StoreCommand(msg_set, flags, StoreCommand.Option.ADD_FLAGS));
}
-
+
// TODO: Only use old-school EXPUNGE when closing folder (or rely on CLOSE to do that work
// for us). See:
// http://redmine.yorba.org/issues/7532
@@ -649,32 +649,32 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
} else {
cmds.add(new ExpungeCommand());
}
-
+
yield exec_commands_async(cmds, null, null, cancellable);
}
-
+
public async void mark_email_async(Gee.List<MessageSet> msg_sets, Geary.EmailFlags? flags_to_add,
Geary.EmailFlags? flags_to_remove, Cancellable? cancellable) throws Error {
Gee.List<MessageFlag> msg_flags_add = new Gee.ArrayList<MessageFlag>();
Gee.List<MessageFlag> msg_flags_remove = new Gee.ArrayList<MessageFlag>();
- MessageFlag.from_email_flags(flags_to_add, flags_to_remove, out msg_flags_add,
+ MessageFlag.from_email_flags(flags_to_add, flags_to_remove, out msg_flags_add,
out msg_flags_remove);
-
+
if (msg_flags_add.size == 0 && msg_flags_remove.size == 0)
return;
-
+
Gee.Collection<Command> cmds = new Gee.ArrayList<Command>();
foreach (MessageSet msg_set in msg_sets) {
if (msg_flags_add.size > 0)
cmds.add(new StoreCommand(msg_set, msg_flags_add, StoreCommand.Option.ADD_FLAGS));
-
+
if (msg_flags_remove.size > 0)
cmds.add(new StoreCommand(msg_set, msg_flags_remove, StoreCommand.Option.REMOVE_FLAGS));
}
-
+
yield exec_commands_async(cmds, null, null, cancellable);
}
-
+
// Returns a mapping of the source UID to the destination UID. If the MessageSet is not for
// UIDs, then null is returned. If the server doesn't support COPYUID, null is returned.
public async Gee.Map<UID, UID>? copy_email_async(MessageSet msg_set, FolderPath destination,
@@ -686,10 +686,10 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
Gee.Map<Command, StatusResponse>? responses = yield exec_commands_async(
Geary.iterate<Command>(cmd).to_array_list(), null, null, cancellable);
-
+
if (!responses.has_key(cmd))
return null;
-
+
StatusResponse response = responses.get(cmd);
if (response.response_code != null && msg_set.is_uid) {
Gee.List<UID>? src_uids = null;
@@ -699,30 +699,30 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
} catch (ImapError ierr) {
debug("Unable to retrieve COPYUID UIDs: %s", ierr.message);
}
-
+
if (!Collection.is_empty(src_uids) && !Collection.is_empty(dst_uids)) {
Gee.Map<UID, UID> copyuids = new Gee.HashMap<UID, UID>();
int ctr = 0;
for (;;) {
UID? src_uid = (ctr < src_uids.size) ? src_uids[ctr] : null;
UID? dst_uid = (ctr < dst_uids.size) ? dst_uids[ctr] : null;
-
+
if (src_uid != null && dst_uid != null)
copyuids.set(src_uid, dst_uid);
else
break;
-
+
ctr++;
}
-
+
if (copyuids.size > 0)
return copyuids;
}
}
-
+
return null;
}
-
+
public async Gee.SortedSet<Imap.UID>? search_async(SearchCriteria criteria, Cancellable? cancellable)
throws Error {
// always perform a UID SEARCH
@@ -746,7 +746,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
Gee.List<FetchDataSpecifier> data_types_list, out FetchBodyDataSpecifier? header_specifier) {
// pack all the needed headers into a single FetchBodyDataType
string[] field_names = new string[0];
-
+
// The assumption here is that because ENVELOPE is such a common fetch command, the
// server will have optimizations for it, whereas if we called for each header in the
// envelope separately, the server has to chunk harder parsing the RFC822 header ... have
@@ -755,45 +755,45 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
if (fields.is_all_set(Geary.Email.Field.ENVELOPE)) {
data_types_list.add(FetchDataSpecifier.ENVELOPE);
field_names += "References";
-
+
// remove those flags and process any remaining
fields = fields.clear(Geary.Email.Field.ENVELOPE);
}
-
+
foreach (Geary.Email.Field field in Geary.Email.Field.all()) {
switch (fields & field) {
case Geary.Email.Field.DATE:
field_names += "Date";
break;
-
+
case Geary.Email.Field.ORIGINATORS:
field_names += "From";
field_names += "Sender";
field_names += "Reply-To";
break;
-
+
case Geary.Email.Field.RECEIVERS:
field_names += "To";
field_names += "Cc";
field_names += "Bcc";
break;
-
+
case Geary.Email.Field.REFERENCES:
field_names += "References";
field_names += "Message-ID";
field_names += "In-Reply-To";
break;
-
+
case Geary.Email.Field.SUBJECT:
field_names += "Subject";
break;
-
+
case Geary.Email.Field.HEADER:
// TODO: If the entire header is being pulled, then no need to pull down partial
// headers; simply get them all and decode what is needed directly
data_types_list.add(FetchDataSpecifier.RFC822_HEADER);
break;
-
+
case Geary.Email.Field.NONE:
case Geary.Email.Field.BODY:
case Geary.Email.Field.PROPERTIES:
@@ -801,12 +801,12 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
case Geary.Email.Field.PREVIEW:
// not set or fetched separately
break;
-
+
default:
assert_not_reached();
}
}
-
+
// convert field names into single FetchBodyDataType object
if (field_names.length > 0) {
header_specifier = new FetchBodyDataSpecifier.peek(
@@ -817,7 +817,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
header_specifier = null;
}
}
-
+
private static Geary.Email fetched_data_to_email(string folder_name, UID uid,
FetchedData fetched_data, Geary.Email.Field required_fields,
FetchBodyDataSpecifier? header_specifier, FetchBodyDataSpecifier? body_specifier,
@@ -826,22 +826,22 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
// database) is unknown at this time; this means ImapDB *must* create a new EmailIdentifier
// for this email after create/merge is completed
Geary.Email email = new Geary.Email(new ImapDB.EmailIdentifier.no_message_id(uid));
-
+
// accumulate these to submit Imap.EmailProperties all at once
InternalDate? internaldate = null;
RFC822.Size? rfc822_size = null;
-
+
// accumulate these to submit References all at once
RFC822.MessageID? message_id = null;
RFC822.MessageIDList? in_reply_to = null;
RFC822.MessageIDList? references = null;
-
+
// loop through all available FetchDataTypes and gather converted data
foreach (FetchDataSpecifier data_type in fetched_data.data_map.keys) {
MessageData? data = fetched_data.data_map.get(data_type);
if (data == null)
continue;
-
+
switch (data_type) {
case FetchDataSpecifier.ENVELOPE:
Envelope envelope = (Envelope) data;
@@ -859,37 +859,37 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
message_id = envelope.message_id;
in_reply_to = envelope.in_reply_to;
break;
-
+
case FetchDataSpecifier.RFC822_HEADER:
email.set_message_header((RFC822.Header) data);
break;
-
+
case FetchDataSpecifier.RFC822_TEXT:
email.set_message_body((RFC822.Text) data);
break;
-
+
case FetchDataSpecifier.RFC822_SIZE:
rfc822_size = (RFC822.Size) data;
break;
-
+
case FetchDataSpecifier.FLAGS:
email.set_flags(new Imap.EmailFlags((MessageFlags) data));
break;
-
+
case FetchDataSpecifier.INTERNALDATE:
internaldate = (InternalDate) data;
break;
-
+
default:
// everything else dropped on the floor (not applicable to Geary.Email)
break;
}
}
-
+
// Only set PROPERTIES if all have been found
if (internaldate != null && rfc822_size != null)
email.set_email_properties(new Geary.Imap.EmailProperties(internaldate, rfc822_size));
-
+
// if the header was requested, convert its fields now
bool has_header_specifier = fetched_data.body_data_map.has_key(header_specifier);
if (header_specifier != null && !has_header_specifier) {
@@ -944,20 +944,20 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
string? value = headers.get_header("To");
if (!String.is_empty(value))
to = new RFC822.MailboxAddresses.from_rfc822_string(value);
-
+
RFC822.MailboxAddresses? cc = null;
value = headers.get_header("Cc");
if (!String.is_empty(value))
cc = new RFC822.MailboxAddresses.from_rfc822_string(value);
-
+
RFC822.MailboxAddresses? bcc = null;
value = headers.get_header("Bcc");
if (!String.is_empty(value))
bcc = new RFC822.MailboxAddresses.from_rfc822_string(value);
-
+
email.set_receivers(to, cc, bcc);
}
-
+
// REFERENCES
// (Note that it's possible the request used an IMAP ENVELOPE, in which case only the
// References header will be present if REFERENCES were required, which is why
@@ -967,19 +967,19 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
if (!String.is_empty(value))
message_id = new RFC822.MessageID(value);
}
-
+
if (in_reply_to == null) {
string? value = headers.get_header("In-Reply-To");
if (!String.is_empty(value))
in_reply_to = new RFC822.MessageIDList.from_rfc822_string(value);
}
-
+
if (references == null) {
string? value = headers.get_header("References");
if (!String.is_empty(value))
references = new RFC822.MessageIDList.from_rfc822_string(value);
}
-
+
// SUBJECT
// Unlike DATE, allow for empty subjects
if (required_but_not_set(Geary.Email.Field.SUBJECT, required_fields, email)) {
@@ -990,7 +990,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
email.set_message_subject(null);
}
}
-
+
// It's possible for all these fields to be null even though they were requested from
// the server, so use requested fields for determination
if (required_but_not_set(Geary.Email.Field.REFERENCES, required_fields, email))
@@ -1086,10 +1086,10 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
if (response.status == Status.OK && response.response_code != null &&
response.response_code.get_response_code_type().is_value("appenduid")) {
UID new_id = new UID.checked(response.response_code.get_as_string(2).as_int64());
-
+
return new ImapDB.EmailIdentifier.no_message_id(new_id);
}
-
+
// We didn't get a UID back from the server.
return null;
}
diff --git a/src/engine/imap/command/imap-capability-command.vala
b/src/engine/imap/command/imap-capability-command.vala
index 8785bfad..0f353997 100644
--- a/src/engine/imap/command/imap-capability-command.vala
+++ b/src/engine/imap/command/imap-capability-command.vala
@@ -12,7 +12,7 @@
public class Geary.Imap.CapabilityCommand : Command {
public const string NAME = "capability";
-
+
public CapabilityCommand() {
base (NAME);
}
diff --git a/src/engine/imap/command/imap-close-command.vala b/src/engine/imap/command/imap-close-command.vala
index 1026d417..1dac6a97 100644
--- a/src/engine/imap/command/imap-close-command.vala
+++ b/src/engine/imap/command/imap-close-command.vala
@@ -10,7 +10,7 @@
public class Geary.Imap.CloseCommand : Command {
public const string NAME = "close";
-
+
public CloseCommand() {
base (NAME);
}
diff --git a/src/engine/imap/command/imap-compress-command.vala
b/src/engine/imap/command/imap-compress-command.vala
index 1c15031b..98ca05ff 100644
--- a/src/engine/imap/command/imap-compress-command.vala
+++ b/src/engine/imap/command/imap-compress-command.vala
@@ -10,9 +10,9 @@
public class Geary.Imap.CompressCommand : Command {
public const string NAME = "compress";
-
+
public const string ALGORITHM_DEFLATE = "deflate";
-
+
public CompressCommand(string algorithm) {
base (NAME, { algorithm });
}
diff --git a/src/engine/imap/command/imap-list-return-parameter.vala
b/src/engine/imap/command/imap-list-return-parameter.vala
index e4207c65..04bb62a8 100644
--- a/src/engine/imap/command/imap-list-return-parameter.vala
+++ b/src/engine/imap/command/imap-list-return-parameter.vala
@@ -20,7 +20,7 @@ public class Geary.Imap.ListReturnParameter : ListParameter {
* See [[https://tools.ietf.org/html/rfc6154]]
*/
public const string SPECIAL_USE = "special-use";
-
+
/**
* Creates an empty {@link ListReturnParameter}.
*
@@ -28,7 +28,7 @@ public class Geary.Imap.ListReturnParameter : ListParameter {
*/
public ListReturnParameter() {
}
-
+
public void add_special_use() {
add(StringParameter.get_best_for_unchecked(SPECIAL_USE));
}
diff --git a/src/engine/imap/command/imap-login-command.vala b/src/engine/imap/command/imap-login-command.vala
index 5eb324b0..c3114780 100644
--- a/src/engine/imap/command/imap-login-command.vala
+++ b/src/engine/imap/command/imap-login-command.vala
@@ -10,11 +10,11 @@
public class Geary.Imap.LoginCommand : Command {
public const string NAME = "login";
-
+
public LoginCommand(string user, string pass) {
base (NAME, { user, pass });
}
-
+
public override string to_string() {
return "%s %s <user> <pass>".printf(tag.to_string(), name);
}
diff --git a/src/engine/imap/command/imap-logout-command.vala
b/src/engine/imap/command/imap-logout-command.vala
index ffc40600..7e81fc54 100644
--- a/src/engine/imap/command/imap-logout-command.vala
+++ b/src/engine/imap/command/imap-logout-command.vala
@@ -10,7 +10,7 @@
public class Geary.Imap.LogoutCommand : Command {
public const string NAME = "logout";
-
+
public LogoutCommand() {
base (NAME);
}
diff --git a/src/engine/imap/command/imap-message-set.vala b/src/engine/imap/command/imap-message-set.vala
index d7951031..5957c695 100644
--- a/src/engine/imap/command/imap-message-set.vala
+++ b/src/engine/imap/command/imap-message-set.vala
@@ -19,97 +19,97 @@ public class Geary.Imap.MessageSet : BaseObject {
// is set to keep max. command length somewhere under 1K (including tag, command, parameters,
// etc.)
private const int MAX_SPARSE_VALUES_PER_SET = 50;
-
+
private delegate void ParserCallback(int64 value) throws ImapError;
-
+
/**
* True if the {@link MessageSet} was created with a UID or a UID range.
*
* For {@link Command}s that accept MessageSets, they will use a UID variant
*/
public bool is_uid { get; private set; default = false; }
-
+
private string value { get; private set; }
-
+
public MessageSet(SequenceNumber seq_num) {
assert(seq_num.value > 0);
-
+
value = seq_num.serialize();
}
-
+
public MessageSet.uid(UID uid) {
assert(uid.value > 0);
-
+
value = uid.serialize();
is_uid = true;
}
-
+
public MessageSet.range_by_count(SequenceNumber low_seq_num, int count) {
assert(low_seq_num.value > 0);
assert(count > 0);
-
+
value = (count > 1)
? "%s:%s".printf(low_seq_num.value.to_string(), (low_seq_num.value + count - 1).to_string())
: low_seq_num.serialize();
}
-
+
public MessageSet.range_by_first_last(SequenceNumber low_seq_num, SequenceNumber high_seq_num) {
assert(low_seq_num.value > 0);
assert(high_seq_num.value > 0);
-
+
// correct range problems (i.e. last before first)
if (low_seq_num.value > high_seq_num.value) {
SequenceNumber swap = low_seq_num;
low_seq_num = high_seq_num;
high_seq_num = swap;
}
-
+
value = (!low_seq_num.equal_to(high_seq_num))
? "%s:%s".printf(low_seq_num.serialize(), high_seq_num.serialize())
: low_seq_num.serialize();
}
-
+
public MessageSet.uid_range(UID low, UID high) {
assert(low.value > 0);
assert(high.value > 0);
-
+
// correct ordering
if (low.value > high.value) {
UID swap = low;
low = high;
high = swap;
}
-
+
if (low.equal_to(high))
value = low.serialize();
else
value = "%s:%s".printf(low.serialize(), high.serialize());
-
+
is_uid = true;
}
-
+
public MessageSet.range_to_highest(SequenceNumber low_seq_num) {
assert(low_seq_num.value > 0);
-
+
value = "%s:*".printf(low_seq_num.serialize());
}
-
+
public MessageSet.uid_range_to_highest(UID low) {
assert(low.value > 0);
-
+
value = "%s:*".printf(low.serialize());
is_uid = true;
}
-
+
public MessageSet.custom(string custom) {
value = custom;
}
-
+
public MessageSet.uid_custom(string custom) {
value = custom;
is_uid = true;
}
-
+
/**
* Parses a string representing a {@link MessageSet} into a List of {@link SequenceNumber}s.
*
@@ -122,10 +122,10 @@ public class Geary.Imap.MessageSet : BaseObject {
public static Gee.List<SequenceNumber>? parse(string str) throws ImapError {
Gee.List<SequenceNumber> seq_nums = new Gee.ArrayList<SequenceNumber>();
parse_string(str, (value) => { seq_nums.add(new SequenceNumber.checked(value)); });
-
+
return seq_nums.size > 0 ? seq_nums : null;
}
-
+
/**
* Parses a string representing a {@link MessageSet} into a List of {@link UID}s.
*
@@ -145,69 +145,69 @@ public class Geary.Imap.MessageSet : BaseObject {
public static Gee.List<UID>? uid_parse(string str) throws ImapError {
Gee.List<UID> uids = new Gee.ArrayList<UID>();
parse_string(str, (value) => { uids.add(new UID.checked(value)); });
-
+
return uids.size > 0 ? uids : null;
}
-
+
private static void parse_string(string str, ParserCallback cb) throws ImapError {
StringBuilder acc = new StringBuilder();
int64 start_range = -1;
bool in_range = false;
-
+
unichar ch;
int index = 0;
while (str.get_next_char(ref index, out ch)) {
// if number, add to accumulator
if (ch.isdigit()) {
acc.append_unichar(ch);
-
+
continue;
}
-
+
// look for special characters and deal with them
switch (ch) {
case ':':
// range separator
if (in_range)
throw new ImapError.INVALID("Bad range specifier in message set \"%s\"", str);
-
+
in_range = true;
-
+
// store current accumulated value as start of range
start_range = int64.parse(acc.str);
acc = new StringBuilder();
break;
-
+
case ',':
// number separator
-
+
// if in range, treat as end-of-range
if (in_range) {
// don't be forgiving here
if (String.is_empty(acc.str))
throw new ImapError.INVALID("Bad range specifier in message set \"%s\"", str);
-
+
process_range(start_range, int64.parse(acc.str), cb);
in_range = false;
} else {
// Be forgiving here
if (String.is_empty(acc.str))
continue;
-
+
cb(int64.parse(acc.str));
}
-
+
// reset accumulator
acc = new StringBuilder();
break;
-
+
default:
// unknown character, treat with great violence
throw new ImapError.INVALID("Bad character '%s' in message set \"%s\"",
ch.to_string(), str);
}
}
-
+
// report last bit remaining in accumulator
if (!String.is_empty(acc.str)) {
if (in_range)
@@ -218,13 +218,13 @@ public class Geary.Imap.MessageSet : BaseObject {
throw new ImapError.INVALID("Incomplete range specifier in message set \"%s\"", str);
}
}
-
+
private static void process_range(int64 start, int64 end, ParserCallback cb) throws ImapError {
int64 count_by = (start <= end) ? 1 : -1;
for (int64 ctr = start; ctr != end + count_by; ctr += count_by)
cb(ctr);
}
-
+
/**
* Convert a collection of {@link SequenceNumber}s into a list of {@link MessageSet}s.
*
@@ -234,7 +234,7 @@ public class Geary.Imap.MessageSet : BaseObject {
public static Gee.List<MessageSet> sparse(Gee.Collection<SequenceNumber> seq_nums) {
return build_sparse_sets(seq_array_to_int64(seq_nums), false);
}
-
+
/**
* Convert a collection of {@link UID}s into a list of {@link MessageSet}s.
*
@@ -244,48 +244,48 @@ public class Geary.Imap.MessageSet : BaseObject {
public static Gee.List<MessageSet> uid_sparse(Gee.Collection<UID> msg_uids) {
return build_sparse_sets(uid_array_to_int64(msg_uids), true);
}
-
+
// create zero or more MessageSets of no more than MAX_SPARSE_VALUES_PER_SET UIDs/sequence
// numbers
private static Gee.List<MessageSet> build_sparse_sets(int64[] sorted, bool is_uid) {
Gee.List<MessageSet> list = new Gee.ArrayList<MessageSet>();
-
+
int start = 0;
for (;;) {
if (start >= sorted.length)
break;
-
+
int end = (start + MAX_SPARSE_VALUES_PER_SET).clamp(0, sorted.length);
unowned int64[] slice = sorted[start:end];
-
+
string sparse_range = build_sparse_range(slice);
list.add(is_uid ? new MessageSet.uid_custom(sparse_range) : new MessageSet.custom(sparse_range));
-
+
start = end;
}
-
+
return list;
}
-
+
// Builds sparse range of either UID values or sequence numbers. Values should be sorted before
// calling to maximum finding runs.
private static string build_sparse_range(int64[] seq_nums) {
assert(seq_nums.length > 0);
-
+
int64 start_of_span = -1;
int64 last_seq_num = -1;
int span_count = 0;
StringBuilder builder = new StringBuilder();
foreach (int64 seq_num in seq_nums) {
assert(seq_num >= 0);
-
+
// the first number is automatically the start of a span, although it may be a span of one
// (start_of_span < 0 should only happen on first iteration; can't easily break out of
// loop because foreach/Iterator would still require a special case to skip it)
if (start_of_span < 0) {
// start of first span
builder.append(seq_num.to_string());
-
+
start_of_span = seq_num;
span_count = 1;
} else if ((start_of_span + span_count) == seq_num) {
@@ -293,7 +293,7 @@ public class Geary.Imap.MessageSet : BaseObject {
span_count++;
} else {
assert(span_count >= 1);
-
+
// span ends, another begins
if (span_count == 1) {
builder.append_printf(",%s", seq_num.to_string());
@@ -304,56 +304,56 @@ public class Geary.Imap.MessageSet : BaseObject {
builder.append_printf(":%s,%s", (start_of_span + span_count - 1).to_string(),
seq_num.to_string());
}
-
+
start_of_span = seq_num;
span_count = 1;
}
-
+
last_seq_num = seq_num;
}
-
+
// there should always be one seq_num in sorted, so the loop should exit with some state
assert(start_of_span >= 0);
assert(span_count > 0);
assert(last_seq_num >= 0);
-
+
// look for open-ended span
if (span_count == 2)
builder.append_printf(",%s", last_seq_num.to_string());
else if (last_seq_num != start_of_span)
builder.append_printf(":%s", last_seq_num.to_string());
-
+
return builder.str;
}
-
+
private static int64[] seq_array_to_int64(Gee.Collection<SequenceNumber> seq_nums) {
// guarantee sorted (to maximum finding runs in build_sparse_range())
Gee.TreeSet<SequenceNumber> sorted = new Gee.TreeSet<SequenceNumber>();
sorted.add_all(seq_nums);
-
+
// build sorted array
int64[] ret = new int64[sorted.size];
int index = 0;
foreach (SequenceNumber seq_num in sorted)
ret[index++] = (int64) seq_num.value;
-
+
return ret;
}
-
+
private static int64[] uid_array_to_int64(Gee.Collection<UID> msg_uids) {
// guarantee sorted (to maximize finding runs in build_sparse_range())
Gee.TreeSet<UID> sorted = new Gee.TreeSet<UID>();
sorted.add_all(msg_uids);
-
+
// build sorted array
int64[] ret = new int64[sorted.size];
int index = 0;
foreach (UID uid in sorted)
ret[index++] = uid.value;
-
+
return ret;
}
-
+
/**
* Returns the {@link MessageSet} as a {@link Parameter} suitable for inclusion in a
* {@link Command}.
@@ -363,14 +363,14 @@ public class Geary.Imap.MessageSet : BaseObject {
// be a Gmailism...)
return new UnquotedStringParameter(value);
}
-
+
/**
* Returns the {@link MessageSet} in a Gee.List.
*/
public Gee.List<MessageSet> to_list() {
return iterate<MessageSet>(this).to_array_list();
}
-
+
public string to_string() {
return "%s::%s".printf(is_uid ? "UID" : "pos", value);
}
diff --git a/src/engine/imap/command/imap-noop-command.vala b/src/engine/imap/command/imap-noop-command.vala
index 9884a320..4d894723 100644
--- a/src/engine/imap/command/imap-noop-command.vala
+++ b/src/engine/imap/command/imap-noop-command.vala
@@ -12,7 +12,7 @@
public class Geary.Imap.NoopCommand : Command {
public const string NAME = "noop";
-
+
public NoopCommand() {
base (NAME);
}
diff --git a/src/engine/imap/command/imap-search-criteria.vala
b/src/engine/imap/command/imap-search-criteria.vala
index 67c0d604..ff08421a 100644
--- a/src/engine/imap/command/imap-search-criteria.vala
+++ b/src/engine/imap/command/imap-search-criteria.vala
@@ -27,7 +27,7 @@ public class Geary.Imap.SearchCriteria : ListParameter {
if (first != null)
add_all(first.to_parameters());
}
-
+
/**
* Clears the {@link SearchCriteria} and sets the supplied {@link SearchCriterion} to the first
* in the list.
@@ -37,10 +37,10 @@ public class Geary.Imap.SearchCriteria : ListParameter {
public unowned SearchCriteria is_(SearchCriterion first) {
clear();
add_all(first.to_parameters());
-
+
return this;
}
-
+
/**
* AND another {@link SearchCriterion} to the {@link SearchCriteria}.
*
@@ -48,10 +48,10 @@ public class Geary.Imap.SearchCriteria : ListParameter {
*/
public unowned SearchCriteria and(SearchCriterion next) {
add_all(next.to_parameters());
-
+
return this;
}
-
+
/**
* OR two {@link SearchCriterion}s to the {@link SearchCriteria}.
*
@@ -59,10 +59,10 @@ public class Geary.Imap.SearchCriteria : ListParameter {
*/
public unowned SearchCriteria or(SearchCriterion a, SearchCriterion b) {
add_all(SearchCriterion.or(a, b).to_parameters());
-
+
return this;
}
-
+
/**
* NOT another {@link SearchCriterion} to the {@link SearchCriteria}.
*
@@ -70,7 +70,7 @@ public class Geary.Imap.SearchCriteria : ListParameter {
*/
public unowned SearchCriteria not(SearchCriterion next) {
add_all(SearchCriterion.not(next).to_parameters());
-
+
return this;
}
}
diff --git a/src/engine/imap/command/imap-search-criterion.vala
b/src/engine/imap/command/imap-search-criterion.vala
index 7f0d8cb8..70f2f28d 100644
--- a/src/engine/imap/command/imap-search-criterion.vala
+++ b/src/engine/imap/command/imap-search-criterion.vala
@@ -16,7 +16,7 @@
public class Geary.Imap.SearchCriterion : BaseObject {
private Gee.List<Parameter> parameters = new Gee.ArrayList<Parameter>();
-
+
/**
* Create a single simple criterion for the {@link SearchCommand}.
*/
@@ -24,14 +24,14 @@ public class Geary.Imap.SearchCriterion : BaseObject {
if (parameter != null)
parameters.add(parameter);
}
-
+
/**
* Creates a simple search criterion.
*/
public SearchCriterion.simple(string name) {
parameters.add(prep_name(name));
}
-
+
/**
* Create a single criterion with a simple name and custom value.
*/
@@ -39,7 +39,7 @@ public class Geary.Imap.SearchCriterion : BaseObject {
parameters.add(prep_name(name));
parameters.add(value);
}
-
+
/**
* Create a single criterion with a simple name and custom value.
*/
@@ -47,58 +47,58 @@ public class Geary.Imap.SearchCriterion : BaseObject {
parameters.add(prep_name(name));
parameters.add(Parameter.get_for_string(value));
}
-
+
private static Parameter prep_name(string name) {
Parameter? namep = StringParameter.try_get_best_for(name);
if (namep == null) {
warning("Using a search name that requires a literal parameter: %s", name);
namep = new LiteralParameter(new Memory.StringBuffer(name));
}
-
+
return namep;
}
-
+
/**
* The IMAP SEARCH ALL criterion.
*/
public static SearchCriterion all() {
return new SearchCriterion.simple("all");
}
-
+
/**
* The IMAP SEARCH OR criterion, which operates on other {@link SearchCriterion}.
*/
public static SearchCriterion or(SearchCriterion a, SearchCriterion b) {
SearchCriterion criterion = new SearchCriterion.simple("or");
-
+
// add each set of Parameters as lists (which are AND-ed)
criterion.parameters.add(a.to_list_parameter());
criterion.parameters.add(b.to_list_parameter());
-
+
return criterion;
}
-
+
/**
* The IMAP SEARCH NOT criterion.
*/
public static SearchCriterion not(SearchCriterion a) {
return new SearchCriterion.parameter_value("not", a.to_list_parameter());
}
-
+
/**
* The IMAP SEARCH NEW criterion.
*/
public static SearchCriterion new_messages() {
return new SearchCriterion.simple("new");
}
-
+
/**
* The IMAP SEARCH OLD criterion.
*/
public static SearchCriterion old_messages() {
return new SearchCriterion.simple("old");
}
-
+
/**
* The IMAP SEARCH KEYWORD criterion, or if the {@link MessageFlag} has a macro, that value.
*/
@@ -106,10 +106,10 @@ public class Geary.Imap.SearchCriterion : BaseObject {
string? keyword = flag.get_search_keyword(true);
if (keyword != null)
return new SearchCriterion.simple(keyword);
-
+
return new SearchCriterion.parameter_value("keyword", flag.to_parameter());
}
-
+
/**
* The IMAP SEARCH UNKEYWORD criterion, or if the {@link MessageFlag} has a macro, that value.
*/
@@ -117,59 +117,59 @@ public class Geary.Imap.SearchCriterion : BaseObject {
string? keyword = flag.get_search_keyword(false);
if (keyword != null)
return new SearchCriterion.simple(keyword);
-
+
return new SearchCriterion.parameter_value("unkeyword", flag.to_parameter());
}
-
+
/**
* The IMAP SEARCH BEFORE criterion.
*/
public static SearchCriterion before_internaldate(InternalDate internaldate) {
return new SearchCriterion.parameter_value("before", internaldate.to_search_parameter());
}
-
+
/**
* The IMAP SEARCH ON criterion.
*/
public static SearchCriterion on_internaldate(InternalDate internaldate) {
return new SearchCriterion.parameter_value("on", internaldate.to_search_parameter());
}
-
+
/**
* The IMAP SEARCH SINCE criterion.
*/
public static SearchCriterion since_internaldate(InternalDate internaldate) {
return new SearchCriterion.parameter_value("since", internaldate.to_search_parameter());
}
-
+
/**
* The IMAP SEARCH BODY criterion, which searches the body for the string.
*/
public static SearchCriterion body(string value) {
return new SearchCriterion.string_value("body", value);
}
-
+
/**
* The IMAP SEARCH TEXT criterion, which searches the header and body for the string.
*/
public static SearchCriterion text(string value) {
return new SearchCriterion.string_value("text", value);
}
-
+
/**
* The IMAP SEARCH SMALLER criterion.
*/
public static SearchCriterion smaller(uint32 value) {
return new SearchCriterion.parameter_value("smaller", new NumberParameter.uint32(value));
}
-
+
/**
* The IMAP SEARCH LARGER criterion.
*/
public static SearchCriterion larger(uint32 value) {
return new SearchCriterion.parameter_value("larger", new NumberParameter.uint32(value));
}
-
+
/**
* Specifies messages (by sequence number or UID) to limit the IMAP SEARCH to.
*/
@@ -177,7 +177,7 @@ public class Geary.Imap.SearchCriterion : BaseObject {
return msg_set.is_uid ? new SearchCriterion.parameter_value("uid", msg_set.to_parameter())
: new SearchCriterion(msg_set.to_parameter());
}
-
+
/**
* Returns the {@link SearchCriterion} as one or more IMAP {@link Parameter}s.
*
@@ -189,7 +189,7 @@ public class Geary.Imap.SearchCriterion : BaseObject {
public Gee.List<Parameter> to_parameters() {
return parameters;
}
-
+
/**
* Return {@link Parameter}s as a {@link ListParameter} if multiple, a single Parameter
* otherwise.
@@ -197,13 +197,13 @@ public class Geary.Imap.SearchCriterion : BaseObject {
public Parameter to_list_parameter() {
if (parameters.size == 1)
return parameters[0];
-
+
ListParameter listp = new ListParameter();
listp.add_all(parameters);
-
+
return listp;
}
-
+
public string to_string() {
return to_list_parameter().to_string();
}
diff --git a/src/engine/imap/command/imap-starttls-command.vala
b/src/engine/imap/command/imap-starttls-command.vala
index 528f9f24..74e5ad71 100644
--- a/src/engine/imap/command/imap-starttls-command.vala
+++ b/src/engine/imap/command/imap-starttls-command.vala
@@ -10,7 +10,7 @@
public class Geary.Imap.StarttlsCommand : Command {
public const string NAME = "STARTTLS";
-
+
public StarttlsCommand() {
base (NAME);
}
diff --git a/src/engine/imap/imap.vala b/src/engine/imap/imap.vala
index c52866e8..dbc15c20 100644
--- a/src/engine/imap/imap.vala
+++ b/src/engine/imap/imap.vala
@@ -11,7 +11,7 @@ private int init_count = 0;
internal void init() {
if (init_count++ != 0)
return;
-
+
MessageFlag.init();
MailboxAttribute.init();
Tag.init();
diff --git a/src/engine/imap/message/imap-data-format.vala b/src/engine/imap/message/imap-data-format.vala
index 7c56b558..35e490fe 100644
--- a/src/engine/imap/message/imap-data-format.vala
+++ b/src/engine/imap/message/imap-data-format.vala
@@ -61,28 +61,28 @@ public bool is_tag_special(char ch, string? exceptions = null) {
public Quoting is_quoting_required(string str) {
if (String.is_empty(str))
return Quoting.REQUIRED;
-
+
int index = 0;
for (;;) {
char ch = str[index++];
if (ch == String.EOS)
break;
-
+
if (ch > 0x7F)
return Quoting.UNALLOWED;
-
+
switch (ch) {
case '\n':
case '\r':
return Quoting.UNALLOWED;
-
+
default:
if (is_atom_special(ch))
return Quoting.REQUIRED;
break;
}
}
-
+
return Quoting.OPTIONAL;
}
diff --git a/src/engine/imap/message/imap-fetch-body-data-specifier.vala
b/src/engine/imap/message/imap-fetch-body-data-specifier.vala
index b2dda653..19932c61 100644
--- a/src/engine/imap/message/imap-fetch-body-data-specifier.vala
+++ b/src/engine/imap/message/imap-fetch-body-data-specifier.vala
@@ -48,62 +48,62 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
HEADER_FIELDS_NOT,
MIME,
TEXT;
-
+
public string serialize() {
switch (this) {
case NONE:
return "";
-
+
case HEADER:
return "header";
-
+
case HEADER_FIELDS:
return "header.fields";
-
+
case HEADER_FIELDS_NOT:
return "header.fields.not";
-
+
case MIME:
return "mime";
-
+
case TEXT:
return "text";
-
+
default:
assert_not_reached();
}
}
-
+
public static SectionPart deserialize(string value) throws ImapError {
if (String.is_empty(value))
return NONE;
-
+
switch (Ascii.strdown(value)) {
case "header":
return HEADER;
-
+
case "header.fields":
return HEADER_FIELDS;
-
+
case "header.fields.not":
return HEADER_FIELDS_NOT;
-
+
case "mime":
return MIME;
-
+
case "text":
return TEXT;
-
+
default:
throw new ImapError.PARSE_ERROR("Invalid SectionPart name \"%s\"", value);
}
}
-
+
public string to_string() {
return serialize();
}
}
-
+
/**
* The {@link SectionPart} for this FETCH BODY specifier.
*
@@ -111,7 +111,7 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
* well in the future, if necessary.
*/
public SectionPart section_part { get; private set; }
-
+
/**
* When false, indicates that the FETCH BODY specifier is using a hack to operate with
* non-conformant servers.
@@ -122,14 +122,14 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
* @see omit_request_header_fields_space
*/
public bool request_header_fields_space { get; private set; default = true; }
-
+
private int[]? part_number;
private int subset_start;
private int subset_count;
private Gee.TreeSet<string>? field_names;
private bool is_peek;
private string hashable;
-
+
/**
* Create a FetchBodyDataType with the various required and optional parameters specified.
*
@@ -143,7 +143,7 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
int subset_count, string[]? field_names) {
init(section_part, part_number, subset_start, subset_count, field_names, false, false);
}
-
+
/**
* Like FetchBodyDataType, but the /Seen flag will not be set when used on a message.
*/
@@ -151,12 +151,12 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
int subset_count, string[]? field_names) {
init(section_part, part_number, subset_start, subset_count, field_names, true, false);
}
-
+
public FetchBodyDataSpecifier.response(SectionPart section_part, int[]? part_number,
int subset_start, string[]? field_names) {
init(section_part, part_number, subset_start, -1, field_names, false, true);
}
-
+
private void init(SectionPart section_part, int[]? part_number, int subset_start, int subset_count,
string[]? field_names, bool is_peek, bool is_response) {
switch (section_part) {
@@ -164,37 +164,37 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
case SectionPart.HEADER_FIELDS_NOT:
assert(field_names != null && field_names.length > 0);
break;
-
+
default:
assert(field_names == null);
break;
}
-
+
if (subset_start >= 0 && !is_response)
assert(subset_count > 0);
-
+
this.section_part = section_part;
this.part_number = part_number;
this.subset_start = subset_start;
this.subset_count = subset_count;
this.is_peek = is_peek;
-
+
if (field_names != null && field_names.length > 0) {
this.field_names = new Gee.TreeSet<string>(Ascii.strcmp);
foreach (string field_name in field_names) {
string converted = Ascii.strdown(field_name.strip());
-
+
if (!String.is_empty(converted))
this.field_names.add(converted);
}
} else {
this.field_names = null;
}
-
+
// see equal_to() for why the response version is used
hashable = serialize_response();
}
-
+
/**
* Returns the {@link FetchBodyDataSpecifier} in a string ready for a {@link Command}.
*
@@ -208,7 +208,7 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
serialize_field_names(),
serialize_subset(true));
}
-
+
/**
* Returns the {@link FetchBodyDataSpecifier} in a string as it might appear in a
* {@link ServerResponse}.
@@ -228,34 +228,34 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
serialize_field_names(),
serialize_subset(false));
}
-
+
public Parameter to_request_parameter() {
return new AtomParameter(serialize_request());
}
-
+
private string serialize_part_number() {
if (part_number == null || part_number.length == 0)
return "";
-
+
StringBuilder builder = new StringBuilder();
foreach (int part in part_number) {
if (builder.len > 0)
builder.append_c('.');
-
+
builder.append_printf("%d", part);
}
-
+
// if there's a SectionPart that follows, append a period as a separator
if (section_part != SectionPart.NONE)
builder.append_c('.');
-
+
return builder.str;
}
-
+
private string serialize_field_names() {
if (field_names == null || field_names.size == 0)
return "";
-
+
// note that the leading space is supplied here
StringBuilder builder = new StringBuilder(request_header_fields_space ? " (" : "(");
Gee.Iterator<string> iter = field_names.iterator();
@@ -265,10 +265,10 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
builder.append_c(' ');
}
builder.append_c(')');
-
+
return builder.str;
}
-
+
// See note at serialize_response for reason is_request is necessary.
// Note that this could've been formed with the .response() ctor, which doesn't pass along
// subset_count (because it's unknown), so this simply uses subset_start in that case
@@ -278,7 +278,7 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
else
return (subset_start < 0) ? "" : "<%d>".printf(subset_start);
}
-
+
/**
* Returns true if the {@link StringParameter} is formatted like a
* {@link FetchBodyDataSpecifier}.
@@ -291,10 +291,10 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
*/
public static bool is_fetch_body_data_specifier(StringParameter stringp) {
string strd = stringp.as_lower().strip();
-
+
return strd.has_prefix("body[") || strd.has_prefix("body.peek[");
}
-
+
/**
* Attempts to convert a {@link StringParameter} into a {@link FetchBodyDataSpecifier}.
*
@@ -311,7 +311,7 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
// * Remove quoting (some servers return field names quoted, some don't, Geary never uses them
// when requesting)
string strd = stringp.as_lower().replace("\"", "").strip();
-
+
// Convert full form into two sections: "body[SECTION_STRING]<OCTET_STRING>"
// ^^^^^^^^^^^^^^ optional
// response never contains ".peek", even if specified in request
@@ -326,17 +326,17 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
section_string = (string) section_chars;
octet_string = null;
break;
-
+
case 2:
section_string = (string) section_chars;
octet_string = (string) octet_chars;
break;
-
+
default:
throw new ImapError.PARSE_ERROR("%s is not a FETCH body data type %d", stringp.to_string(),
count);
}
-
+
// convert section string into its parts:
// "PART_STRING (FIELDS_STRING)"
// ^^^^^^^^^^^^^^^^ optional
@@ -347,14 +347,14 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
if (section_string.contains("(")) {
if (section_string.scanf("%[^(](%[^)])", part_chars, fields_chars) != 2)
throw new ImapError.PARSE_ERROR("%s: malformed part/header names", stringp.to_string());
-
+
part_string = (string) part_chars;
fields_string = (string?) fields_chars;
} else {
part_string = section_string;
fields_string = null;
}
-
+
// convert part_string into its part number and section part name
// "#.#.#.SECTION_PART"
// ^^^^^^ optional
@@ -365,13 +365,13 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
bool no_more = false;
for (int ctr = 0; ctr < part_number_tokens.length; ctr++) {
string token = part_number_tokens[ctr];
-
+
// stop treating as numbers when non-digit found (SectionParts contain periods
// too and must be preserved);
if (!no_more && Ascii.is_numeric(token)) {
if (part_number == null)
part_number = new int[0];
-
+
part_number += int.parse(token);
} else {
no_more = true;
@@ -383,9 +383,9 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
} else {
section_part_builder.append(part_string);
}
-
+
SectionPart section_part = SectionPart.deserialize(section_part_builder.str.strip());
-
+
// Convert optional fields_string into an array of field names
string[]? field_names = null;
if (fields_string != null) {
@@ -393,7 +393,7 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
if (field_names.length == 0)
field_names = null;
}
-
+
// If octet_string found, trim surrounding brackets and convert to integer
// The span in the returned response is merely the offset ("1.15" becomes "1") because the
// associated literal data specifies its own length)
@@ -403,17 +403,17 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
throw new ImapError.PARSE_ERROR("Improperly formed octet \"%s\" in %s", octet_string,
stringp.to_string());
}
-
+
if (subset_start < 0) {
throw new ImapError.PARSE_ERROR("Invalid octet count %d in %s", subset_start,
stringp.to_string());
}
}
-
+
return new FetchBodyDataSpecifier.response(section_part, part_number, subset_start,
field_names);
}
-
+
/**
* Omit the space between "header.fields" and the list of email headers.
*
@@ -432,7 +432,7 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
public void omit_request_header_fields_space() {
request_header_fields_space = false;
}
-
+
/**
* {@link FetchBodyDataSpecifier}s are considered equal if they're serialized responses are
* equal.
@@ -445,14 +445,14 @@ public class Geary.Imap.FetchBodyDataSpecifier : BaseObject, Gee.Hashable<FetchB
public bool equal_to(FetchBodyDataSpecifier other) {
if (this == other)
return true;
-
+
return hashable == other.hashable;
}
-
+
public uint hash() {
return str_hash(hashable);
}
-
+
// Return serialize_request() because it's a more fuller representation than what the server returns
public string to_string() {
return serialize_request();
diff --git a/src/engine/imap/message/imap-fetch-data-specifier.vala
b/src/engine/imap/message/imap-fetch-data-specifier.vala
index d9e38497..5d05c674 100644
--- a/src/engine/imap/message/imap-fetch-data-specifier.vala
+++ b/src/engine/imap/message/imap-fetch-data-specifier.vala
@@ -30,53 +30,53 @@ public enum Geary.Imap.FetchDataSpecifier {
FAST,
ALL,
FULL;
-
+
public string to_string() {
switch (this) {
case UID:
return "uid";
-
+
case FLAGS:
return "flags";
-
+
case INTERNALDATE:
return "internaldate";
-
+
case ENVELOPE:
return "envelope";
-
+
case BODYSTRUCTURE:
return "bodystructure";
-
+
case BODY:
return "body";
-
+
case RFC822:
return "rfc822";
-
+
case RFC822_HEADER:
return "rfc822.header";
-
+
case RFC822_SIZE:
return "rfc822.size";
-
+
case RFC822_TEXT:
return "rfc822.text";
-
+
case FAST:
return "fast";
-
+
case ALL:
return "all";
-
+
case FULL:
return "full";
-
+
default:
assert_not_reached();
}
}
-
+
/**
* Decodes a {@link StringParameter} into a {@link FetchDataSpecifier}.
*/
@@ -84,56 +84,56 @@ public enum Geary.Imap.FetchDataSpecifier {
switch (strparam.as_lower()) {
case "uid":
return UID;
-
+
case "flags":
return FLAGS;
-
+
case "internaldate":
return INTERNALDATE;
-
+
case "envelope":
return ENVELOPE;
-
+
case "bodystructure":
return BODYSTRUCTURE;
-
+
case "body":
return BODY;
-
+
case "rfc822":
return RFC822;
-
+
case "rfc822.header":
return RFC822_HEADER;
-
+
case "rfc822.size":
return RFC822_SIZE;
-
+
case "rfc822.text":
return RFC822_TEXT;
-
+
case "fast":
return FAST;
-
+
case "all":
return ALL;
-
+
case "full":
return FULL;
-
+
default:
throw new ImapError.PARSE_ERROR("\"%s\" is not a valid fetch-command data item",
strparam.to_string());
}
}
-
+
/**
* Turns this {@link FetchDataSpecifier} into a {@link StringParameter} for transmission.
*/
public StringParameter to_parameter() {
return new AtomParameter(to_string());
}
-
+
/**
* Returns the appropriate {@link FetchDataDecoder} for this {@link FetchDataSpecifier}.
*
@@ -146,28 +146,28 @@ public enum Geary.Imap.FetchDataSpecifier {
switch (this) {
case UID:
return new UIDDecoder();
-
+
case FLAGS:
return new MessageFlagsDecoder();
-
+
case ENVELOPE:
return new EnvelopeDecoder();
-
+
case INTERNALDATE:
return new InternalDateDecoder();
-
+
case RFC822_SIZE:
return new RFC822SizeDecoder();
-
+
case RFC822_HEADER:
return new RFC822HeaderDecoder();
-
+
case RFC822_TEXT:
return new RFC822TextDecoder();
-
+
case RFC822:
return new RFC822FullDecoder();
-
+
default:
return null;
}
diff --git a/src/engine/imap/message/imap-flag.vala b/src/engine/imap/message/imap-flag.vala
index 96dbfa56..6edf0028 100644
--- a/src/engine/imap/message/imap-flag.vala
+++ b/src/engine/imap/message/imap-flag.vala
@@ -28,11 +28,11 @@ public abstract class Geary.Imap.Flag : BaseObject, Gee.Hashable<Geary.Imap.Flag
public bool is_system() {
return value[0] == '\\';
}
-
+
public bool equals_string(string value) {
return Ascii.stri_equal(this.value, value);
}
-
+
public bool equal_to(Geary.Imap.Flag flag) {
return (flag == this) ? true : flag.equals_string(value);
}
@@ -47,7 +47,7 @@ public abstract class Geary.Imap.Flag : BaseObject, Gee.Hashable<Geary.Imap.Flag
public uint hash() {
return Ascii.stri_hash(value);
}
-
+
public string to_string() {
return value;
}
diff --git a/src/engine/imap/message/imap-flags.vala b/src/engine/imap/message/imap-flags.vala
index 4ec67064..b2f5956b 100644
--- a/src/engine/imap/message/imap-flags.vala
+++ b/src/engine/imap/message/imap-flags.vala
@@ -11,22 +11,22 @@
public abstract class Geary.Imap.Flags : Geary.MessageData.AbstractMessageData, Geary.Imap.MessageData,
Gee.Hashable<Geary.Imap.Flags> {
public int size { get { return list.size; } }
-
+
protected Gee.Set<Flag> list;
-
+
public Flags(Gee.Collection<Flag> flags) {
list = new Gee.HashSet<Flag>();
list.add_all(flags);
}
-
+
public bool contains(Flag flag) {
return list.contains(flag);
}
-
+
public Gee.Set<Flag> get_all() {
return list.read_only_view;
}
-
+
/**
* Returns the flags in serialized form, which is each flag separated by a space (legal in
* IMAP, as flags must be atoms and atoms prohibit spaces).
@@ -34,7 +34,7 @@ public abstract class Geary.Imap.Flags : Geary.MessageData.AbstractMessageData,
public virtual string serialize() {
return to_string();
}
-
+
/**
* Returns a {@link ListParameter} representation of these flags.
*
@@ -50,32 +50,32 @@ public abstract class Geary.Imap.Flags : Geary.MessageData.AbstractMessageData,
message("Unable to parameterize flag \"%s\": %s", flag.to_string(), ierr.message);
}
}
-
+
return listp;
}
-
+
public bool equal_to(Geary.Imap.Flags other) {
if (this == other)
return true;
-
+
if (other.size != size)
return false;
-
+
return Geary.traverse<Flag>(list).all(f => other.contains(f));
}
-
+
public override string to_string() {
StringBuilder builder = new StringBuilder();
foreach (Flag flag in list) {
if (!String.is_empty(builder.str))
builder.append_c(' ');
-
+
builder.append(flag.value);
}
-
+
return builder.str;
}
-
+
public uint hash() {
return Ascii.stri_hash(to_string());
}
diff --git a/src/engine/imap/message/imap-internal-date.vala b/src/engine/imap/message/imap-internal-date.vala
index 50317e21..ad01e6d2 100644
--- a/src/engine/imap/message/imap-internal-date.vala
+++ b/src/engine/imap/message/imap-internal-date.vala
@@ -22,30 +22,30 @@ public class Geary.Imap.InternalDate : Geary.MessageData.AbstractMessageData, Ge
private const string[] EN_US_MON = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
-
+
private const string[] EN_US_MON_DOWN = {
"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"
};
-
+
public DateTime value { get; private set; }
public string? original { get; private set; default = null; }
-
+
private InternalDate(string original, DateTime datetime) {
this.original = original;
value = datetime;
}
-
+
public InternalDate.from_date_time(DateTime datetime) throws ImapError {
value = datetime;
}
-
+
public static InternalDate decode(string internaldate) throws ImapError {
if (String.is_empty(internaldate))
throw new ImapError.PARSE_ERROR("Invalid INTERNALDATE: empty string");
-
+
if (internaldate.length > 64)
throw new ImapError.PARSE_ERROR("Invalid INTERNALDATE: too long (%d)", internaldate.length);
-
+
// Alas, GMime.utils_header_decode_date() is too forgiving for our needs, so do it manually
int day, year, hour, min, sec;
char mon[4] = { 0 };
@@ -54,7 +54,7 @@ public class Geary.Imap.InternalDate : Geary.MessageData.AbstractMessageData, Ge
out min, out sec, tz);
if (count != 6 && count != 7)
throw new ImapError.PARSE_ERROR("Invalid INTERNALDATE \"%s\": too few fields (%d)",
internaldate, count);
-
+
// check numerical ranges; this does not verify this is an actual date, DateTime will do
// that (and round upward, which has to be accepted)
if (!Numeric.int_in_range_inclusive(day, 1, 31)
@@ -64,30 +64,30 @@ public class Geary.Imap.InternalDate : Geary.MessageData.AbstractMessageData, Ge
|| year < 1970) {
throw new ImapError.PARSE_ERROR("Invalid INTERNALDATE \"%s\": bad numerical range",
internaldate);
}
-
+
// check month (this catches localization problems)
int month = -1;
string mon_down = Ascii.strdown(((string) mon));
for (int ctr = 0; ctr < EN_US_MON_DOWN.length; ctr++) {
if (mon_down == EN_US_MON_DOWN[ctr]) {
month = ctr;
-
+
break;
}
}
-
+
if (month < 0)
throw new ImapError.PARSE_ERROR("Invalid INTERNALDATE \"%s\": bad month", internaldate);
-
+
// TODO: verify timezone
-
+
// if no timezone listed, ISO 8601 says to use local time
TimeZone timezone = (tz[0] != '\0') ? new TimeZone((string) tz) : new TimeZone.local();
-
+
// assemble into DateTime, which validates the time as well (this is why we want to keep
// original around, for other reasons) ... month is 1-based in DateTime
DateTime datetime = new DateTime(timezone, year, month + 1, day, hour, min, sec);
-
+
return new InternalDate(internaldate, datetime);
}
@@ -97,14 +97,14 @@ public class Geary.Imap.InternalDate : Geary.MessageData.AbstractMessageData, Ge
public time_t to_time_t () {
return Time.datetime_to_time_t(this.value);
}
-
+
/**
* Returns the {@link InternalDate} as a {@link Parameter}.
*/
public Parameter to_parameter() {
return Parameter.get_for_string(serialize());
}
-
+
/**
* Returns the {@link InternalDate} as a {@link Parameter} for a {@link SearchCriterion}.
*
@@ -113,7 +113,7 @@ public class Geary.Imap.InternalDate : Geary.MessageData.AbstractMessageData, Ge
public Parameter to_search_parameter() {
return Parameter.get_for_string(serialize_for_search());
}
-
+
/**
* Returns the {@link InternalDate}'s string representation.
*
@@ -122,7 +122,7 @@ public class Geary.Imap.InternalDate : Geary.MessageData.AbstractMessageData, Ge
public string serialize() {
return original ?? value.format("%d-%%s-%Y %H:%M:%S %z").printf(get_en_us_mon());
}
-
+
/**
* Returns the {@link InternalDate}'s string representation for a SEARCH command.
*
@@ -135,7 +135,7 @@ public class Geary.Imap.InternalDate : Geary.MessageData.AbstractMessageData, Ge
public string serialize_for_search() {
return value.format("%d-%%s-%Y").printf(get_en_us_mon());
}
-
+
/**
* Because IMAP's INTERNALDATE strings are ''never'' localized (as best as I can gather), so
* need to use en_US appreviated month names, as that's the only value in INTERNALDATE that is
@@ -144,22 +144,22 @@ public class Geary.Imap.InternalDate : Geary.MessageData.AbstractMessageData, Ge
private string get_en_us_mon() {
// month is 1-based inside of DateTime
int mon = (value.get_month() - 1).clamp(0, EN_US_MON.length - 1);
-
+
return EN_US_MON[mon];
}
-
+
public uint hash() {
return value.hash();
}
-
+
public bool equal_to(InternalDate other) {
return value.equal(other.value);
}
-
+
public int compare_to(InternalDate other) {
return value.compare(other.value);
}
-
+
public override string to_string() {
return serialize();
}
diff --git a/src/engine/imap/message/imap-mailbox-specifier.vala
b/src/engine/imap/message/imap-mailbox-specifier.vala
index 6d7c9160..da89bbbc 100644
--- a/src/engine/imap/message/imap-mailbox-specifier.vala
+++ b/src/engine/imap/message/imap-mailbox-specifier.vala
@@ -34,12 +34,12 @@ public class Geary.Imap.MailboxSpecifier : BaseObject, Gee.Hashable<MailboxSpeci
return (_inbox != null) ? _inbox : _inbox = new MailboxSpecifier(CANONICAL_INBOX_NAME);
}
}
-
+
/**
* Decoded mailbox path name.
*/
public string name { get; private set; }
-
+
/**
* Indicates this is the {@link StatusData} for Inbox.
*
@@ -100,7 +100,7 @@ public class Geary.Imap.MailboxSpecifier : BaseObject, Gee.Hashable<MailboxSpeci
public static bool is_inbox_name(string name) {
return Ascii.stri_equal(name, CANONICAL_INBOX_NAME);
}
-
+
/**
* Returns true if the string is the ''canonical'' name of the IMAP Inbox.
*
@@ -163,7 +163,7 @@ public class Geary.Imap.MailboxSpecifier : BaseObject, Gee.Hashable<MailboxSpeci
name = decoded;
is_inbox = is_inbox_name(decoded);
}
-
+
/**
* The mailbox's path as a list of strings.
*
@@ -172,7 +172,7 @@ public class Geary.Imap.MailboxSpecifier : BaseObject, Gee.Hashable<MailboxSpeci
*/
public Gee.List<string> to_list(string? delim) {
Gee.List<string> path = new Gee.LinkedList<string>();
-
+
if (!String.is_empty(delim)) {
string[] split = name.split(delim);
foreach (string str in split) {
@@ -180,10 +180,10 @@ public class Geary.Imap.MailboxSpecifier : BaseObject, Gee.Hashable<MailboxSpeci
path.add(str);
}
}
-
+
if (path.size == 0)
path.add(name);
-
+
return path;
}
@@ -225,13 +225,13 @@ public class Geary.Imap.MailboxSpecifier : BaseObject, Gee.Hashable<MailboxSpeci
public string get_basename(string? delim) {
if (String.is_empty(delim))
return name;
-
+
int index = name.last_index_of(delim);
if (index < 0)
return name;
-
+
string basename = name.substring(index + 1);
-
+
return !String.is_empty(basename) ? basename : name;
}
@@ -249,27 +249,27 @@ public class Geary.Imap.MailboxSpecifier : BaseObject, Gee.Hashable<MailboxSpeci
public uint hash() {
return is_inbox ? Ascii.stri_hash(name) : Ascii.str_hash(name);
}
-
+
public bool equal_to(MailboxSpecifier other) {
if (this == other)
return true;
-
+
if (is_inbox)
return Ascii.stri_equal(name, other.name);
-
+
return Ascii.str_equal(name, other.name);
}
-
+
public int compare_to(MailboxSpecifier other) {
if (this == other)
return 0;
-
+
if (is_inbox && other.is_inbox)
return 0;
-
+
return Ascii.strcmp(name, other.name);
}
-
+
public string to_string() {
return name;
}
diff --git a/src/engine/imap/message/imap-message-flag.vala b/src/engine/imap/message/imap-message-flag.vala
index 819cfb58..19e1cc9c 100644
--- a/src/engine/imap/message/imap-message-flag.vala
+++ b/src/engine/imap/message/imap-message-flag.vala
@@ -19,73 +19,73 @@ public class Geary.Imap.MessageFlag : Geary.Imap.Flag {
public static MessageFlag ANSWERED { get {
if (_answered == null)
_answered = new MessageFlag("\\answered");
-
+
return _answered;
} }
-
+
private static MessageFlag? _deleted = null;
public static MessageFlag DELETED { get {
if (_deleted == null)
_deleted = new MessageFlag("\\deleted");
-
+
return _deleted;
} }
-
+
private static MessageFlag? _draft = null;
public static MessageFlag DRAFT { get {
if (_draft == null)
_draft = new MessageFlag("\\draft");
-
+
return _draft;
} }
-
+
private static MessageFlag? _flagged = null;
public static MessageFlag FLAGGED { get {
if (_flagged == null)
_flagged = new MessageFlag("\\flagged");
-
+
return _flagged;
} }
-
+
private static MessageFlag? _recent = null;
public static MessageFlag RECENT { get {
if (_recent == null)
_recent = new MessageFlag("\\recent");
-
+
return _recent;
} }
-
+
private static MessageFlag? _seen = null;
public static MessageFlag SEEN { get {
if (_seen == null)
_seen = new MessageFlag("\\seen");
-
+
return _seen;
} }
-
+
private static MessageFlag? _allows_new = null;
public static MessageFlag ALLOWS_NEW { get {
if (_allows_new == null)
_allows_new = new MessageFlag("\\*");
-
+
return _allows_new;
} }
-
+
private static MessageFlag? _load_remote_images = null;
public static MessageFlag LOAD_REMOTE_IMAGES { get {
if (_load_remote_images == null)
_load_remote_images = new MessageFlag("LoadRemoteImages");
-
+
return _load_remote_images;
} }
-
+
/**
* Creates an IMAP message (email) named flag.
*/
public MessageFlag(string value) {
base (value);
}
-
+
// Call these at init time to prevent thread issues
internal static void init() {
MessageFlag to_init = ANSWERED;
@@ -97,10 +97,10 @@ public class Geary.Imap.MessageFlag : Geary.Imap.Flag {
to_init = ALLOWS_NEW;
to_init = LOAD_REMOTE_IMAGES;
}
-
+
// Converts a list of email flags to add and remove to a list of message
// flags to add and remove.
- public static void from_email_flags(Geary.EmailFlags? email_flags_add,
+ public static void from_email_flags(Geary.EmailFlags? email_flags_add,
Geary.EmailFlags? email_flags_remove, out Gee.List<MessageFlag> msg_flags_add,
out Gee.List<MessageFlag> msg_flags_remove) {
msg_flags_add = new Gee.ArrayList<MessageFlag>();
@@ -132,7 +132,7 @@ public class Geary.Imap.MessageFlag : Geary.Imap.Flag {
msg_flags_remove.add(MessageFlag.DELETED);
}
}
-
+
/**
* Returns a keyword suitable for the IMAP SEARCH command.
*
@@ -145,22 +145,22 @@ public class Geary.Imap.MessageFlag : Geary.Imap.Flag {
public string? get_search_keyword(bool present) {
if (equal_to(ANSWERED))
return present ? "answered" : "unanswered";
-
+
if (equal_to(DELETED))
return present ? "deleted" : "undeleted";
-
+
if (equal_to(DRAFT))
return present ? "draft" : "undraft";
-
+
if (equal_to(FLAGGED))
return present ? "flagged" : "unflagged";
-
+
if (equal_to(RECENT))
return present ? "recent" : null;
-
+
if (equal_to(SEEN))
return present ? "seen" : "unseen";
-
+
return null;
}
}
diff --git a/src/engine/imap/message/imap-message-flags.vala b/src/engine/imap/message/imap-message-flags.vala
index 281477ea..735d62ed 100644
--- a/src/engine/imap/message/imap-message-flags.vala
+++ b/src/engine/imap/message/imap-message-flags.vala
@@ -16,7 +16,7 @@ public class Geary.Imap.MessageFlags : Geary.Imap.Flags {
public MessageFlags(Gee.Collection<MessageFlag> flags) {
base (flags);
}
-
+
/**
* Create {@link MessageFlags} from a {@link ListParameter} of flag strings.
*/
@@ -24,30 +24,30 @@ public class Geary.Imap.MessageFlags : Geary.Imap.Flags {
Gee.Collection<MessageFlag> list = new Gee.ArrayList<MessageFlag>();
for (int ctr = 0; ctr < listp.size; ctr++)
list.add(new MessageFlag(listp.get_as_string(ctr).ascii));
-
+
return new MessageFlags(list);
}
-
+
/**
* Create {@link MessageFlags} from a flat string of space-delimited flags.
*/
public static MessageFlags deserialize(string? str) {
if (String.is_empty(str))
return new MessageFlags(new Gee.ArrayList<MessageFlag>());
-
+
string[] tokens = str.split(" ");
-
+
Gee.Collection<MessageFlag> flags = new Gee.ArrayList<MessageFlag>();
foreach (string token in tokens)
flags.add(new MessageFlag(token));
-
+
return new MessageFlags(flags);
}
-
+
internal void add(MessageFlag flag) {
list.add(flag);
}
-
+
internal void remove(MessageFlag flag) {
list.remove(flag);
}
diff --git a/src/engine/imap/message/imap-sequence-number.vala
b/src/engine/imap/message/imap-sequence-number.vala
index d14d15a6..48dce2a9 100644
--- a/src/engine/imap/message/imap-sequence-number.vala
+++ b/src/engine/imap/message/imap-sequence-number.vala
@@ -20,12 +20,12 @@ public class Geary.Imap.SequenceNumber : Geary.MessageData.Int64MessageData, Gea
* See [[http://tools.ietf.org/html/rfc3501#section-2.3.1.2]]
*/
public const int64 MIN = 1;
-
+
/**
* Upper limit of a valid {@link SequenceNumber}.
*/
public const int64 MAX = 0xFFFFFFFF;
-
+
/**
* Create a new {@link SequenceNumber}.
*
@@ -37,7 +37,7 @@ public class Geary.Imap.SequenceNumber : Geary.MessageData.Int64MessageData, Gea
public SequenceNumber(int64 value) {
base (value);
}
-
+
/**
* Create a new {@link SequenceNumber}, throwing {@link ImapError.INVALID} if an invalid value
* is passed in.
@@ -48,24 +48,24 @@ public class Geary.Imap.SequenceNumber : Geary.MessageData.Int64MessageData, Gea
public SequenceNumber.checked(int64 value) throws ImapError {
if (!is_value_valid(value))
throw new ImapError.INVALID("Invalid sequence number %s", value.to_string());
-
+
base (value);
}
-
+
/**
* Defined as {@link MessageData.Int64MessageData.value} >= {@link MIN} and <= {@link MAX}.
*/
public static bool is_value_valid(int64 value) {
return value >= MIN && value <= MAX;
}
-
+
/**
* Defined as {@link MessageData.Int64MessageData.value} >= {@link MIN} and <= {@link MAX}.
*/
public bool is_valid() {
return is_value_valid(value);
}
-
+
/**
* Returns a new {@link SequenceNumber} that is one lower than this value.
*
@@ -74,7 +74,7 @@ public class Geary.Imap.SequenceNumber : Geary.MessageData.Int64MessageData, Gea
public SequenceNumber? dec() {
return (value > MIN) ? new SequenceNumber(value - 1) : null;
}
-
+
/**
* Returns a new {@link SequenceNumber} that is one lower than this value.
*
@@ -83,7 +83,7 @@ public class Geary.Imap.SequenceNumber : Geary.MessageData.Int64MessageData, Gea
public SequenceNumber dec_clamped() {
return (value > MIN) ? new SequenceNumber(value - 1) : new SequenceNumber(MIN);
}
-
+
/**
* Returns the {@link SequenceNumber} after the suppled SequenceNumber has been removed from
* the vector of messages.
@@ -98,21 +98,21 @@ public class Geary.Imap.SequenceNumber : Geary.MessageData.Int64MessageData, Gea
// shifts downward ... dec() returns null if dropping below 1, which is suitable here
return dec();
}
-
+
if (comparison == 0) {
// the appended message was removed outright, so drop from new list
return null;
}
-
+
// otherwise, removed position was higher than appended message's, so it doesn't
// shift
return this;
}
-
+
public virtual int compare_to(SequenceNumber other) {
return (int) (value - other.value).clamp(-1, 1);
}
-
+
public string serialize() {
return value.to_string();
}
diff --git a/src/engine/imap/message/imap-status-data-type.vala
b/src/engine/imap/message/imap-status-data-type.vala
index d2f169b5..a6fc0494 100644
--- a/src/engine/imap/message/imap-status-data-type.vala
+++ b/src/engine/imap/message/imap-status-data-type.vala
@@ -17,11 +17,11 @@ public enum Geary.Imap.StatusDataType {
UIDNEXT,
UIDVALIDITY,
UNSEEN;
-
+
public static StatusDataType[] all() {
return { MESSAGES, RECENT, UIDNEXT, UIDVALIDITY, UNSEEN };
}
-
+
public string to_string() {
switch (this) {
case MESSAGES:
@@ -43,29 +43,29 @@ public enum Geary.Imap.StatusDataType {
assert_not_reached();
}
}
-
+
public static StatusDataType from_parameter(StringParameter stringp) throws ImapError {
switch (stringp.as_lower()) {
case "messages":
return MESSAGES;
-
+
case "recent":
return RECENT;
-
+
case "uidnext":
return UIDNEXT;
-
+
case "uidvalidity":
return UIDVALIDITY;
-
+
case "unseen":
return UNSEEN;
-
+
default:
throw new ImapError.PARSE_ERROR("Unknown status data type \"%s\"", stringp.to_string());
}
}
-
+
public StringParameter to_parameter() {
return new AtomParameter(to_string());
}
diff --git a/src/engine/imap/message/imap-tag.vala b/src/engine/imap/message/imap-tag.vala
index 10c53f0f..11e256bb 100644
--- a/src/engine/imap/message/imap-tag.vala
+++ b/src/engine/imap/message/imap-tag.vala
@@ -19,93 +19,93 @@ public class Geary.Imap.Tag : AtomParameter, Gee.Hashable<Geary.Imap.Tag> {
public const string UNTAGGED_VALUE = "*";
public const string CONTINUATION_VALUE = "+";
public const string UNASSIGNED_VALUE = "----";
-
+
private static Tag? untagged = null;
private static Tag? unassigned = null;
private static Tag? continuation = null;
-
+
public Tag(string ascii) {
base (ascii);
}
-
+
public Tag.from_parameter(StringParameter strparam) {
base (strparam.ascii);
}
-
+
internal static void init() {
get_untagged();
get_continuation();
get_unassigned();
}
-
+
public static Tag get_untagged() {
if (untagged == null)
untagged = new Tag(UNTAGGED_VALUE);
-
+
return untagged;
}
-
+
public static Tag get_continuation() {
if (continuation == null)
continuation = new Tag(CONTINUATION_VALUE);
-
+
return continuation;
}
-
+
public static Tag get_unassigned() {
if (unassigned == null)
unassigned = new Tag(UNASSIGNED_VALUE);
-
+
return unassigned;
}
-
+
/**
* Returns true if the StringParameter resembles a tag token: an unquoted non-empty string
- * that either matches the untagged or continuation special tags or
+ * that either matches the untagged or continuation special tags or
*/
public static bool is_tag(StringParameter stringp) {
if (stringp is QuotedStringParameter)
return false;
-
+
if (stringp.is_empty())
return false;
-
+
if (stringp.equals_cs(UNTAGGED_VALUE) || stringp.equals_cs(CONTINUATION_VALUE))
return true;
-
+
int index = 0;
for (;;) {
char ch = stringp.ascii[index++];
if (ch == String.EOS)
break;
-
+
if (DataFormat.is_tag_special(ch))
return false;
}
-
+
return true;
}
-
+
public bool is_tagged() {
return !equals_cs(UNTAGGED_VALUE) && !equals_cs(CONTINUATION_VALUE) && !equals_cs(UNASSIGNED_VALUE);
}
-
+
public bool is_continuation() {
return equals_cs(CONTINUATION_VALUE);
}
-
+
public bool is_assigned() {
return !equals_cs(UNASSIGNED_VALUE) && !equals_cs(CONTINUATION_VALUE);
}
-
+
public uint hash() {
return Ascii.str_hash(ascii);
}
-
+
public bool equal_to(Geary.Imap.Tag tag) {
if (this == tag)
return true;
-
+
return equals_cs(tag.ascii);
}
}
diff --git a/src/engine/imap/message/imap-uid-validity.vala b/src/engine/imap/message/imap-uid-validity.vala
index 2f9ed825..6f948cb1 100644
--- a/src/engine/imap/message/imap-uid-validity.vala
+++ b/src/engine/imap/message/imap-uid-validity.vala
@@ -17,7 +17,7 @@ public class Geary.Imap.UIDValidity : Geary.MessageData.Int64MessageData, Geary.
* Minimum valid value for a {@link UIDValidity}.
*/
public const int64 MIN = 1;
-
+
/**
* Maximum valid value for a {@link UIDValidity}.
*
@@ -31,7 +31,7 @@ public class Geary.Imap.UIDValidity : Geary.MessageData.Int64MessageData, Geary.
* Invalid (placeholder) {@link UIDValidity} value.
*/
public const int64 INVALID = -1;
-
+
/**
* Creates a new {@link UIDValidity} without checking for valid values.
*
@@ -40,7 +40,7 @@ public class Geary.Imap.UIDValidity : Geary.MessageData.Int64MessageData, Geary.
public UIDValidity(int64 value) {
base (value);
}
-
+
/**
* Creates a new {@link UIDValidity}, throwing {@link ImapError.INVALID} if the supplied value
* is invalid.
@@ -50,17 +50,17 @@ public class Geary.Imap.UIDValidity : Geary.MessageData.Int64MessageData, Geary.
public UIDValidity.checked(int64 value) throws ImapError {
if (!is_value_valid(value))
throw new ImapError.INVALID("Invalid UIDVALIDITY %s", value.to_string());
-
+
base (value);
}
-
+
/**
* @see is_value_valid
*/
public bool is_valid() {
return is_value_valid(value);
}
-
+
/**
* Returns true if the supplied value is between {@link MIN} and {@link MAX}, inclusive.
*/
diff --git a/src/engine/imap/message/imap-uid.vala b/src/engine/imap/message/imap-uid.vala
index d2270de7..e7f9f4eb 100644
--- a/src/engine/imap/message/imap-uid.vala
+++ b/src/engine/imap/message/imap-uid.vala
@@ -18,17 +18,17 @@ public class Geary.Imap.UID : Geary.MessageData.Int64MessageData, Geary.Imap.Mes
* Minimum valid value for a {@link UID}.
*/
public const int64 MIN = 1;
-
+
/**
* Maximum valid value for a {@link UID}.
*/
public const int64 MAX = 0xFFFFFFFF;
-
+
/**
* Invalid (placeholder) {@link UID} value.
*/
public const int64 INVALID = -1;
-
+
/**
* Creates a new {@link UID} without checking for validity.
*
@@ -38,7 +38,7 @@ public class Geary.Imap.UID : Geary.MessageData.Int64MessageData, Geary.Imap.Mes
public UID(int64 value) {
base (value);
}
-
+
/**
* Creates a new {@link UID}, throwing an {@link ImapError.INVALID} if the supplied value is
* not a positive unsigned 32-bit integer.
@@ -48,24 +48,24 @@ public class Geary.Imap.UID : Geary.MessageData.Int64MessageData, Geary.Imap.Mes
public UID.checked(int64 value) throws ImapError {
if (!is_value_valid(value))
throw new ImapError.INVALID("Invalid UID %s", value.to_string());
-
+
base (value);
}
-
+
/**
* @see is_value_valid
*/
public bool is_valid() {
return is_value_valid(value);
}
-
+
/**
* Returns true if the supplied value is between {@link MIN} and {@link MAX}, inclusive.
*/
public static bool is_value_valid(int64 val) {
return Numeric.int64_in_range_inclusive(val, MIN, MAX);
}
-
+
/**
* Returns the UID logically next (or after) this one.
*
@@ -80,7 +80,7 @@ public class Geary.Imap.UID : Geary.MessageData.Int64MessageData, Geary.Imap.Mes
public UID next(bool clamped) {
return clamped ? new UID((value + 1).clamp(MIN, MAX)) : new UID(value + 1);
}
-
+
/**
* Returns the UID logically previous (or before) this one.
*
@@ -95,11 +95,11 @@ public class Geary.Imap.UID : Geary.MessageData.Int64MessageData, Geary.Imap.Mes
public UID previous(bool clamped) {
return clamped ? new UID((value - 1).clamp(MIN, MAX)) : new UID(value - 1);
}
-
+
public virtual int compare_to(Geary.Imap.UID other) {
return (int) (value - other.value).clamp(-1, 1);
}
-
+
public string serialize() {
return value.to_string();
}
diff --git a/src/engine/imap/parameter/imap-list-parameter.vala
b/src/engine/imap/parameter/imap-list-parameter.vala
index a8abca23..c8ab3bb5 100644
--- a/src/engine/imap/parameter/imap-list-parameter.vala
+++ b/src/engine/imap/parameter/imap-list-parameter.vala
@@ -16,7 +16,7 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
* in the StringParameter getters.
*/
public const int MAX_STRING_LITERAL_LENGTH = 4096;
-
+
/**
* Returns the number of {@link Parameter}s held in this {@link ListParameter}.
*/
@@ -90,7 +90,7 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
//
// Parameter retrieval
//
-
+
/**
* Returns the {@link Parameter} at the index in the list, null if index is out of range.
*
@@ -101,7 +101,7 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public new Parameter? get(int index) {
return ((index >= 0) && (index < list.size)) ? list.get(index) : null;
}
-
+
/**
* Returns the Parameter at the index. Throws an ImapError.TYPE_ERROR if the index is out of
* range.
@@ -113,14 +113,14 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public Parameter get_required(int index) throws ImapError {
if ((index < 0) || (index >= list.size))
throw new ImapError.TYPE_ERROR("No parameter at index %d", index);
-
+
Parameter? param = list.get(index);
if (param == null)
throw new ImapError.TYPE_ERROR("No parameter at index %d", index);
-
+
return param;
}
-
+
/**
* Returns {@link Parameter} at index if in range and of Type type, otherwise throws an
* {@link ImapError.TYPE_ERROR}.
@@ -130,16 +130,16 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public Parameter get_as(int index, Type type) throws ImapError {
if (!type.is_a(typeof(Parameter)))
throw new ImapError.TYPE_ERROR("Attempting to cast non-Parameter at index %d", index);
-
+
Parameter param = get_required(index);
if (!param.get_type().is_a(type)) {
throw new ImapError.TYPE_ERROR("Parameter %d is not of type %s (is %s)", index,
type.name(), param.get_type().name());
}
-
+
return param;
}
-
+
/**
* Like {@link get_as}, but returns null if the {@link Parameter} at index is a
* {@link NilParameter}.
@@ -149,25 +149,25 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public Parameter? get_as_nullable(int index, Type type) throws ImapError {
if (!type.is_a(typeof(Parameter)))
throw new ImapError.TYPE_ERROR("Attempting to cast non-Parameter at index %d", index);
-
+
Parameter param = get_required(index);
if (param is NilParameter)
return null;
-
+
// Because Deserializer doesn't produce NilParameters, check manually if this Parameter
// can legally be NIL according to IMAP grammer.
StringParameter? stringp = param as StringParameter;
if (stringp != null && NilParameter.is_nil(stringp))
return null;
-
+
if (!param.get_type().is_a(type)) {
throw new ImapError.TYPE_ERROR("Parameter %d is not of type %s (is %s)", index,
type.name(), param.get_type().name());
}
-
+
return param;
}
-
+
/**
* Like {@link get}, but returns null if {@link Parameter} at index is not of the specified type.
*
@@ -176,18 +176,18 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public Parameter? get_if(int index, Type type) {
if (!type.is_a(typeof(Parameter)))
return null;
-
+
Parameter? param = get(index);
if (param == null || !param.get_type().is_a(type))
return null;
-
+
return param;
}
-
+
//
// String retrieval
//
-
+
/**
* Returns a {@link StringParameter} only if the {@link Parameter} at index is a StringParameter.
*
@@ -196,7 +196,7 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public StringParameter? get_if_string(int index) {
return (StringParameter?) get_if(index, typeof(StringParameter));
}
-
+
/**
* Returns a {@link StringParameter} for the value at the index only if the {@link Parameter}
* is a StringParameter or a {@link LiteralParameter} with a length less than or equal to
@@ -211,19 +211,19 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
*/
public StringParameter get_as_string(int index) throws ImapError {
Parameter param = get_required(index);
-
+
StringParameter? stringp = param as StringParameter;
if (stringp != null)
return stringp;
-
+
LiteralParameter? literalp = param as LiteralParameter;
if (literalp != null && literalp.get_size() <= MAX_STRING_LITERAL_LENGTH)
return literalp.coerce_to_string_parameter();
-
+
throw new ImapError.TYPE_ERROR("Parameter %d not of type string or literal (is %s)", index,
param.get_type().name());
}
-
+
/**
* Returns a {@link StringParameter} for the value at the index only if the {@link Parameter}
* is a StringParameter or a {@link LiteralParameter} with a length less than or equal to
@@ -240,33 +240,33 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
Parameter? param = get_as_nullable(index, typeof(Parameter));
if (param == null)
return null;
-
+
StringParameter? stringp = param as StringParameter;
if (stringp != null)
return stringp;
-
+
LiteralParameter? literalp = param as LiteralParameter;
if (literalp != null && literalp.get_size() <= MAX_STRING_LITERAL_LENGTH)
return literalp.coerce_to_string_parameter();
-
+
throw new ImapError.TYPE_ERROR("Parameter %d not of type string or literal (is %s)", index,
param.get_type().name());
}
-
+
/**
* Much like get_as_nullable_string() but returns an empty StringParameter (rather than null)
* if the parameter at index is a NilParameter.
*/
public StringParameter get_as_empty_string(int index) throws ImapError {
StringParameter? stringp = get_as_nullable_string(index);
-
+
return stringp ?? StringParameter.get_best_for("");
}
-
+
//
// Number retrieval
//
-
+
/**
* Returns a {@link NumberParameter} at index, null if not of that type.
*
@@ -275,7 +275,7 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public NumberParameter? get_if_number(int index) {
return (NumberParameter?) get_if(index, typeof(NumberParameter));
}
-
+
/**
* Returns a {@link NumberParameter} at index.
*
@@ -285,26 +285,26 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
*/
public NumberParameter get_as_number(int index) throws ImapError {
Parameter param = get_required(index);
-
+
NumberParameter? numberp = param as NumberParameter;
if (numberp != null)
return numberp;
-
+
StringParameter? stringp = param as StringParameter;
if (stringp != null) {
numberp = stringp.coerce_to_number_parameter();
if (numberp != null)
return numberp;
}
-
+
throw new ImapError.TYPE_ERROR("Parameter %d not of type number or string (is %s)", index,
param.get_type().name());
}
-
+
//
// List retrieval
//
-
+
/**
* Returns a {@link ListParameter} at index.
*
@@ -313,7 +313,7 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public ListParameter get_as_list(int index) throws ImapError {
return (ListParameter) get_as(index, typeof(ListParameter));
}
-
+
/**
* Returns a {@link ListParameter} at index, null if NIL.
*
@@ -340,11 +340,11 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public ListParameter? get_if_list(int index) {
return (ListParameter?) get_if(index, typeof(ListParameter));
}
-
+
//
// Literal retrieval
//
-
+
/**
* Returns a {@link LiteralParameter} at index.
*
@@ -353,7 +353,7 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public LiteralParameter get_as_literal(int index) throws ImapError {
return (LiteralParameter) get_as(index, typeof(LiteralParameter));
}
-
+
/**
* Returns a {@link LiteralParameter} at index, null if NIL.
*
@@ -362,7 +362,7 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public LiteralParameter? get_as_nullable_literal(int index) throws ImapError {
return (LiteralParameter?) get_as_nullable(index, typeof(LiteralParameter));
}
-
+
/**
* Returns a {@link LiteralParameter} at index, null if not a list.
*
@@ -371,16 +371,16 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public LiteralParameter? get_if_literal(int index) {
return (LiteralParameter?) get_if(index, typeof(LiteralParameter));
}
-
+
/**
* Returns [@link LiteralParameter} at index, an empty list if NIL.
*/
public LiteralParameter get_as_empty_literal(int index) throws ImapError {
LiteralParameter? param = get_as_nullable_literal(index);
-
+
return param ?? new LiteralParameter(Geary.Memory.EmptyBuffer.instance);
}
-
+
/**
* Returns a {@link Memory.Buffer} for the {@link Parameter} at position index.
*
@@ -391,14 +391,14 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
LiteralParameter? literalp = get_if_literal(index);
if (literalp != null)
return literalp.get_buffer();
-
+
StringParameter? stringp = get_if_string(index);
if (stringp != null)
return stringp.as_buffer();
-
+
return null;
}
-
+
/**
* Returns a {@link Memory.Buffer} for the {@link Parameter} at position index.
*
@@ -408,14 +408,14 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
public Memory.Buffer get_as_empty_buffer(int index) throws ImapError {
return get_as_nullable_buffer(index) ?? Memory.EmptyBuffer.instance;
}
-
+
/**
* Returns a read-only List of {@link Parameter}s.
*/
public Gee.List<Parameter> get_all() {
return list.read_only_view;
}
-
+
/**
* Returns the replaced Paramater. Throws ImapError.TYPE_ERROR if no Parameter exists at the
* index.
@@ -437,27 +437,27 @@ public class Geary.Imap.ListParameter : Geary.Imap.Parameter {
*/
public void adopt_children(ListParameter src) {
clear();
-
+
Gee.List<Parameter> src_children = new Gee.ArrayList<Parameter>();
src_children.add_all(src.list);
src.clear();
-
+
add_all(src_children);
}
-
+
protected string stringize_list() {
StringBuilder builder = new StringBuilder();
-
+
int length = list.size;
for (int ctr = 0; ctr < length; ctr++) {
builder.append(list[ctr].to_string());
if (ctr < (length - 1))
builder.append_c(' ');
}
-
+
return builder.str;
}
-
+
/**
* {@inheritDoc}
*/
diff --git a/src/engine/imap/parameter/imap-literal-parameter.vala
b/src/engine/imap/parameter/imap-literal-parameter.vala
index 4d394697..a3b1055f 100644
--- a/src/engine/imap/parameter/imap-literal-parameter.vala
+++ b/src/engine/imap/parameter/imap-literal-parameter.vala
@@ -16,25 +16,25 @@
public class Geary.Imap.LiteralParameter : Geary.Imap.Parameter {
private Memory.Buffer buffer;
-
+
public LiteralParameter(Memory.Buffer buffer) {
this.buffer = buffer;
}
-
+
/**
* Returns the number of bytes in the literal parameter's buffer.
*/
public size_t get_size() {
return buffer.size;
}
-
+
/**
* Returns the literal paremeter's buffer.
*/
public Memory.Buffer get_buffer() {
return buffer;
}
-
+
/**
* Returns the {@link LiteralParameter} as though it had been a {@link StringParameter} on the
* wire.
@@ -47,7 +47,7 @@ public class Geary.Imap.LiteralParameter : Geary.Imap.Parameter {
public StringParameter coerce_to_string_parameter() {
return new UnquotedStringParameter(buffer.get_valid_utf8());
}
-
+
/**
* {@inheritDoc}
*/
diff --git a/src/engine/imap/parameter/imap-nil-parameter.vala
b/src/engine/imap/parameter/imap-nil-parameter.vala
index c81efd94..82d34a5a 100644
--- a/src/engine/imap/parameter/imap-nil-parameter.vala
+++ b/src/engine/imap/parameter/imap-nil-parameter.vala
@@ -20,20 +20,20 @@
public class Geary.Imap.NilParameter : Geary.Imap.Parameter {
public const string VALUE = "NIL";
-
+
private static NilParameter? _instance = null;
public static NilParameter instance {
get {
if (_instance == null)
_instance = new NilParameter();
-
+
return _instance;
}
}
-
+
private NilParameter() {
}
-
+
/**
* See note at {@link NilParameter} for comparison rules of "NIL".
*
diff --git a/src/engine/imap/parameter/imap-number-parameter.vala
b/src/engine/imap/parameter/imap-number-parameter.vala
index 37391fa7..bde47a71 100644
--- a/src/engine/imap/parameter/imap-number-parameter.vala
+++ b/src/engine/imap/parameter/imap-number-parameter.vala
@@ -15,27 +15,27 @@ public class Geary.Imap.NumberParameter : UnquotedStringParameter {
public NumberParameter(int num) {
base (num.to_string());
}
-
+
public NumberParameter.uint(uint num) {
base (num.to_string());
}
-
+
public NumberParameter.int32(int32 num) {
base (num.to_string());
}
-
+
public NumberParameter.uint32(uint32 num) {
base (num.to_string());
}
-
+
public NumberParameter.int64(int64 num) {
base (num.to_string());
}
-
+
public NumberParameter.uint64(uint64 num) {
base (num.to_string());
}
-
+
/**
* Creates a {@link NumberParameter} for a string representation of a number.
*
@@ -45,7 +45,7 @@ public class Geary.Imap.NumberParameter : UnquotedStringParameter {
public NumberParameter.from_ascii(string ascii) {
base (ascii);
}
-
+
/**
* Returns true if the string is composed of numeric 7-bit characters.
*
@@ -61,40 +61,40 @@ public class Geary.Imap.NumberParameter : UnquotedStringParameter {
*/
public static bool is_ascii_numeric(string ascii, out bool is_negative) {
is_negative = false;
-
+
string str = ascii.strip();
-
+
if (String.is_empty(str))
return false;
-
+
bool has_nonzero = false;
int index = 0;
for (;;) {
char ch = str[index++];
if (ch == String.EOS)
break;
-
+
if (index == 1 && ch == '-') {
is_negative = true;
-
+
continue;
}
-
+
if (!ch.isdigit())
return false;
-
+
if (ch != '0')
has_nonzero = true;
}
-
+
// watch for negative but no numeric portion
if (is_negative && str.length == 1)
return false;
-
+
// no such thing as negative zero
if (is_negative && !has_nonzero)
is_negative = false;
-
+
return true;
}
}
diff --git a/src/engine/imap/parameter/imap-root-parameters.vala
b/src/engine/imap/parameter/imap-root-parameters.vala
index c731ec38..397bf17d 100644
--- a/src/engine/imap/parameter/imap-root-parameters.vala
+++ b/src/engine/imap/parameter/imap-root-parameters.vala
@@ -17,7 +17,7 @@
public class Geary.Imap.RootParameters : Geary.Imap.ListParameter {
public RootParameters() {
}
-
+
/**
* Moves all contained {@link Parameter} objects inside the supplied RootParameters into a
* new RootParameters.
@@ -27,7 +27,7 @@ public class Geary.Imap.RootParameters : Geary.Imap.ListParameter {
public RootParameters.migrate(RootParameters root) {
adopt_children(root);
}
-
+
/**
* Returns null if the first parameter is not a StringParameter that resembles a Tag.
*/
@@ -35,13 +35,13 @@ public class Geary.Imap.RootParameters : Geary.Imap.ListParameter {
StringParameter? strparam = get_if_string(0);
if (strparam == null)
return null;
-
+
if (!Tag.is_tag(strparam))
return null;
-
+
return new Tag.from_parameter(strparam);
}
-
+
/**
* Returns true if the first parameter is a StringParameter that resembles a Tag.
*/
@@ -49,10 +49,10 @@ public class Geary.Imap.RootParameters : Geary.Imap.ListParameter {
StringParameter? strparam = get_if_string(0);
if (strparam == null)
return false;
-
+
return (strparam != null) ? Tag.is_tag(strparam) : false;
}
-
+
/**
* {@inheritDoc}
*/
diff --git a/src/engine/imap/parameter/imap-string-parameter.vala
b/src/engine/imap/parameter/imap-string-parameter.vala
index 529edaae..aaa54bbc 100644
--- a/src/engine/imap/parameter/imap-string-parameter.vala
+++ b/src/engine/imap/parameter/imap-string-parameter.vala
@@ -24,7 +24,7 @@ public abstract class Geary.Imap.StringParameter : Geary.Imap.Parameter {
* The unquoted, decoded string as 7-bit ASCII.
*/
public string ascii { get; private set; }
-
+
/**
* Returns {@link ascii} or null if value is empty (zero-length).
*/
@@ -33,11 +33,11 @@ public abstract class Geary.Imap.StringParameter : Geary.Imap.Parameter {
return String.is_empty(ascii) ? null : ascii;
}
}
-
+
protected StringParameter(string ascii) {
this.ascii = ascii;
}
-
+
/**
* Returns a {@link StringParameter} appropriate for the contents of value.
*
@@ -54,22 +54,22 @@ public abstract class Geary.Imap.StringParameter : Geary.Imap.Parameter {
public static StringParameter get_best_for(string value) throws ImapError {
if (NumberParameter.is_ascii_numeric(value, null))
return new NumberParameter.from_ascii(value);
-
+
switch (DataFormat.is_quoting_required(value)) {
case DataFormat.Quoting.REQUIRED:
return new QuotedStringParameter(value);
-
+
case DataFormat.Quoting.OPTIONAL:
return new UnquotedStringParameter(value);
-
+
case DataFormat.Quoting.UNALLOWED:
throw new ImapError.NOT_SUPPORTED("String must be a literal parameter");
-
+
default:
assert_not_reached();
}
}
-
+
/**
* Like {@link get_best_for} but the library will panic if the value cannot be turned into
* a {@link StringParameter}.
@@ -83,7 +83,7 @@ public abstract class Geary.Imap.StringParameter : Geary.Imap.Parameter {
error("Unable to create StringParameter for \"%s\": %s", value, ierr.message);
}
}
-
+
/**
* Like {@link get_best_for} but returns null if the value cannot be stored as a
* {@link StringParameter}.
@@ -97,7 +97,7 @@ public abstract class Geary.Imap.StringParameter : Geary.Imap.Parameter {
return null;
}
}
-
+
/**
* Can be used by subclasses to properly serialize the string value according to quoting rules.
*
@@ -117,54 +117,54 @@ public abstract class Geary.Imap.StringParameter : Geary.Imap.Parameter {
case DataFormat.Quoting.UNALLOWED:
error("Unable to serialize literal data");
-
+
default:
assert_not_reached();
}
}
-
+
/**
* Returns the string as a {@link Memory.Buffer}.
*/
public Memory.Buffer as_buffer() {
return new Memory.StringBuffer(ascii);
}
-
+
/**
* Returns true if the string is empty (zero-length).
*/
public bool is_empty() {
return String.is_empty(ascii);
}
-
+
/**
* Case-sensitive comparison.
*/
public bool equals_cs(string value) {
return Ascii.str_equal(ascii, value);
}
-
+
/**
* Case-insensitive comparison.
*/
public bool equals_ci(string value) {
return Ascii.stri_equal(ascii, value);
}
-
+
/**
* Returns the string lowercased.
*/
public string as_lower() {
return Ascii.strdown(ascii);
}
-
+
/**
* Returns the string uppercased.
*/
public string as_upper() {
return Ascii.strup(ascii);
}
-
+
/**
* Converts the {@link ascii} to a signed 32-bit integer, clamped between clamp_min and
* clamp_max.
@@ -175,10 +175,10 @@ public abstract class Geary.Imap.StringParameter : Geary.Imap.Parameter {
public int32 as_int32(int32 clamp_min = int32.MIN, int32 clamp_max = int32.MAX) throws ImapError {
if (!NumberParameter.is_ascii_numeric(ascii, null))
throw new ImapError.INVALID("Cannot convert \"%s\" to int32: not numeric", ascii);
-
+
return (int32) int64.parse(ascii).clamp(clamp_min, clamp_max);
}
-
+
/**
* Converts the {@link ascii} to a signed 64-bit integer, clamped between clamp_min and
* clamp_max.
@@ -189,10 +189,10 @@ public abstract class Geary.Imap.StringParameter : Geary.Imap.Parameter {
public int64 as_int64(int64 clamp_min = int64.MIN, int64 clamp_max = int64.MAX) throws ImapError {
if (!NumberParameter.is_ascii_numeric(ascii, null))
throw new ImapError.INVALID("Cannot convert \"%s\" to int64: not numeric", ascii);
-
+
return int64.parse(ascii).clamp(clamp_min, clamp_max);
}
-
+
/**
* Attempts to coerce a {@link StringParameter} into a {@link NumberParameter}.
*
@@ -204,10 +204,10 @@ public abstract class Geary.Imap.StringParameter : Geary.Imap.Parameter {
NumberParameter? numberp = this as NumberParameter;
if (numberp != null)
return numberp;
-
+
if (NumberParameter.is_ascii_numeric(ascii, null))
return new NumberParameter.from_ascii(ascii);
-
+
return null;
}
}
diff --git a/src/engine/imap/response/imap-capabilities.vala b/src/engine/imap/response/imap-capabilities.vala
index f413f71b..22179c9a 100644
--- a/src/engine/imap/response/imap-capabilities.vala
+++ b/src/engine/imap/response/imap-capabilities.vala
@@ -44,7 +44,7 @@ public class Geary.Imap.Capabilities : Geary.GenericCapabilities {
public override string to_string() {
return "#%d: %s".printf(revision, base.to_string());
}
-
+
/**
* Indicates the {@link ClientSession} reported support for IDLE.
*
@@ -53,7 +53,7 @@ public class Geary.Imap.Capabilities : Geary.GenericCapabilities {
public bool supports_idle() {
return has_capability(IDLE);
}
-
+
/**
* Indicates the {@link ClientSession} reported support for UIDPLUS.
*
@@ -62,7 +62,7 @@ public class Geary.Imap.Capabilities : Geary.GenericCapabilities {
public bool supports_uidplus() {
return has_capability(UIDPLUS);
}
-
+
/**
* Indicates the {@link ClientSession} reported support for SPECIAL-USE.
*
diff --git a/src/engine/imap/response/imap-continuation-response.vala
b/src/engine/imap/response/imap-continuation-response.vala
index 82fe22e7..8fc0a7cc 100644
--- a/src/engine/imap/response/imap-continuation-response.vala
+++ b/src/engine/imap/response/imap-continuation-response.vala
@@ -18,7 +18,7 @@ public class Geary.Imap.ContinuationResponse : ServerResponse {
private ContinuationResponse() {
base (Tag.get_continuation());
}
-
+
/**
* Converts the {@link RootParameters} into a {@link ContinuationResponse}.
*
@@ -27,17 +27,17 @@ public class Geary.Imap.ContinuationResponse : ServerResponse {
*/
public ContinuationResponse.migrate(RootParameters root) throws ImapError {
base.migrate(root);
-
+
if (!tag.is_continuation())
throw new ImapError.INVALID("Tag %s is not a continuation", tag.to_string());
}
-
+
/**
* Returns true if the {@link RootParameters}'s {@link Tag} is a continuation character ("+").
*/
public static bool is_continuation_response(RootParameters root) {
Tag? tag = root.get_tag();
-
+
return tag != null ? tag.is_continuation() : false;
}
}
diff --git a/src/engine/imap/response/imap-fetch-data-decoder.vala
b/src/engine/imap/response/imap-fetch-data-decoder.vala
index 5bd7c95d..2b3eae91 100644
--- a/src/engine/imap/response/imap-fetch-data-decoder.vala
+++ b/src/engine/imap/response/imap-fetch-data-decoder.vala
@@ -18,11 +18,11 @@
public abstract class Geary.Imap.FetchDataDecoder : BaseObject {
public FetchDataSpecifier data_item { get; private set; }
-
+
public FetchDataDecoder(FetchDataSpecifier data_item) {
this.data_item = data_item;
}
-
+
/*
* The default implementation determines the type of the parameter and calls the appropriate
* virtual function; most implementations of a FetchResponseDecoder shouldn't need to override
@@ -32,11 +32,11 @@ public abstract class Geary.Imap.FetchDataDecoder : BaseObject {
StringParameter? stringp = param as StringParameter;
if (stringp != null)
return decode_string(stringp);
-
+
ListParameter? listp = param as ListParameter;
if (listp != null)
return decode_list(listp);
-
+
LiteralParameter? literalp = param as LiteralParameter;
if (literalp != null) {
// because this method is called without the help of get_as_string() (which converts
@@ -50,30 +50,30 @@ public abstract class Geary.Imap.FetchDataDecoder : BaseObject {
if (!(imap_err is ImapError.TYPE_ERROR))
throw imap_err;
}
-
+
return decode_literal(literalp);
}
-
+
NilParameter? nilp = param as NilParameter;
if (nilp != null)
return decode_nil(nilp);
-
+
// bad news; this means this function isn't handling a Parameter type properly
assert_not_reached();
}
-
+
protected virtual MessageData decode_string(StringParameter param) throws ImapError {
throw new ImapError.TYPE_ERROR("%s does not accept a string parameter", data_item.to_string());
}
-
+
protected virtual MessageData decode_list(ListParameter list) throws ImapError {
throw new ImapError.TYPE_ERROR("%s does not accept a list parameter", data_item.to_string());
}
-
+
protected virtual MessageData decode_literal(LiteralParameter literal) throws ImapError {
throw new ImapError.TYPE_ERROR("%s does not accept a literal parameter", data_item.to_string());
}
-
+
protected virtual MessageData decode_nil(NilParameter nil) throws ImapError {
throw new ImapError.TYPE_ERROR("%s does not accept a nil parameter", data_item.to_string());
}
@@ -83,7 +83,7 @@ public class Geary.Imap.UIDDecoder : Geary.Imap.FetchDataDecoder {
public UIDDecoder() {
base (FetchDataSpecifier.UID);
}
-
+
protected override MessageData decode_string(StringParameter stringp) throws ImapError {
return new UID.checked(stringp.as_int64());
}
@@ -93,12 +93,12 @@ public class Geary.Imap.MessageFlagsDecoder : Geary.Imap.FetchDataDecoder {
public MessageFlagsDecoder() {
base (FetchDataSpecifier.FLAGS);
}
-
+
protected override MessageData decode_list(ListParameter listp) throws ImapError {
Gee.List<MessageFlag> flags = new Gee.ArrayList<MessageFlag>();
for (int ctr = 0; ctr < listp.size; ctr++)
flags.add(new MessageFlag(listp.get_as_string(ctr).ascii));
-
+
return new MessageFlags(flags);
}
}
@@ -107,7 +107,7 @@ public class Geary.Imap.InternalDateDecoder : Geary.Imap.FetchDataDecoder {
public InternalDateDecoder() {
base (FetchDataSpecifier.INTERNALDATE);
}
-
+
protected override MessageData decode_string(StringParameter stringp) throws ImapError {
return InternalDate.decode(stringp.ascii);
}
@@ -117,7 +117,7 @@ public class Geary.Imap.RFC822SizeDecoder : Geary.Imap.FetchDataDecoder {
public RFC822SizeDecoder() {
base (FetchDataSpecifier.RFC822_SIZE);
}
-
+
protected override MessageData decode_string(StringParameter stringp) throws ImapError {
return new RFC822Size(stringp.as_int64(0, int64.MAX));
}
@@ -127,7 +127,7 @@ public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
public EnvelopeDecoder() {
base (FetchDataSpecifier.ENVELOPE);
}
-
+
protected override MessageData decode_list(ListParameter listp) throws ImapError {
StringParameter? sent = listp.get_as_nullable_string(0);
StringParameter subject = listp.get_as_empty_string(1);
@@ -139,7 +139,7 @@ public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
ListParameter? bcc = listp.get_as_nullable_list(7);
StringParameter? in_reply_to = listp.get_as_nullable_string(8);
StringParameter? message_id = listp.get_as_nullable_string(9);
-
+
// Although Message-ID is required to be returned by IMAP, it may be blank if the email
// does not supply it (optional according to RFC822); deal with this cognitive dissonance
if (message_id != null && message_id.is_empty())
@@ -161,13 +161,13 @@ public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
sent_date,
new Geary.RFC822.Subject.decode(subject.ascii),
parse_addresses(from), parse_addresses(sender), parse_addresses(reply_to),
- (to != null) ? parse_addresses(to) : null,
+ (to != null) ? parse_addresses(to) : null,
(cc != null) ? parse_addresses(cc) : null,
(bcc != null) ? parse_addresses(bcc) : null,
(in_reply_to != null) ? new Geary.RFC822.MessageIDList.from_rfc822_string(in_reply_to.ascii) :
null,
(message_id != null) ? new Geary.RFC822.MessageID(message_id.ascii) : null);
}
-
+
// TODO: This doesn't handle group lists (see Johnson, p.268) -- this will throw an
// ImapError.TYPE_ERROR if this occurs.
private Geary.RFC822.MailboxAddresses? parse_addresses(ListParameter listp) throws ImapError {
@@ -178,7 +178,7 @@ public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
StringParameter? source_route = fields.get_as_nullable_string(1);
StringParameter mailbox = fields.get_as_empty_string(2);
StringParameter domain = fields.get_as_empty_string(3);
-
+
Geary.RFC822.MailboxAddress addr = new Geary.RFC822.MailboxAddress.imap(
(name != null) ? name.nullable_ascii : null,
(source_route != null) ? source_route.nullable_ascii : null,
@@ -186,7 +186,7 @@ public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
domain.ascii);
list.add(addr);
}
-
+
return new Geary.RFC822.MailboxAddresses(list);
}
}
@@ -195,7 +195,7 @@ public class Geary.Imap.RFC822HeaderDecoder : Geary.Imap.FetchDataDecoder {
public RFC822HeaderDecoder() {
base (FetchDataSpecifier.RFC822_HEADER);
}
-
+
protected override MessageData decode_literal(LiteralParameter literalp) throws ImapError {
return new Geary.Imap.RFC822Header(literalp.get_buffer());
}
@@ -205,11 +205,11 @@ public class Geary.Imap.RFC822TextDecoder : Geary.Imap.FetchDataDecoder {
public RFC822TextDecoder() {
base (FetchDataSpecifier.RFC822_TEXT);
}
-
+
protected override MessageData decode_literal(LiteralParameter literalp) throws ImapError {
return new Geary.Imap.RFC822Text(literalp.get_buffer());
}
-
+
protected override MessageData decode_nil(NilParameter nilp) throws ImapError {
return new Geary.Imap.RFC822Text(Memory.EmptyBuffer.instance);
}
@@ -219,7 +219,7 @@ public class Geary.Imap.RFC822FullDecoder : Geary.Imap.FetchDataDecoder {
public RFC822FullDecoder() {
base (FetchDataSpecifier.RFC822);
}
-
+
protected override MessageData decode_literal(LiteralParameter literalp) throws ImapError {
return new Geary.Imap.RFC822Full(literalp.get_buffer());
}
diff --git a/src/engine/imap/response/imap-fetched-data.vala b/src/engine/imap/response/imap-fetched-data.vala
index 114aa892..7bc47d57 100644
--- a/src/engine/imap/response/imap-fetched-data.vala
+++ b/src/engine/imap/response/imap-fetched-data.vala
@@ -1,7 +1,7 @@
/* Copyright 2016 Software Freedom Conservancy Inc.
*
* This software is licensed under the GNU Lesser General Public License
- * (version 2.1 or later). See the COPYING file in this distribution.
+ * (version 2.1 or later). See the COPYING file in this distribution.
*/
/**
@@ -17,7 +17,7 @@ public class Geary.Imap.FetchedData : Object {
* The positional address of the email in the mailbox.
*/
public SequenceNumber seq_num { get; private set; }
-
+
/**
* A Map of {@link FetchDataSpecifier}s to their {@link Imap.MessageData} for this email.
*
@@ -25,17 +25,17 @@ public class Geary.Imap.FetchedData : Object {
*/
public Gee.Map<FetchDataSpecifier, MessageData> data_map { get; private set;
default = new Gee.HashMap<FetchDataSpecifier, MessageData>(); }
-
+
/**
* List of {@link FetchBodyDataSpecifier} responses.
*/
public Gee.Map<FetchBodyDataSpecifier, Memory.Buffer> body_data_map { get; private set;
default = new Gee.HashMap<FetchBodyDataSpecifier, Memory.Buffer>(); }
-
+
public FetchedData(SequenceNumber seq_num) {
this.seq_num = seq_num;
}
-
+
/**
* Decodes {@link ServerData} into a FetchedData representation.
*
@@ -48,25 +48,25 @@ public class Geary.Imap.FetchedData : Object {
public static FetchedData decode(ServerData server_data) throws ImapError {
if (!server_data.get_as_string(2).equals_ci(FetchCommand.NAME))
throw new ImapError.PARSE_ERROR("Not FETCH data: %s", server_data.to_string());
-
+
FetchedData fetched_data = new FetchedData(
new SequenceNumber.checked(server_data.get_as_string(1).as_int64()));
-
+
// walk the list for each returned fetch data item, which is paired by its data item name
// and the structured data itself
ListParameter list = server_data.get_as_list(3);
for (int ctr = 0; ctr < list.size; ctr += 2) {
StringParameter data_item_param = list.get_as_string(ctr);
-
+
// watch for truncated lists, which indicate an empty return value
bool has_value = (ctr < (list.size - 1));
-
+
if (FetchBodyDataSpecifier.is_fetch_body_data_specifier(data_item_param)) {
// "fake" the identifier by merely dropping in the StringParameter wholesale ...
// this works because FetchBodyDataIdentifier does case-insensitive comparisons ...
// other munging may be required if this isn't sufficient
FetchBodyDataSpecifier specifier =
FetchBodyDataSpecifier.deserialize_response(data_item_param);
-
+
if (has_value)
fetched_data.body_data_map.set(specifier, list.get_as_empty_buffer(ctr + 1));
else
@@ -77,10 +77,10 @@ public class Geary.Imap.FetchedData : Object {
if (decoder == null) {
debug("Unable to decode fetch response for \"%s\": No decoder available",
data_item.to_string());
-
+
continue;
}
-
+
// watch for empty return values
if (has_value)
fetched_data.data_map.set(data_item, decoder.decode(list.get_required(ctr + 1)));
@@ -88,10 +88,10 @@ public class Geary.Imap.FetchedData : Object {
fetched_data.data_map.set(data_item, decoder.decode(NilParameter.instance));
}
}
-
+
return fetched_data;
}
-
+
/**
* Returns the merge of this {@link FetchedData} and the supplied one.
*
@@ -105,7 +105,7 @@ public class Geary.Imap.FetchedData : Object {
public FetchedData? combine(FetchedData other) {
if (!seq_num.equal_to(other.seq_num))
return null;
-
+
FetchedData combined = new FetchedData(seq_num);
Collection.map_set_all<FetchDataSpecifier, MessageData>(combined.data_map, data_map);
Collection.map_set_all<FetchDataSpecifier, MessageData>(combined.data_map, other.data_map);
@@ -113,21 +113,21 @@ public class Geary.Imap.FetchedData : Object {
body_data_map);
Collection.map_set_all<FetchBodyDataSpecifier, Memory.Buffer>(combined.body_data_map,
other.body_data_map);
-
+
return combined;
}
-
+
public string to_string() {
StringBuilder builder = new StringBuilder();
-
+
builder.append_printf("[%s] ", seq_num.to_string());
-
+
foreach (FetchDataSpecifier data_type in data_map.keys)
builder.append_printf("%s=%s ", data_type.to_string(), data_map.get(data_type).to_string());
-
+
foreach (FetchBodyDataSpecifier specifier in body_data_map.keys)
builder.append_printf("%s=%lu ", specifier.to_string(), body_data_map.get(specifier).size);
-
+
return builder.str;
}
}
diff --git a/src/engine/imap/response/imap-mailbox-attribute.vala
b/src/engine/imap/response/imap-mailbox-attribute.vala
index 845fbd48..91df675c 100644
--- a/src/engine/imap/response/imap-mailbox-attribute.vala
+++ b/src/engine/imap/response/imap-mailbox-attribute.vala
@@ -18,92 +18,92 @@ public class Geary.Imap.MailboxAttribute : Geary.Imap.Flag {
public static MailboxAttribute NO_INFERIORS { get {
if (_no_inferiors == null)
_no_inferiors = new MailboxAttribute("\\noinferiors");
-
+
return _no_inferiors;
} }
-
+
private static MailboxAttribute? _nonexistent = null;
public static MailboxAttribute NONEXISTENT { get {
return (_nonexistent != null) ? _nonexistent : _nonexistent = new MailboxAttribute("\\NonExistent");
} }
-
+
private static MailboxAttribute? _no_select = null;
public static MailboxAttribute NO_SELECT { get {
if (_no_select == null)
_no_select = new MailboxAttribute("\\noselect");
-
+
return _no_select;
} }
-
+
private static MailboxAttribute? _marked = null;
public static MailboxAttribute MARKED { get {
if (_marked == null)
_marked = new MailboxAttribute("\\marked");
-
+
return _marked;
} }
-
+
private static MailboxAttribute? _unmarked = null;
public static MailboxAttribute UNMARKED { get {
if (_unmarked == null)
_unmarked = new MailboxAttribute("\\unmarked");
-
+
return _unmarked;
} }
-
+
private static MailboxAttribute? _has_no_children = null;
public static MailboxAttribute HAS_NO_CHILDREN { get {
if (_has_no_children == null)
_has_no_children = new MailboxAttribute("\\hasnochildren");
-
+
return _has_no_children;
} }
-
+
private static MailboxAttribute? _has_children = null;
public static MailboxAttribute HAS_CHILDREN { get {
if (_has_children == null)
_has_children = new MailboxAttribute("\\haschildren");
-
+
return _has_children;
} }
-
+
private static MailboxAttribute? _allows_new = null;
public static MailboxAttribute ALLOWS_NEW { get {
if (_allows_new == null)
_allows_new = new MailboxAttribute("\\*");
-
+
return _allows_new;
} }
-
+
private static MailboxAttribute? _xlist_inbox = null;
public static MailboxAttribute SPECIAL_FOLDER_INBOX { get {
if (_xlist_inbox == null)
_xlist_inbox = new MailboxAttribute("\\Inbox");
-
+
return _xlist_inbox;
} }
-
+
private static MailboxAttribute? _xlist_all_mail = null;
public static MailboxAttribute SPECIAL_FOLDER_ALL_MAIL { get {
if (_xlist_all_mail == null)
_xlist_all_mail = new MailboxAttribute("\\AllMail");
-
+
return _xlist_all_mail;
} }
-
+
private static MailboxAttribute? _xlist_trash = null;
public static MailboxAttribute SPECIAL_FOLDER_TRASH { get {
if (_xlist_trash == null)
_xlist_trash = new MailboxAttribute("\\Trash");
-
+
return _xlist_trash;
} }
-
+
private static MailboxAttribute? _xlist_drafts = null;
public static MailboxAttribute SPECIAL_FOLDER_DRAFTS { get {
if (_xlist_drafts == null)
_xlist_drafts = new MailboxAttribute("\\Drafts");
-
+
return _xlist_drafts;
} }
@@ -111,7 +111,7 @@ public class Geary.Imap.MailboxAttribute : Geary.Imap.Flag {
public static MailboxAttribute SPECIAL_FOLDER_SENT { get {
if (_xlist_sent == null)
_xlist_sent = new MailboxAttribute("\\Sent");
-
+
return _xlist_sent;
} }
@@ -119,54 +119,54 @@ public class Geary.Imap.MailboxAttribute : Geary.Imap.Flag {
public static MailboxAttribute SPECIAL_FOLDER_SPAM { get {
if (_xlist_spam == null)
_xlist_spam = new MailboxAttribute("\\Spam");
-
+
return _xlist_spam;
} }
-
+
private static MailboxAttribute? _xlist_starred = null;
public static MailboxAttribute SPECIAL_FOLDER_STARRED { get {
if (_xlist_starred == null)
_xlist_starred = new MailboxAttribute("\\Starred");
-
+
return _xlist_starred;
} }
-
+
private static MailboxAttribute? _xlist_important = null;
public static MailboxAttribute SPECIAL_FOLDER_IMPORTANT { get {
if (_xlist_important == null)
_xlist_important = new MailboxAttribute("\\Important");
-
+
return _xlist_important;
} }
-
+
private static MailboxAttribute? _special_use_all = null;
public static MailboxAttribute SPECIAL_FOLDER_ALL { get {
return (_special_use_all != null) ? _special_use_all
: _special_use_all = new MailboxAttribute("\\All");
} }
-
+
private static MailboxAttribute? _special_use_archive = null;
public static MailboxAttribute SPECIAL_FOLDER_ARCHIVE { get {
return (_special_use_archive != null) ? _special_use_archive
: _special_use_archive = new MailboxAttribute("\\Archive");
} }
-
+
private static MailboxAttribute? _special_use_flagged = null;
public static MailboxAttribute SPECIAL_FOLDER_FLAGGED { get {
return (_special_use_flagged != null) ? _special_use_flagged
: _special_use_flagged = new MailboxAttribute("\\Flagged");
} }
-
+
private static MailboxAttribute? _special_use_junk = null;
public static MailboxAttribute SPECIAL_FOLDER_JUNK { get {
return (_special_use_junk != null) ? _special_use_junk
: _special_use_junk = new MailboxAttribute("\\Junk");
} }
-
+
public MailboxAttribute(string value) {
base (value);
}
-
+
// Call these at init time to prevent thread issues
internal static void init() {
MailboxAttribute to_init = NO_INFERIORS;
diff --git a/src/engine/imap/response/imap-mailbox-attributes.vala
b/src/engine/imap/response/imap-mailbox-attributes.vala
index 42abc8fe..2546ba2d 100644
--- a/src/engine/imap/response/imap-mailbox-attributes.vala
+++ b/src/engine/imap/response/imap-mailbox-attributes.vala
@@ -22,11 +22,11 @@ public class Geary.Imap.MailboxAttributes : Geary.Imap.Flags {
public bool is_no_select { get {
return contains(MailboxAttribute.NO_SELECT) || contains(MailboxAttribute.NONEXISTENT);
} }
-
+
public MailboxAttributes(Gee.Collection<MailboxAttribute> attrs) {
base (attrs);
}
-
+
/**
* Create {@link MailboxAttributes} from a {@link ListParameter} of attribute strings.
*/
@@ -34,26 +34,26 @@ public class Geary.Imap.MailboxAttributes : Geary.Imap.Flags {
Gee.Collection<MailboxAttribute> list = new Gee.ArrayList<MailboxAttribute>();
for (int ctr = 0; ctr < listp.size; ctr++)
list.add(new MailboxAttribute(listp.get_as_string(ctr).ascii));
-
+
return new MailboxAttributes(list);
}
-
+
/**
* Create {@link MailboxAttributes} from a flat string of space-delimited attributes.
*/
public static MailboxAttributes deserialize(string? str) {
if (String.is_empty(str))
return new MailboxAttributes(new Gee.ArrayList<MailboxAttribute>());
-
+
string[] tokens = str.split(" ");
-
+
Gee.Collection<MailboxAttribute> attrs = new Gee.ArrayList<MailboxAttribute>();
foreach (string token in tokens)
attrs.add(new MailboxAttribute(token));
-
+
return new MailboxAttributes(attrs);
}
-
+
/**
* Search the {@link MailboxAttributes} looking for an XLIST-style
* {@link Geary.SpecialFolderType}.
@@ -61,37 +61,37 @@ public class Geary.Imap.MailboxAttributes : Geary.Imap.Flags {
public Geary.SpecialFolderType get_special_folder_type() {
if (contains(MailboxAttribute.SPECIAL_FOLDER_INBOX))
return Geary.SpecialFolderType.INBOX;
-
+
if (contains(MailboxAttribute.SPECIAL_FOLDER_ALL_MAIL))
return Geary.SpecialFolderType.ALL_MAIL;
-
+
if (contains(MailboxAttribute.SPECIAL_FOLDER_TRASH))
return Geary.SpecialFolderType.TRASH;
-
+
if (contains(MailboxAttribute.SPECIAL_FOLDER_DRAFTS))
return Geary.SpecialFolderType.DRAFTS;
-
+
if (contains(MailboxAttribute.SPECIAL_FOLDER_SENT))
return Geary.SpecialFolderType.SENT;
-
+
if (contains(MailboxAttribute.SPECIAL_FOLDER_JUNK))
return Geary.SpecialFolderType.SPAM;
-
+
if (contains(MailboxAttribute.SPECIAL_FOLDER_SPAM))
return Geary.SpecialFolderType.SPAM;
-
+
if (contains(MailboxAttribute.SPECIAL_FOLDER_STARRED))
return Geary.SpecialFolderType.FLAGGED;
-
+
if (contains(MailboxAttribute.SPECIAL_FOLDER_IMPORTANT))
return Geary.SpecialFolderType.IMPORTANT;
-
+
if (contains(MailboxAttribute.SPECIAL_FOLDER_ARCHIVE))
return Geary.SpecialFolderType.ARCHIVE;
-
+
if (contains(MailboxAttribute.SPECIAL_FOLDER_FLAGGED))
return Geary.SpecialFolderType.FLAGGED;
-
+
return Geary.SpecialFolderType.NONE;
}
}
diff --git a/src/engine/imap/response/imap-mailbox-information.vala
b/src/engine/imap/response/imap-mailbox-information.vala
index 0d00324f..80929818 100644
--- a/src/engine/imap/response/imap-mailbox-information.vala
+++ b/src/engine/imap/response/imap-mailbox-information.vala
@@ -1,7 +1,7 @@
/* Copyright 2016 Software Freedom Conservancy Inc.
*
* This software is licensed under the GNU Lesser General Public License
- * (version 2.1 or later). See the COPYING file in this distribution.
+ * (version 2.1 or later). See the COPYING file in this distribution.
*/
/**
@@ -19,23 +19,23 @@ public class Geary.Imap.MailboxInformation : BaseObject {
* Name of the mailbox.
*/
public MailboxSpecifier mailbox { get; private set; }
-
+
/**
* The (optional) delimiter specified by the server.
*/
public string? delim { get; private set; }
-
+
/**
* Folder attributes returned by the server.
*/
public MailboxAttributes attrs { get; private set; }
-
+
public MailboxInformation(MailboxSpecifier mailbox, string? delim, MailboxAttributes attrs) {
this.mailbox = mailbox;
this.delim = delim;
this.attrs = attrs;
}
-
+
/**
* Decodes {@link ServerData} into a MailboxInformation representation.
*
@@ -52,7 +52,7 @@ public class Geary.Imap.MailboxInformation : BaseObject {
StringParameter cmd = server_data.get_as_string(1);
if (!cmd.equals_ci(ListCommand.NAME) && !cmd.equals_ci(ListCommand.XLIST_NAME))
throw new ImapError.PARSE_ERROR("Not LIST or XLIST data: %s", server_data.to_string());
-
+
// Build list of attributes
ListParameter attrs = server_data.get_as_list(2);
Gee.Collection<MailboxAttribute> attrlist = new Gee.ArrayList<MailboxAttribute>();
@@ -61,13 +61,13 @@ public class Geary.Imap.MailboxInformation : BaseObject {
if (stringp == null) {
debug("Bad list attribute \"%s\": Attribute not a string value",
server_data.to_string());
-
+
continue;
}
-
+
attrlist.add(new MailboxAttribute(stringp.ascii));
}
-
+
// decode everything
MailboxAttributes attributes = new MailboxAttributes(attrlist);
StringParameter? delim = server_data.get_as_nullable_string(3);
diff --git a/src/engine/imap/response/imap-response-code-type.vala
b/src/engine/imap/response/imap-response-code-type.vala
index 79cf75a4..494b5663 100644
--- a/src/engine/imap/response/imap-response-code-type.vala
+++ b/src/engine/imap/response/imap-response-code-type.vala
@@ -37,18 +37,18 @@ public class Geary.Imap.ResponseCodeType : BaseObject, Gee.Hashable<ResponseCode
public const string UIDNEXT = "uidnext";
public const string UNAVAILABLE = "unavailable";
public const string UNSEEN = "unseen";
-
+
/**
* The original response code value submitted to the object (possibly off-the-wire).
*/
public string original { get; private set; }
-
+
/**
* The response code value set to lowercase, making it easy to compare to constant strings
* in a uniform way.
*/
public string value { get; private set; }
-
+
/**
* Throws an {@link ImapError.INVALID} if the string cannot be represented as an
* {link ResponseCodeType}.
@@ -56,7 +56,7 @@ public class Geary.Imap.ResponseCodeType : BaseObject, Gee.Hashable<ResponseCode
public ResponseCodeType(string value) throws ImapError {
init(value);
}
-
+
/**
* Throws an {@link ImapError.INVALID} if the {@link StringParameter} cannot be represented as
* an {link ResponseCodeType}.
@@ -64,33 +64,33 @@ public class Geary.Imap.ResponseCodeType : BaseObject, Gee.Hashable<ResponseCode
public ResponseCodeType.from_parameter(StringParameter stringp) throws ImapError {
init(stringp.ascii);
}
-
+
private void init(string ascii) throws ImapError {
// note that is_quoting_required() also catches empty strings (as they require quoting)
if (DataFormat.is_quoting_required(ascii) != DataFormat.Quoting.OPTIONAL)
throw new ImapError.INVALID("\"%s\" cannot be represented as a ResponseCodeType", ascii);
-
+
// store lowercased so it's easily compared with const strings above
original = ascii;
value = Ascii.strdown(ascii);
}
-
+
public bool is_value(string str) {
return Ascii.stri_equal(value, str);
}
-
+
public StringParameter to_parameter() {
return new AtomParameter(original);
}
-
+
public bool equal_to(ResponseCodeType other) {
return (this == other) ? true : Ascii.stri_equal(value, other.value);
}
-
+
public uint hash() {
return Ascii.stri_hash(value);
}
-
+
public string to_string() {
return value;
}
diff --git a/src/engine/imap/response/imap-response-code.vala
b/src/engine/imap/response/imap-response-code.vala
index aec8493a..2f283332 100644
--- a/src/engine/imap/response/imap-response-code.vala
+++ b/src/engine/imap/response/imap-response-code.vala
@@ -13,11 +13,11 @@
public class Geary.Imap.ResponseCode : Geary.Imap.ListParameter {
public ResponseCode() {
}
-
+
public ResponseCodeType get_response_code_type() throws ImapError {
return new ResponseCodeType.from_parameter(get_as_string(0));
}
-
+
/**
* Converts the {@link ResponseCode} into a UIDNEXT {@link UID}, if possible.
*
@@ -26,10 +26,10 @@ public class Geary.Imap.ResponseCode : Geary.Imap.ListParameter {
public UID get_uid_next() throws ImapError {
if (!get_response_code_type().is_value(ResponseCodeType.UIDNEXT))
throw new ImapError.INVALID("Not UIDNEXT: %s", to_string());
-
+
return new UID.checked(get_as_string(1).as_int64());
}
-
+
/**
* Converts the {@link ResponseCode} into a {@link UIDValidity}, if possible.
*
@@ -38,10 +38,10 @@ public class Geary.Imap.ResponseCode : Geary.Imap.ListParameter {
public UIDValidity get_uid_validity() throws ImapError {
if (!get_response_code_type().is_value(ResponseCodeType.UIDVALIDITY))
throw new ImapError.INVALID("Not UIDVALIDITY: %s", to_string());
-
+
return new UIDValidity.checked(get_as_string(1).as_int64());
}
-
+
/**
* Converts the {@link ResponseCode} into an UNSEEN value, if possible.
*
@@ -50,10 +50,10 @@ public class Geary.Imap.ResponseCode : Geary.Imap.ListParameter {
public int get_unseen() throws ImapError {
if (!get_response_code_type().is_value(ResponseCodeType.UNSEEN))
throw new ImapError.INVALID("Not UNSEEN: %s", to_string());
-
+
return get_as_string(1).as_int32(0, int.MAX);
}
-
+
/**
* Converts the {@link ResponseCode} into PERMANENTFLAGS {@link MessageFlags}, if possible.
*
@@ -62,10 +62,10 @@ public class Geary.Imap.ResponseCode : Geary.Imap.ListParameter {
public MessageFlags get_permanent_flags() throws ImapError {
if (!get_response_code_type().is_value(ResponseCodeType.PERMANENT_FLAGS))
throw new ImapError.INVALID("Not PERMANENTFLAGS: %s", to_string());
-
+
return MessageFlags.from_list(get_as_list(1));
}
-
+
/**
* Parses the {@link ResponseCode} into {@link Capabilities}, if possible.
*
@@ -78,17 +78,17 @@ public class Geary.Imap.ResponseCode : Geary.Imap.ListParameter {
public Capabilities get_capabilities(ref int next_revision) throws ImapError {
if (!get_response_code_type().is_value(ResponseCodeType.CAPABILITY))
throw new ImapError.INVALID("Not CAPABILITY response code: %s", to_string());
-
+
Capabilities capabilities = new Capabilities(next_revision++);
for (int ctr = 1; ctr < size; ctr++) {
StringParameter? param = get_if_string(ctr);
if (param != null)
capabilities.add_parameter(param);
}
-
+
return capabilities;
}
-
+
/**
* Parses the {@link ResponseCode} into UIDPLUS' COPYUID response, if possible.
*
@@ -103,12 +103,12 @@ public class Geary.Imap.ResponseCode : Geary.Imap.ListParameter {
out Gee.List<UID>? destination_uids) throws ImapError {
if (!get_response_code_type().is_value(ResponseCodeType.COPYUID))
throw new ImapError.INVALID("Not COPYUID response code: %s", to_string());
-
+
uidvalidity = new UIDValidity.checked(get_as_number(1).as_int64());
source_uids = MessageSet.uid_parse(get_as_string(2).ascii);
destination_uids = MessageSet.uid_parse(get_as_string(3).ascii);
}
-
+
public override string to_string() {
return "[%s]".printf(stringize_list());
}
diff --git a/src/engine/imap/response/imap-server-data-type.vala
b/src/engine/imap/response/imap-server-data-type.vala
index f7246f72..349f9dc0 100644
--- a/src/engine/imap/response/imap-server-data-type.vala
+++ b/src/engine/imap/response/imap-server-data-type.vala
@@ -23,27 +23,27 @@ public enum Geary.Imap.ServerDataType {
SEARCH,
STATUS,
XLIST;
-
+
public string to_string() {
switch (this) {
case CAPABILITY:
return "capability";
-
+
case EXISTS:
return "exists";
-
+
case EXPUNGE:
return "expunge";
-
+
case FETCH:
return "fetch";
-
+
case FLAGS:
return "flags";
-
+
case LIST:
return "list";
-
+
case LSUB:
return "lsub";
@@ -52,21 +52,21 @@ public enum Geary.Imap.ServerDataType {
case RECENT:
return "recent";
-
+
case SEARCH:
return "search";
-
+
case STATUS:
return "status";
-
+
case XLIST:
return "xlist";
-
+
default:
assert_not_reached();
}
}
-
+
/**
* Convert a {@link StringParameter} into a ServerDataType.
*
@@ -76,23 +76,23 @@ public enum Geary.Imap.ServerDataType {
switch (param.as_lower()) {
case "capability":
return CAPABILITY;
-
+
case "exists":
return EXISTS;
-
+
case "expunge":
case "expunged":
return EXPUNGE;
-
+
case "fetch":
return FETCH;
-
+
case "flags":
return FLAGS;
-
+
case "list":
return LIST;
-
+
case "lsub":
return LSUB;
@@ -101,25 +101,25 @@ public enum Geary.Imap.ServerDataType {
case "recent":
return RECENT;
-
+
case "search":
return SEARCH;
-
+
case "status":
return STATUS;
-
+
case "xlist":
return XLIST;
-
+
default:
throw new ImapError.PARSE_ERROR("\"%s\" is not a valid server data type", param.to_string());
}
}
-
+
public StringParameter to_parameter() {
return new AtomParameter(to_string());
}
-
+
/**
* Examines the {@link RootParameters} looking for a ServerDataType.
*
@@ -134,56 +134,56 @@ public enum Geary.Imap.ServerDataType {
switch (firstparam.as_lower()) {
case "capability":
return CAPABILITY;
-
+
case "flags":
return FLAGS;
-
+
case "list":
return LIST;
-
+
case "lsub":
return LSUB;
-
+
case "namespace":
return NAMESPACE;
-
+
case "search":
return SEARCH;
-
+
case "status":
return STATUS;
-
+
case "xlist":
return XLIST;
-
+
default:
// fall-through
break;
}
}
-
+
StringParameter? secondparam = root.get_if_string(2);
if (secondparam != null) {
switch (secondparam.as_lower()) {
case "exists":
return EXISTS;
-
+
case "expunge":
case "expunged":
return EXPUNGE;
-
+
case "fetch":
return FETCH;
-
+
case "recent":
return RECENT;
-
+
default:
// fall-through
break;
}
}
-
+
throw new ImapError.PARSE_ERROR("\"%s\" unrecognized server data", root.to_string());
}
}
diff --git a/src/engine/imap/response/imap-server-data.vala b/src/engine/imap/response/imap-server-data.vala
index e2e91aa4..a4202956 100644
--- a/src/engine/imap/response/imap-server-data.vala
+++ b/src/engine/imap/response/imap-server-data.vala
@@ -12,13 +12,13 @@
public class Geary.Imap.ServerData : ServerResponse {
public ServerDataType server_data_type { get; private set; }
-
+
private ServerData(Tag tag, ServerDataType server_data_type) {
base (tag);
-
+
this.server_data_type = server_data_type;
}
-
+
/**
* Converts the {@link RootParameters} into {@link ServerData}.
*
@@ -27,26 +27,26 @@ public class Geary.Imap.ServerData : ServerResponse {
*/
public ServerData.migrate(RootParameters root) throws ImapError {
base.migrate(root);
-
+
server_data_type = ServerDataType.from_response(this);
}
-
+
/**
* Returns true if {@link RootParameters} is recognized by {@link ServerDataType.from_response}.
*/
public static bool is_server_data(RootParameters root) {
if (!root.has_tag())
return false;
-
+
try {
ServerDataType.from_response(root);
-
+
return true;
} catch (ImapError ierr) {
return false;
}
}
-
+
/**
* Parses the {@link ServerData} into {@link Capabilities}, if possible.
*
@@ -59,17 +59,17 @@ public class Geary.Imap.ServerData : ServerResponse {
public Capabilities get_capabilities(ref int next_revision) throws ImapError {
if (server_data_type != ServerDataType.CAPABILITY)
throw new ImapError.INVALID("Not CAPABILITY data: %s", to_string());
-
+
Capabilities capabilities = new Capabilities(next_revision++);
for (int ctr = 2; ctr < size; ctr++) {
StringParameter? param = get_if_string(ctr);
if (param != null)
capabilities.add_parameter(param);
}
-
+
return capabilities;
}
-
+
/**
* Parses the {@link ServerData} into an {@link ServerDataType.EXISTS} value, if possible.
*
@@ -78,10 +78,10 @@ public class Geary.Imap.ServerData : ServerResponse {
public int get_exists() throws ImapError {
if (server_data_type != ServerDataType.EXISTS)
throw new ImapError.INVALID("Not EXISTS data: %s", to_string());
-
+
return get_as_string(1).as_int32(0);
}
-
+
/**
* Parses the {@link ServerData} into an expunged {@link SequenceNumber}, if possible.
*
@@ -90,10 +90,10 @@ public class Geary.Imap.ServerData : ServerResponse {
public SequenceNumber get_expunge() throws ImapError {
if (server_data_type != ServerDataType.EXPUNGE)
throw new ImapError.INVALID("Not EXPUNGE data: %s", to_string());
-
+
return new SequenceNumber.checked(get_as_string(1).as_int64());
}
-
+
/**
* Parses the {@link ServerData} into {@link FetchedData}, if possible.
*
@@ -102,10 +102,10 @@ public class Geary.Imap.ServerData : ServerResponse {
public FetchedData get_fetch() throws ImapError {
if (server_data_type != ServerDataType.FETCH)
throw new ImapError.INVALID("Not FETCH data: %s", to_string());
-
+
return FetchedData.decode(this);
}
-
+
/**
* Parses the {@link ServerData} into {@link MailboxAttributes}, if possible.
*
@@ -114,10 +114,10 @@ public class Geary.Imap.ServerData : ServerResponse {
public MailboxAttributes get_flags() throws ImapError {
if (server_data_type != ServerDataType.FLAGS)
throw new ImapError.INVALID("Not FLAGS data: %s", to_string());
-
+
return MailboxAttributes.from_list(get_as_list(2));
}
-
+
/**
* Parses the {@link ServerData} into {@link MailboxInformation}, if possible.
*
@@ -126,7 +126,7 @@ public class Geary.Imap.ServerData : ServerResponse {
public MailboxInformation get_list() throws ImapError {
if (server_data_type != ServerDataType.LIST && server_data_type != ServerDataType.XLIST)
throw new ImapError.INVALID("Not LIST/XLIST data: %s", to_string());
-
+
return MailboxInformation.decode(this, true);
}
@@ -150,10 +150,10 @@ public class Geary.Imap.ServerData : ServerResponse {
public int get_recent() throws ImapError {
if (server_data_type != ServerDataType.RECENT)
throw new ImapError.INVALID("Not RECENT data: %s", to_string());
-
+
return get_as_string(1).as_int32(0);
}
-
+
/**
* Parses the {@link ServerData} into a {@link ServerDataType.SEARCH} value, if possible.
*
@@ -162,17 +162,17 @@ public class Geary.Imap.ServerData : ServerResponse {
public int64[] get_search() throws ImapError {
if (server_data_type != ServerDataType.SEARCH)
throw new ImapError.INVALID("Not SEARCH data: %s", to_string());
-
+
if (size <= 2)
return new int64[0];
-
+
int64[] results = new int64[size - 2];
for (int ctr = 2; ctr < size; ctr++)
results[ctr - 2] = get_as_string(ctr).as_int64(0);
-
+
return results;
}
-
+
/**
* Parses the {@link ServerData} into {@link StatusData}, if possible.
*
@@ -181,7 +181,7 @@ public class Geary.Imap.ServerData : ServerResponse {
public StatusData get_status() throws ImapError {
if (server_data_type != ServerDataType.STATUS)
throw new ImapError.INVALID("Not STATUS data: %s", to_string());
-
+
return StatusData.decode(this);
}
}
diff --git a/src/engine/imap/response/imap-server-response.vala
b/src/engine/imap/response/imap-server-response.vala
index 43565196..fad1d274 100644
--- a/src/engine/imap/response/imap-server-response.vala
+++ b/src/engine/imap/response/imap-server-response.vala
@@ -15,11 +15,11 @@
public abstract class Geary.Imap.ServerResponse : RootParameters {
public Tag tag { get; private set; }
-
+
protected ServerResponse(Tag tag) {
this.tag = tag;
}
-
+
/**
* Converts the {@link RootParameters} into a ServerResponse.
*
@@ -27,13 +27,13 @@ public abstract class Geary.Imap.ServerResponse : RootParameters {
*/
public ServerResponse.migrate(RootParameters root) throws ImapError {
base.migrate(root);
-
+
if (!has_tag())
throw new ImapError.INVALID("Server response does not have a tag token: %s", to_string());
-
+
tag = get_tag();
}
-
+
/**
* Migrate the contents of RootParameters into a new, properly-typed ServerResponse.
*
@@ -47,13 +47,13 @@ public abstract class Geary.Imap.ServerResponse : RootParameters {
public static ServerResponse migrate_from_server(RootParameters root) throws ImapError {
if (ContinuationResponse.is_continuation_response(root))
return new ContinuationResponse.migrate(root);
-
+
if (StatusResponse.is_status_response(root))
return new StatusResponse.migrate(root);
-
+
if (ServerData.is_server_data(root))
return new ServerData.migrate(root);
-
+
throw new ImapError.PARSE_ERROR("Unknown server response: %s", root.to_string());
}
}
diff --git a/src/engine/imap/response/imap-status-data.vala b/src/engine/imap/response/imap-status-data.vala
index 8992d227..9c1786e9 100644
--- a/src/engine/imap/response/imap-status-data.vala
+++ b/src/engine/imap/response/imap-status-data.vala
@@ -16,41 +16,41 @@ public class Geary.Imap.StatusData : Object {
// NOTE: This must be negative one; other values won't work well due to how the values are
// decoded
public const int UNSET = -1;
-
+
/**
* Name of the mailbox.
*/
public MailboxSpecifier mailbox { get; private set; }
-
+
/**
* {@link UNSET} if not set.
*/
public int messages { get; private set; }
-
+
/**
* {@link UNSET} if not set.
*/
public int recent { get; private set; }
-
+
/**
* The UIDNEXT of the mailbox, if returned.
*
* See [[http://tools.ietf.org/html/rfc3501#section-2.3.1.1]]
*/
public UID? uid_next { get; private set; }
-
+
/**
* The UIDVALIDITY of the mailbox, if returned.
*
* See [[http://tools.ietf.org/html/rfc3501#section-2.3.1.1]]
*/
public UIDValidity? uid_validity { get; private set; }
-
+
/**
* {@link UNSET} if not set.
*/
public int unseen { get; private set; }
-
+
public StatusData(MailboxSpecifier mailbox, int messages, int recent, UID? uid_next,
UIDValidity? uid_validity, int unseen) {
this.mailbox = mailbox;
@@ -60,7 +60,7 @@ public class Geary.Imap.StatusData : Object {
this.uid_validity = uid_validity;
this.unseen = unseen;
}
-
+
/**
* Decodes {@link ServerData} into a StatusData representation.
*
@@ -82,37 +82,37 @@ public class Geary.Imap.StatusData : Object {
UID? uid_next = null;
UIDValidity? uid_validity = null;
int unseen = UNSET;
-
+
ListParameter values = server_data.get_as_list(3);
for (int ctr = 0; ctr < values.size; ctr += 2) {
try {
StringParameter typep = values.get_as_string(ctr);
StringParameter valuep = values.get_as_string(ctr + 1);
-
+
switch (StatusDataType.from_parameter(typep)) {
case StatusDataType.MESSAGES:
// see note at UNSET
messages = valuep.as_int32(-1, int.MAX);
break;
-
+
case StatusDataType.RECENT:
// see note at UNSET
recent = valuep.as_int32(-1, int.MAX);
break;
-
+
case StatusDataType.UIDNEXT:
uid_next = new UID.checked(valuep.as_int64());
break;
-
+
case StatusDataType.UIDVALIDITY:
uid_validity = new UIDValidity.checked(valuep.as_int64());
break;
-
+
case StatusDataType.UNSEEN:
// see note at UNSET
unseen = valuep.as_int32(-1, int.MAX);
break;
-
+
default:
message("Bad STATUS data type %s", typep.to_string());
break;
@@ -122,11 +122,11 @@ public class Geary.Imap.StatusData : Object {
server_data.to_string(), ierr.message);
}
}
-
+
return new StatusData(new MailboxSpecifier.from_parameter(mailbox_param), messages, recent,
uid_next, uid_validity, unseen);
}
-
+
public string to_string() {
return "%s/%d/UIDNEXT=%s/UIDVALIDITY=%s".printf(mailbox.to_string(), messages,
(uid_next != null) ? uid_next.to_string() : "(none)",
diff --git a/src/engine/imap/response/imap-status-response.vala
b/src/engine/imap/response/imap-status-response.vala
index 1bf3b74d..2b5c7336 100644
--- a/src/engine/imap/response/imap-status-response.vala
+++ b/src/engine/imap/response/imap-status-response.vala
@@ -23,25 +23,25 @@ public class Geary.Imap.StatusResponse : ServerResponse {
* {@link Status.OK}, {@link Status.NO}, or {@link Status.BAD}.
*/
public bool is_completion { get; private set; default = false; }
-
+
/**
* The {@link Status} being reported by the server in this {@link ServerResponse}.
*/
public Status status { get; private set; }
-
+
/**
* An optional {@link ResponseCode} reported by the server in this {@link ServerResponse}.
*/
public ResponseCode? response_code { get; private set; }
-
+
private StatusResponse(Tag tag, Status status, ResponseCode? response_code) {
base (tag);
-
+
this.status = status;
this.response_code = response_code;
update_is_completion();
}
-
+
/**
* Converts the {@link RootParameters} into a {@link StatusResponse}.
*
@@ -50,12 +50,12 @@ public class Geary.Imap.StatusResponse : ServerResponse {
*/
public StatusResponse.migrate(RootParameters root) throws ImapError {
base.migrate(root);
-
+
status = Status.from_parameter(get_as_string(1));
response_code = get_if_list(2) as ResponseCode;
update_is_completion();
}
-
+
private void update_is_completion() {
// TODO: Is this too stringent? It means a faulty server could send back a completion
// with another Status code and cause the client to treat the command as "unanswered",
@@ -68,14 +68,14 @@ public class Geary.Imap.StatusResponse : ServerResponse {
case Status.BAD:
is_completion = true;
break;
-
+
default:
// fall through
break;
}
}
}
-
+
/**
* Returns optional text provided by the server. Note that this text is not internationalized
* and probably in English, and is not standard or uniformly declared. It's not recommended
@@ -93,20 +93,20 @@ public class Geary.Imap.StatusResponse : ServerResponse {
builder.append_c(' ');
}
}
-
+
return !String.is_empty(builder.str) ? builder.str : null;
}
-
+
/**
* Returns true if {@link RootParameters} holds a {@link Status} parameter.
*/
public static bool is_status_response(RootParameters root) {
if (!root.has_tag())
return false;
-
+
try {
Status.from_parameter(root.get_as_string(1));
-
+
return true;
} catch (ImapError err) {
return false;
diff --git a/src/engine/imap/response/imap-status.vala b/src/engine/imap/response/imap-status.vala
index fb1fe6eb..ec1ea8f4 100644
--- a/src/engine/imap/response/imap-status.vala
+++ b/src/engine/imap/response/imap-status.vala
@@ -16,51 +16,51 @@ public enum Geary.Imap.Status {
BAD,
PREAUTH,
BYE;
-
+
public string to_string() {
switch (this) {
case OK:
return "ok";
-
+
case NO:
return "no";
-
+
case BAD:
return "bad";
-
+
case PREAUTH:
return "preauth";
-
+
case BYE:
return "bye";
-
+
default:
assert_not_reached();
}
}
-
+
public static Status from_parameter(StringParameter strparam) throws ImapError {
switch (strparam.as_lower()) {
case "ok":
return OK;
-
+
case "no":
return NO;
-
+
case "bad":
return BAD;
-
+
case "preauth":
return PREAUTH;
-
+
case "bye":
return BYE;
-
+
default:
throw new ImapError.PARSE_ERROR("Unrecognized status response \"%s\"", strparam.to_string());
}
}
-
+
public Parameter to_parameter() {
return new AtomParameter(to_string());
}
diff --git a/src/engine/imap/transport/imap-client-connection.vala
b/src/engine/imap/transport/imap-client-connection.vala
index e0789743..4f7e9a26 100644
--- a/src/engine/imap/transport/imap-client-connection.vala
+++ b/src/engine/imap/transport/imap-client-connection.vala
@@ -83,7 +83,7 @@ public class Geary.Imap.ClientConnection : BaseObject {
Logging.debug(Logging.Flag.NETWORK, "[%s] connected to %s", to_string(),
endpoint.to_string());
}
-
+
public virtual signal void disconnected() {
Logging.debug(Logging.Flag.NETWORK, "[%s] disconnected from %s", to_string(),
endpoint.to_string());
@@ -100,37 +100,37 @@ public class Geary.Imap.ClientConnection : BaseObject {
public virtual signal void received_server_data(ServerData server_data) {
Logging.debug(Logging.Flag.NETWORK, "[%s R] %s", to_string(), server_data.to_string());
}
-
+
public virtual signal void received_continuation_response(ContinuationResponse continuation_response) {
Logging.debug(Logging.Flag.NETWORK, "[%s R] %s", to_string(), continuation_response.to_string());
}
-
+
public virtual signal void received_bytes(size_t bytes) {
// this generates a *lot* of debug logging if one was placed here, so it's not
}
-
+
public virtual signal void received_bad_response(RootParameters root, ImapError err) {
Logging.debug(Logging.Flag.NETWORK, "[%s] recv bad response %s: %s", to_string(),
root.to_string(), err.message);
}
-
+
public virtual signal void recv_closed() {
Logging.debug(Logging.Flag.NETWORK, "[%s] recv closed", to_string());
}
-
+
public virtual signal void send_failure(Error err) {
Logging.debug(Logging.Flag.NETWORK, "[%s] send failure: %s", to_string(), err.message);
}
-
+
public virtual signal void receive_failure(Error err) {
Logging.debug(Logging.Flag.NETWORK, "[%s] recv failure: %s", to_string(), err.message);
}
-
+
public virtual signal void deserialize_failure(Error err) {
Logging.debug(Logging.Flag.NETWORK, "[%s] deserialize failure: %s", to_string(),
err.message);
}
-
+
public virtual signal void close_error(Error err) {
Logging.debug(Logging.Flag.NETWORK, "[%s] close error: %s", to_string(), err.message);
}
@@ -151,26 +151,26 @@ public class Geary.Imap.ClientConnection : BaseObject {
public SocketAddress? get_remote_address() {
if (cx == null)
return null;
-
+
try {
return cx.get_remote_address();
} catch (Error err) {
debug("Unable to retrieve remote address: %s", err.message);
}
-
+
return null;
}
-
+
public SocketAddress? get_local_address() {
if (cx == null)
return null;
-
+
try {
return cx.get_local_address();
} catch (Error err) {
debug("Unable to retrieve local address: %s", err.message);
}
-
+
return null;
}
@@ -289,11 +289,11 @@ public class Geary.Imap.ClientConnection : BaseObject {
public async void starttls_async(Cancellable? cancellable = null) throws Error {
if (cx == null)
throw new ImapError.NOT_SUPPORTED("[%s] Unable to enable TLS: no connection", to_string());
-
+
// (mostly) silent fail in this case
if (cx is TlsClientConnection) {
debug("[%s] Already TLS connection", to_string());
-
+
return;
}
@@ -303,9 +303,9 @@ public class Geary.Imap.ClientConnection : BaseObject {
// wrap connection with TLS connection
TlsClientConnection tls_cx = yield endpoint.starttls_handshake_async(cx, cancellable);
-
+
ios = tls_cx;
-
+
// re-open Serializer/Deserializer with the new streams
yield open_channels_async();
}
diff --git a/src/engine/imap/transport/imap-client-session.vala
b/src/engine/imap/transport/imap-client-session.vala
index 76801cf2..0b62370f 100644
--- a/src/engine/imap/transport/imap-client-session.vala
+++ b/src/engine/imap/transport/imap-client-session.vala
@@ -102,66 +102,66 @@ public class Geary.Imap.ClientSession : BaseObject {
*/
CLOSING_MAILBOX
}
-
+
public enum DisconnectReason {
LOCAL_CLOSE,
LOCAL_ERROR,
REMOTE_CLOSE,
REMOTE_ERROR;
-
+
public bool is_error() {
return (this == LOCAL_ERROR) || (this == REMOTE_ERROR);
}
-
+
public bool is_remote() {
return (this == REMOTE_CLOSE) || (this == REMOTE_ERROR);
}
}
-
+
// Many of the async commands go through the FSM, and this is used to pass state in and out of
// the it
private class MachineParams : Object {
// IN
public Command? cmd;
-
+
// OUT
public Error? err = null;
public bool proceed = false;
-
+
public MachineParams(Command? cmd) {
this.cmd = cmd;
}
}
-
+
// Need this because delegates with targets cannot be stored in ADTs.
private class CommandCallback {
public unowned SourceFunc callback;
-
+
public CommandCallback(SourceFunc callback) {
this.callback = callback;
}
}
-
+
private class SendCommandOperation : Nonblocking.BatchOperation {
// IN
public ClientSession owner;
public Command cmd;
-
+
// OUT
public StatusResponse response;
-
+
public SendCommandOperation(ClientSession owner, Command cmd) {
this.owner = owner;
this.cmd = cmd;
}
-
+
public override async Object? execute_async(Cancellable? cancellable) throws Error {
response = yield owner.command_transaction_async(cmd, cancellable);
-
+
return response;
}
}
-
+
private enum State {
// canonical IMAP session states
UNCONNECTED,
@@ -169,24 +169,24 @@ public class Geary.Imap.ClientSession : BaseObject {
AUTHORIZED,
SELECTED,
LOGGED_OUT,
-
+
// transitional states
CONNECTING,
AUTHORIZING,
SELECTING,
CLOSING_MAILBOX,
LOGGING_OUT,
-
+
// terminal state
BROKEN,
-
+
COUNT
}
-
+
private static string state_to_string(uint state) {
return ((State) state).to_string();
}
-
+
private enum Event {
// user-initiated events
CONNECT,
@@ -206,20 +206,20 @@ public class Geary.Imap.ClientSession : BaseObject {
// I/O errors
RECV_ERROR,
SEND_ERROR,
-
+
TIMEOUT,
-
+
COUNT;
}
-
+
private static string event_to_string(uint event) {
return ((Event) event).to_string();
}
-
+
private static Geary.State.MachineDescriptor machine_desc = new Geary.State.MachineDescriptor(
"Geary.Imap.ClientSession", State.UNCONNECTED, State.COUNT, Event.COUNT,
state_to_string, event_to_string);
-
+
/**
* {@link ClientSession} tracks server extensions reported via the CAPABILITY server data
* response.
@@ -288,48 +288,48 @@ public class Geary.Imap.ClientSession : BaseObject {
//
// Connection state changes
//
-
+
public signal void connected();
-
+
public signal void session_denied(string? reason);
-
+
public signal void authorized();
-
+
public signal void logged_out();
-
+
public signal void login_failed(StatusResponse? response);
-
+
public signal void disconnected(DisconnectReason reason);
-
+
public signal void status_response_received(StatusResponse status_response);
-
+
/**
* Fired after the specific {@link ServerData} signals (i.e. {@link capability}, {@link exists}
* {@link expunge}, etc.)
*/
public signal void server_data_received(ServerData server_data);
-
+
public signal void capability(Capabilities capabilities);
-
+
public signal void exists(int count);
-
+
public signal void expunge(SequenceNumber seq_num);
-
+
public signal void fetch(FetchedData fetched_data);
-
+
public signal void flags(MailboxAttributes mailbox_attrs);
-
+
/**
* Fired when a LIST or XLIST {@link ServerData} is returned from the server.
*/
public signal void list(MailboxInformation mailbox_info);
-
+
// TODO: LSUB results
-
+
public signal void recent(int count);
-
+
public signal void search(int64[] seq_or_uid);
-
+
public signal void status(StatusData status_data);
public signal void @namespace(NamespaceResponse namespace);
@@ -341,10 +341,10 @@ public class Geary.Imap.ClientSession : BaseObject {
*/
public signal void current_mailbox_changed(MailboxSpecifier? old_name, MailboxSpecifier? new_name,
bool readonly);
-
+
public ClientSession(Endpoint imap_endpoint) {
this.imap_endpoint = imap_endpoint;
-
+
Geary.State.Mapping[] mappings = {
new Geary.State.Mapping(State.UNCONNECTED, Event.CONNECT, on_connect),
new Geary.State.Mapping(State.UNCONNECTED, Event.LOGIN, on_early_command),
@@ -353,7 +353,7 @@ public class Geary.Imap.ClientSession : BaseObject {
new Geary.State.Mapping(State.UNCONNECTED, Event.CLOSE_MAILBOX, on_early_command),
new Geary.State.Mapping(State.UNCONNECTED, Event.LOGOUT, on_early_command),
new Geary.State.Mapping(State.UNCONNECTED, Event.DISCONNECT, Geary.State.nop),
-
+
new Geary.State.Mapping(State.CONNECTING, Event.CONNECT, on_already_connected),
new Geary.State.Mapping(State.CONNECTING, Event.LOGIN, on_early_command),
new Geary.State.Mapping(State.CONNECTING, Event.SEND_CMD, on_early_command),
@@ -367,7 +367,7 @@ public class Geary.Imap.ClientSession : BaseObject {
new Geary.State.Mapping(State.CONNECTING, Event.SEND_ERROR, on_connecting_send_recv_error),
new Geary.State.Mapping(State.CONNECTING, Event.RECV_ERROR, on_connecting_send_recv_error),
new Geary.State.Mapping(State.CONNECTING, Event.TIMEOUT, on_connecting_timeout),
-
+
new Geary.State.Mapping(State.NOAUTH, Event.CONNECT, on_already_connected),
new Geary.State.Mapping(State.NOAUTH, Event.LOGIN, on_login),
new Geary.State.Mapping(State.NOAUTH, Event.SEND_CMD, on_send_command),
@@ -379,7 +379,7 @@ public class Geary.Imap.ClientSession : BaseObject {
new Geary.State.Mapping(State.NOAUTH, Event.RECV_COMPLETION, on_recv_status),
new Geary.State.Mapping(State.NOAUTH, Event.SEND_ERROR, on_send_error),
new Geary.State.Mapping(State.NOAUTH, Event.RECV_ERROR, on_recv_error),
-
+
new Geary.State.Mapping(State.AUTHORIZING, Event.CONNECT, on_already_connected),
new Geary.State.Mapping(State.AUTHORIZING, Event.LOGIN, on_logging_in),
new Geary.State.Mapping(State.AUTHORIZING, Event.SEND_CMD, on_unauthenticated),
@@ -391,7 +391,7 @@ public class Geary.Imap.ClientSession : BaseObject {
new Geary.State.Mapping(State.AUTHORIZING, Event.RECV_COMPLETION, on_login_recv_completion),
new Geary.State.Mapping(State.AUTHORIZING, Event.SEND_ERROR, on_send_error),
new Geary.State.Mapping(State.AUTHORIZING, Event.RECV_ERROR, on_recv_error),
-
+
new Geary.State.Mapping(State.AUTHORIZED, Event.CONNECT, on_already_connected),
new Geary.State.Mapping(State.AUTHORIZED, Event.LOGIN, on_already_logged_in),
new Geary.State.Mapping(State.AUTHORIZED, Event.SEND_CMD, on_send_command),
@@ -403,7 +403,7 @@ public class Geary.Imap.ClientSession : BaseObject {
new Geary.State.Mapping(State.AUTHORIZED, Event.RECV_COMPLETION, on_recv_status),
new Geary.State.Mapping(State.AUTHORIZED, Event.SEND_ERROR, on_send_error),
new Geary.State.Mapping(State.AUTHORIZED, Event.RECV_ERROR, on_recv_error),
-
+
new Geary.State.Mapping(State.SELECTING, Event.CONNECT, on_already_connected),
new Geary.State.Mapping(State.SELECTING, Event.LOGIN, on_already_logged_in),
new Geary.State.Mapping(State.SELECTING, Event.SEND_CMD, on_send_command),
@@ -416,7 +416,7 @@ public class Geary.Imap.ClientSession : BaseObject {
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),
-
+
new Geary.State.Mapping(State.SELECTED, Event.CONNECT, on_already_connected),
new Geary.State.Mapping(State.SELECTED, Event.LOGIN, on_already_logged_in),
new Geary.State.Mapping(State.SELECTED, Event.SEND_CMD, on_send_command),
@@ -428,7 +428,7 @@ public class Geary.Imap.ClientSession : BaseObject {
new Geary.State.Mapping(State.SELECTED, Event.RECV_COMPLETION, on_recv_status),
new Geary.State.Mapping(State.SELECTED, Event.SEND_ERROR, on_send_error),
new Geary.State.Mapping(State.SELECTED, Event.RECV_ERROR, on_recv_error),
-
+
new Geary.State.Mapping(State.CLOSING_MAILBOX, Event.CONNECT, on_already_connected),
new Geary.State.Mapping(State.CLOSING_MAILBOX, Event.LOGIN, on_already_logged_in),
new Geary.State.Mapping(State.CLOSING_MAILBOX, Event.SEND_CMD, on_send_command),
@@ -440,7 +440,7 @@ public class Geary.Imap.ClientSession : BaseObject {
new Geary.State.Mapping(State.CLOSING_MAILBOX, Event.RECV_COMPLETION,
on_closing_recv_completion),
new Geary.State.Mapping(State.CLOSING_MAILBOX, Event.SEND_ERROR, on_send_error),
new Geary.State.Mapping(State.CLOSING_MAILBOX, Event.RECV_ERROR, on_recv_error),
-
+
new Geary.State.Mapping(State.LOGGING_OUT, Event.CONNECT, on_already_connected),
new Geary.State.Mapping(State.LOGGING_OUT, Event.LOGIN, on_already_logged_in),
new Geary.State.Mapping(State.LOGGING_OUT, Event.SEND_CMD, on_late_command),
@@ -452,7 +452,7 @@ public class Geary.Imap.ClientSession : BaseObject {
new Geary.State.Mapping(State.LOGGING_OUT, Event.RECV_COMPLETION,
on_logging_out_recv_completion),
new Geary.State.Mapping(State.LOGGING_OUT, Event.RECV_ERROR, on_recv_error),
new Geary.State.Mapping(State.LOGGING_OUT, Event.SEND_ERROR, on_send_error),
-
+
new Geary.State.Mapping(State.LOGGED_OUT, Event.CONNECT, on_already_connected),
new Geary.State.Mapping(State.LOGGED_OUT, Event.LOGIN, on_already_logged_in),
new Geary.State.Mapping(State.LOGGED_OUT, Event.SEND_CMD, on_late_command),
@@ -464,7 +464,7 @@ public class Geary.Imap.ClientSession : BaseObject {
new Geary.State.Mapping(State.LOGGED_OUT, Event.RECV_COMPLETION, on_dropped_response),
new Geary.State.Mapping(State.LOGGED_OUT, Event.RECV_ERROR, on_recv_error),
new Geary.State.Mapping(State.LOGGED_OUT, Event.SEND_ERROR, on_send_error),
-
+
new Geary.State.Mapping(State.BROKEN, Event.CONNECT, on_late_command),
new Geary.State.Mapping(State.BROKEN, Event.LOGIN, on_late_command),
new Geary.State.Mapping(State.BROKEN, Event.SEND_CMD, on_late_command),
@@ -478,11 +478,11 @@ public class Geary.Imap.ClientSession : BaseObject {
new Geary.State.Mapping(State.BROKEN, Event.SEND_ERROR, Geary.State.nop),
new Geary.State.Mapping(State.BROKEN, Event.RECV_ERROR, Geary.State.nop),
};
-
+
fsm = new Geary.State.Machine(machine_desc, mappings, on_ignored_transition);
fsm.set_logging(false);
}
-
+
~ClientSession() {
switch (fsm.get_state()) {
case State.UNCONNECTED:
@@ -496,11 +496,11 @@ public class Geary.Imap.ClientSession : BaseObject {
debug("DTOR: ClientSession %s", to_string());
}
-
+
public MailboxSpecifier? get_current_mailbox() {
return current_mailbox;
}
-
+
public bool is_current_mailbox_readonly() {
return current_mailbox_readonly;
}
@@ -588,42 +588,42 @@ public class Geary.Imap.ClientSession : BaseObject {
*/
public ProtocolState get_protocol_state(out MailboxSpecifier? current_mailbox) {
current_mailbox = null;
-
+
switch (fsm.get_state()) {
case State.UNCONNECTED:
case State.LOGGED_OUT:
case State.LOGGING_OUT:
case State.BROKEN:
return ProtocolState.UNCONNECTED;
-
+
case State.NOAUTH:
return ProtocolState.UNAUTHORIZED;
-
+
case State.AUTHORIZED:
return ProtocolState.AUTHORIZED;
-
+
case State.SELECTED:
current_mailbox = this.current_mailbox;
-
+
return ProtocolState.SELECTED;
-
+
case State.CONNECTING:
return ProtocolState.CONNECTING;
-
+
case State.AUTHORIZING:
return ProtocolState.AUTHORIZING;
-
+
case State.SELECTING:
return ProtocolState.SELECTING;
-
+
case State.CLOSING_MAILBOX:
return ProtocolState.CLOSING_MAILBOX;
-
+
default:
assert_not_reached();
}
}
-
+
// Some commands require waiting for a completion response in order to shift the state machine's
// State; this allocates such a wait, returning false if another command is outstanding also
// waiting for one to finish
@@ -632,33 +632,33 @@ public class Geary.Imap.ClientSession : BaseObject {
params.proceed = false;
params.err = new ImapError.NOT_SUPPORTED("Cannot perform operation %s while session is %s",
fsm.get_event_string(event), fsm.get_state_string(state));
-
+
return false;
}
-
+
state_change_cmd = params.cmd;
params.proceed = true;
-
+
return true;
}
-
+
// This is the complement to reserve_state_change_cmd(), returning true if the response represents
// the pending state change Command (and clearing it if it is)
private bool validate_state_change_cmd(ServerResponse response, out Command? cmd = null) {
cmd = state_change_cmd;
-
+
if (state_change_cmd == null || !state_change_cmd.tag.equal_to(response.tag))
return false;
-
+
state_change_cmd = null;
-
+
return true;
}
-
+
//
// connect
//
-
+
/**
* Connect to the server.
*
@@ -678,28 +678,28 @@ public class Geary.Imap.ClientSession : BaseObject {
throws GLib.Error {
MachineParams params = new MachineParams(null);
fsm.issue(Event.CONNECT, null, params);
-
+
if (params.err != null)
throw params.err;
-
+
assert(params.proceed);
-
+
// ClientConnection and the connection waiter should exist at this point
assert(cx != null);
assert(connect_waiter != null);
-
+
// connect and let ClientConnection's signals drive the show
try {
yield cx.connect_async(cancellable);
} catch (Error err) {
fsm.issue(Event.SEND_ERROR, null, null, err);
-
+
throw err;
}
-
+
// set up timer to wait for greeting from server
Scheduler.Scheduled timeout = Scheduler.after_sec(GREETING_TIMEOUT_SEC, on_greeting_timeout);
-
+
// wait for the initial greeting or a timeout ... this prevents the caller from turning
// around and issuing a command while still in CONNECTING state
try {
@@ -710,7 +710,7 @@ public class Geary.Imap.ClientSession : BaseObject {
// cancel the timeout, if it's not already fired
timeout.cancel();
-
+
// if session was denied or timeout, ensure the session is disconnected and throw the
// original Error ... connect_async shouldn't leave the session in a LOGGED_OUT state,
// but completely disconnected if unsuccessful
@@ -721,22 +721,22 @@ public class Geary.Imap.ClientSession : BaseObject {
debug("[%s] Error disconnecting after a failed connect attempt: %s", to_string(),
err.message);
}
-
+
throw connect_err;
}
}
-
+
private bool on_greeting_timeout() {
// if still in CONNECTING state, the greeting never arrived
if (fsm.get_state() == State.CONNECTING)
fsm.issue(Event.TIMEOUT);
-
+
return false;
}
-
+
private uint on_connect(uint state, uint event, void *user, Object? object) {
MachineParams params = (MachineParams) object;
-
+
assert(cx == null);
cx = new ClientConnection(imap_endpoint);
cx.connected.connect(on_network_connected);
@@ -764,7 +764,7 @@ public class Geary.Imap.ClientSession : BaseObject {
// ClientSession
private void drop_connection() {
unschedule_keepalive();
-
+
if (cx != null) {
cx.connected.disconnect(on_network_connected);
cx.disconnected.disconnect(on_network_disconnected);
@@ -778,21 +778,21 @@ public class Geary.Imap.ClientSession : BaseObject {
cx.recv_closed.disconnect(on_received_closed);
cx.receive_failure.disconnect(on_network_receive_failure);
cx.deserialize_failure.disconnect(on_network_receive_failure);
-
+
cx = null;
}
}
private uint on_connected(uint state, uint event) {
debug("[%s] Connected", to_string());
-
+
// stay in current state -- wait for initial status response to move into NOAUTH or LOGGED OUT
return state;
}
-
+
private uint on_connecting_recv_status(uint state, uint event, void *user, Object? object) {
StatusResponse status_response = (StatusResponse) object;
-
+
// see on_connected() why signals and semaphore are delayed for this event
try {
connect_waiter.notify();
@@ -800,21 +800,21 @@ public class Geary.Imap.ClientSession : BaseObject {
message("[%s] Unable to notify connect_waiter of connection: %s", to_string(),
err.message);
}
-
+
if (status_response.status == Status.OK) {
fsm.do_post_transition(() => { connected(); });
-
+
return State.NOAUTH;
}
-
+
debug("[%s] Connect denied: %s", to_string(), status_response.to_string());
-
+
fsm.do_post_transition(() => { session_denied(status_response.get_text()); });
connect_err = new ImapError.SERVER_ERROR("Session denied: %s", status_response.get_text());
-
+
return State.LOGGED_OUT;
}
-
+
private uint on_connecting_timeout(uint state, uint event) {
// wake up the waiting task in connect_async
try {
@@ -823,12 +823,12 @@ public class Geary.Imap.ClientSession : BaseObject {
message("[%s] Unable to notify connect_waiter of timeout: %s", to_string(),
err.message);
}
-
+
debug("[%s] Connect timed-out", to_string());
-
+
connect_err = new IOError.TIMED_OUT("Session greeting not seen in %u seconds",
GREETING_TIMEOUT_SEC);
-
+
return State.LOGGED_OUT;
}
@@ -1068,29 +1068,29 @@ public class Geary.Imap.ClientSession : BaseObject {
private uint on_logging_in(uint state, uint event, void *user, Object? object) {
MachineParams params = (MachineParams) object;
-
+
params.err = new ImapError.ALREADY_CONNECTED("Already logging in to %s", to_string());
-
+
return state;
}
-
+
private uint on_login_recv_completion(uint state, uint event, void *user, Object? object) {
StatusResponse completion_response = (StatusResponse) object;
-
+
if (!validate_state_change_cmd(completion_response))
return state;
-
+
// Remember: only you can prevent firing signals inside state transition handlers
switch (completion_response.status) {
case Status.OK:
fsm.do_post_transition(() => { authorized(); });
-
+
return State.AUTHORIZED;
-
+
default:
debug("[%s] Unable to LOGIN: %s", to_string(), completion_response.to_string());
fsm.do_post_transition((resp) => { login_failed((StatusResponse)resp); },
completion_response);
-
+
return State.NOAUTH;
}
}
@@ -1099,7 +1099,7 @@ public class Geary.Imap.ClientSession : BaseObject {
// keepalives (nop idling to keep the session alive and to periodically receive notifications
// of changes)
//
-
+
/**
* If seconds is negative or zero, keepalives will be disabled. (This is not recommended.)
*
@@ -1111,25 +1111,25 @@ public class Geary.Imap.ClientSession : BaseObject {
selected_keepalive_secs = seconds_while_selected;
selected_with_idle_keepalive_secs = seconds_while_selected_with_idle;
unselected_keepalive_secs = seconds_while_unselected;
-
+
// schedule one now, although will be rescheduled if traffic is received before it fires
schedule_keepalive();
}
-
+
/**
* Returns true if keepalives are disactivated, false if already disabled.
*/
public bool disable_keepalives() {
return unschedule_keepalive();
}
-
+
private bool unschedule_keepalive() {
if (keepalive_id == 0)
return false;
-
+
Source.remove(keepalive_id);
keepalive_id = 0;
-
+
return true;
}
@@ -1164,7 +1164,7 @@ public class Geary.Imap.ClientSession : BaseObject {
private void schedule_keepalive() {
// if old one was scheduled, unschedule and schedule anew
unschedule_keepalive();
-
+
uint seconds;
switch (get_protocol_state(null)) {
case ProtocolState.UNCONNECTED:
@@ -1186,28 +1186,28 @@ public class Geary.Imap.ClientSession : BaseObject {
seconds = unselected_keepalive_secs;
break;
}
-
+
// Possible to not have keepalives in one state but in another, or for neither
//
// Yes, we allow keepalive to be set to 1 second. It's their dime.
if (seconds > 0)
keepalive_id = Timeout.add_seconds(seconds, on_keepalive);
}
-
+
private bool on_keepalive() {
// by returning false, this will not automatically be called again, so the SourceFunc
// is now dead
keepalive_id = 0;
-
+
send_command_async.begin(new NoopCommand(), null, on_keepalive_completed);
Logging.debug(Logging.Flag.PERIODIC, "[%s] Sending keepalive...", to_string());
-
+
// No need to reschedule keepalive, as the notification that the command was sent should
// do that automatically
-
+
return false;
}
-
+
private void on_keepalive_completed(Object? source, AsyncResult result) {
try {
StatusResponse response = send_command_async.end(result);
@@ -1229,12 +1229,12 @@ public class Geary.Imap.ClientSession : BaseObject {
MachineParams params = new MachineParams(cmd);
fsm.issue(Event.SEND_CMD, null, params);
-
+
if (params.err != null)
throw params.err;
-
+
assert(params.proceed);
-
+
return yield command_transaction_async(cmd, cancellable);
}
@@ -1247,14 +1247,14 @@ public class Geary.Imap.ClientSession : BaseObject {
foreach (Command cmd in cmds)
check_unsupported_send_command(cmd);
-
+
// only issue one event to the state machine for all commands; either all succeed or all fail
MachineParams params = new MachineParams(Geary.Collection.get_first(cmds));
fsm.issue(Event.SEND_CMD, null, params);
-
+
if (params.err != null)
throw params.err;
-
+
assert(params.proceed);
// Issue all at once using a single Nonblocking.Batch unless
@@ -1302,44 +1302,44 @@ public class Geary.Imap.ClientSession : BaseObject {
throw new ImapError.NOT_SUPPORTED("Use direct calls rather than commands for %s", cmd.name);
}
}
-
+
private uint on_send_command(uint state, uint event, void *user, Object? object) {
MachineParams params = (MachineParams) object;
-
+
params.proceed = true;
-
+
return state;
}
-
+
private uint on_recv_status(uint state, uint event, void *user, Object? object) {
StatusResponse status_response = (StatusResponse) object;
-
+
switch (status_response.status) {
case Status.OK:
// some good-feeling text that doesn't need to be handled when in this state
break;
-
+
case Status.BYE:
debug("[%s] Received BYE from server: %s", to_string(), status_response.to_string());
-
+
// nothing more we can do; drop connection and report disconnect to user
cx.disconnect_async.begin(null, on_bye_disconnect_completed);
-
+
state = State.BROKEN;
break;
-
+
default:
debug("[%s] Received error from server: %s", to_string(), status_response.to_string());
break;
}
-
+
return state;
}
-
+
private void on_bye_disconnect_completed(Object? source, AsyncResult result) {
dispatch_disconnect_results(DisconnectReason.REMOTE_CLOSE, result);
}
-
+
//
// select/examine
//
@@ -1366,15 +1366,15 @@ public class Geary.Imap.ClientSession : BaseObject {
cmd = new SelectCommand(mailbox);
else
cmd = new ExamineCommand(mailbox);
-
+
MachineParams params = new MachineParams(cmd);
fsm.issue(Event.SELECT, null, params);
-
+
if (params.err != null)
throw params.err;
-
+
assert(params.proceed);
-
+
return yield command_transaction_async(cmd, cancellable);
}
@@ -1389,19 +1389,19 @@ public class Geary.Imap.ClientSession : BaseObject {
private uint on_not_selected(uint state, uint event, void *user, Object? object) {
MachineParams params = (MachineParams) object;
-
+
params.err = new ImapError.INVALID("Can't close mailbox, not selected");
-
+
return state;
}
-
+
private uint on_selecting_recv_completion(uint state, uint event, void *user, Object? object) {
StatusResponse completion_response = (StatusResponse) object;
-
+
Command? cmd;
if (!validate_state_change_cmd(completion_response, out cmd))
return state;
-
+
// get the mailbox from the command
MailboxSpecifier? mailbox = null;
if (cmd is SelectCommand) {
@@ -1411,21 +1411,21 @@ public class Geary.Imap.ClientSession : BaseObject {
mailbox = ((ExamineCommand) cmd).mailbox;
current_mailbox_readonly = true;
}
-
+
// should only get to this point if cmd was SELECT or EXAMINE
assert(mailbox != null);
-
+
switch (completion_response.status) {
case Status.OK:
// mailbox is SELECTED/EXAMINED, report change after completion of transition
MailboxSpecifier? old_mailbox = current_mailbox;
current_mailbox = mailbox;
-
+
if (old_mailbox != current_mailbox)
fsm.do_post_transition(notify_select_completed, null, old_mailbox);
-
+
return State.SELECTED;
-
+
default:
debug("[%s]: Unable to SELECT/EXAMINE: %s", to_string(), completion_response.to_string());
@@ -1435,11 +1435,11 @@ public class Geary.Imap.ClientSession : BaseObject {
return State.AUTHORIZED;
}
}
-
+
private void notify_select_completed(void *user, Object? object) {
current_mailbox_changed((MailboxSpecifier) object, current_mailbox, current_mailbox_readonly);
}
-
+
//
// close mailbox
//
@@ -1450,16 +1450,16 @@ public class Geary.Imap.ClientSession : BaseObject {
MachineParams params = new MachineParams(cmd);
fsm.issue(Event.CLOSE_MAILBOX, null, params);
-
+
if (params.err != null)
throw params.err;
-
+
return yield command_transaction_async(cmd, cancellable);
}
-
+
private uint on_close_mailbox(uint state, uint event, void *user, Object? object) {
MachineParams params = (MachineParams) object;
-
+
assert(params.cmd is CloseCommand);
if (!reserve_state_change_cmd(params, state, event))
return state;
@@ -1469,34 +1469,34 @@ public class Geary.Imap.ClientSession : BaseObject {
return State.CLOSING_MAILBOX;
}
-
+
private uint on_closing_recv_completion(uint state, uint event, void *user, Object? object) {
StatusResponse completion_response = (StatusResponse) object;
-
+
if (!validate_state_change_cmd(completion_response))
return state;
-
+
switch (completion_response.status) {
case Status.OK:
MailboxSpecifier? old_mailbox = current_mailbox;
current_mailbox = null;
-
+
if (old_mailbox != null)
fsm.do_post_transition(notify_mailbox_closed, null, old_mailbox);
-
+
return State.AUTHORIZED;
-
+
default:
debug("[%s] Unable to CLOSE: %s", to_string(), completion_response.to_string());
-
+
return State.SELECTED;
}
}
-
+
private void notify_mailbox_closed(void *user, Object? object) {
current_mailbox_changed((MailboxSpecifier) object, null, false);
}
-
+
//
// logout
//
@@ -1507,14 +1507,14 @@ public class Geary.Imap.ClientSession : BaseObject {
MachineParams params = new MachineParams(cmd);
fsm.issue(Event.LOGOUT, null, params);
-
+
if (params.err != null)
throw params.err;
-
+
if(params.proceed)
yield command_transaction_async(cmd, cancellable);
}
-
+
private uint on_logout(uint state, uint event, void *user, Object? object) {
MachineParams params = (MachineParams) object;
@@ -1527,18 +1527,18 @@ public class Geary.Imap.ClientSession : BaseObject {
return State.LOGGING_OUT;
}
-
+
private uint on_logging_out_recv_completion(uint state, uint event, void *user, Object? object) {
StatusResponse completion_response = (StatusResponse) object;
-
+
if (!validate_state_change_cmd(completion_response))
return state;
-
+
fsm.do_post_transition(() => { logged_out(); });
-
+
return State.LOGGED_OUT;
}
-
+
//
// disconnect
//
@@ -1547,163 +1547,163 @@ public class Geary.Imap.ClientSession : BaseObject {
throws GLib.Error {
MachineParams params = new MachineParams(null);
fsm.issue(Event.DISCONNECT, null, params);
-
+
if (params.err != null)
throw params.err;
-
+
if (!params.proceed)
return;
-
+
Error? disconnect_err = null;
try {
yield cx.disconnect_async(cancellable);
} catch (Error err) {
disconnect_err = err;
}
-
+
drop_connection();
disconnected(DisconnectReason.LOCAL_CLOSE);
-
+
if (disconnect_err != null)
throw disconnect_err;
}
-
+
private uint on_disconnect(uint state, uint event, void *user, Object? object) {
MachineParams params = (MachineParams) object;
-
+
params.proceed = true;
-
+
return State.BROKEN;
}
-
+
//
// error handling
//
-
+
// use different error handler when connecting because, if connect_async() fails, there's no
// requirement for the user to call disconnect_async() and clean up... this prevents leaving the
// FSM in the CONNECTING state, causing an assertion when this object is destroyed
private uint on_connecting_send_recv_error(uint state, uint event, void *user, Object? object, Error?
err) {
assert(err != null);
-
+
debug("[%s] Connecting send/recv error, dropping client connection: %s", to_string(), err.message);
-
+
fsm.do_post_transition(() => { drop_connection(); });
-
+
return State.BROKEN;
}
-
+
private uint on_send_error(uint state, uint event, void *user, Object? object, Error? err) {
assert(err != null);
-
+
if (err is IOError.CANCELLED)
return state;
-
+
debug("[%s] Send error, disconnecting: %s", to_string(), err.message);
-
+
cx.disconnect_async.begin(null, on_fire_send_error_signal);
-
+
return State.BROKEN;
}
-
+
private void on_fire_send_error_signal(Object? object, AsyncResult result) {
dispatch_disconnect_results(DisconnectReason.LOCAL_ERROR, result);
}
-
+
private uint on_recv_error(uint state, uint event, void *user, Object? object, Error? err) {
assert(err != null);
debug("[%s] Receive error, disconnecting: %s", to_string(), err.message);
-
+
cx.disconnect_async.begin(null, on_fire_recv_error_signal);
-
+
return State.BROKEN;
}
-
+
private void on_fire_recv_error_signal(Object? object, AsyncResult result) {
dispatch_disconnect_results(DisconnectReason.REMOTE_ERROR, result);
}
-
+
private void dispatch_disconnect_results(DisconnectReason reason, AsyncResult result) {
debug("[%s] Disconnected due to %s", to_string(), reason.to_string());
-
+
try {
cx.disconnect_async.end(result);
} catch (Error err) {
debug("[%s] Send/recv disconnect failed: %s", to_string(), err.message);
}
-
+
drop_connection();
-
+
disconnected(reason);
}
-
+
// This handles the situation where the user submits a command before the connection has been
// established
private uint on_early_command(uint state, uint event, void *user, Object? object) {
assert(object != null);
-
+
MachineParams params = (MachineParams) object;
params.err = new ImapError.NOT_CONNECTED("Command %s too early: not connected to %s",
params.cmd.name, to_string());
-
+
return state;
}
-
+
// This handles the situation where the user submits a command after the connection has been
// logged out, terminated, or errored-out
private uint on_late_command(uint state, uint event, void *user, Object? object) {
assert(object != null);
-
+
MachineParams params = (MachineParams) object;
params.err = new ImapError.NOT_CONNECTED("Connection to %s closing or closed", to_string());
-
+
return state;
}
-
+
private uint on_already_connected(uint state, uint event, void *user, Object? object) {
assert(object != null);
-
+
MachineParams params = (MachineParams) object;
params.err = new ImapError.ALREADY_CONNECTED("Already connected or connecting to %s", to_string());
-
+
return state;
}
-
+
private uint on_already_logged_in(uint state, uint event, void *user, Object? object) {
assert(object != null);
-
+
MachineParams params = (MachineParams) object;
params.err = new ImapError.ALREADY_CONNECTED("Already logged in to %s", to_string());
-
+
return state;
}
-
+
// This handles the situation where an unanticipated (or uninteresting) ServerResponse was received
private uint on_dropped_response(uint state, uint event, void *user, Object? object) {
ServerResponse server_response = (ServerResponse) object;
-
+
debug("[%s] Dropped server response at %s: %s", to_string(), fsm.get_event_issued_string(state,
event),
server_response.to_string());
-
+
return state;
}
-
+
// This handles commands that the user initiates before the session is in the AUTHENTICATED state
private uint on_unauthenticated(uint state, uint event, void *user, Object? object) {
assert(object != null);
-
+
MachineParams params = (MachineParams) object;
params.err = new ImapError.UNAUTHENTICATED("Not authenticated with %s", to_string());
-
+
return state;
}
-
+
private uint on_ignored_transition(uint state, uint event) {
debug("[%s] Ignored transition: %s", to_string(), fsm.get_event_issued_string(state, event));
-
+
return state;
}
-
+
//
// command submission
//
@@ -1721,19 +1721,19 @@ public class Geary.Imap.ClientSession : BaseObject {
//
// network connection event handlers
//
-
+
private void on_network_connected() {
debug("[%s] Connected to %s", to_string(), imap_endpoint.to_string());
-
+
fsm.issue(Event.CONNECTED);
}
-
+
private void on_network_disconnected() {
debug("[%s] Disconnected from %s", to_string(), imap_endpoint.to_string());
-
+
fsm.issue(Event.DISCONNECTED);
}
-
+
private void on_network_sent_command(Command cmd) {
#if VERBOSE_SESSION
debug("[%s] Sent command %s", to_string(), cmd.to_string());
@@ -1741,10 +1741,10 @@ public class Geary.Imap.ClientSession : BaseObject {
// resechedule keepalive
schedule_keepalive();
}
-
+
private void on_network_send_error(Error err) {
debug("[%s] Send error: %s", to_string(), err.message);
-
+
fsm.issue(Event.SEND_ERROR, null, null, err);
}
@@ -1800,39 +1800,39 @@ public class Geary.Imap.ClientSession : BaseObject {
capabilities = server_data.get_capabilities(ref next_capabilities_revision);
debug("[%s] %s %s", to_string(), server_data.server_data_type.to_string(),
capabilities.to_string());
-
+
capability(capabilities);
break;
-
+
case ServerDataType.EXISTS:
exists(server_data.get_exists());
break;
-
+
case ServerDataType.EXPUNGE:
expunge(server_data.get_expunge());
break;
-
+
case ServerDataType.FETCH:
fetch(server_data.get_fetch());
break;
-
+
case ServerDataType.FLAGS:
flags(server_data.get_flags());
break;
-
+
case ServerDataType.LIST:
case ServerDataType.XLIST:
list(server_data.get_list());
break;
-
+
case ServerDataType.RECENT:
recent(server_data.get_recent());
break;
-
+
case ServerDataType.STATUS:
status(server_data.get_status());
break;
-
+
case ServerDataType.SEARCH:
search(server_data.get_search());
break;
@@ -1849,7 +1849,7 @@ public class Geary.Imap.ClientSession : BaseObject {
server_data.to_string());
break;
}
-
+
server_data_received(server_data);
}
@@ -1885,7 +1885,7 @@ public class Geary.Imap.ClientSession : BaseObject {
private void on_received_bad_response(RootParameters root, ImapError err) {
debug("[%s] Received bad response %s: %s", to_string(), root.to_string(), err.message);
}
-
+
private void on_received_closed(ClientConnection cx) {
#if VERBOSE_SESSION
// This currently doesn't generate any Events, but it does mean the connection has closed
@@ -1893,13 +1893,13 @@ public class Geary.Imap.ClientSession : BaseObject {
debug("[%s] Received closed", to_string());
#endif
}
-
+
private void on_network_receive_failure(Error err) {
debug("[%s] Receive failed: %s", to_string(), err.message);
-
+
fsm.issue(Event.RECV_ERROR, null, null, err);
}
-
+
public string to_string() {
if (cx == null) {
return "%s %s".printf(imap_endpoint.to_string(), fsm.get_state_string(fsm.get_state()));
diff --git a/src/engine/imap/transport/imap-deserializer.vala
b/src/engine/imap/transport/imap-deserializer.vala
index 13c7ecbe..8ca8ec25 100644
--- a/src/engine/imap/transport/imap-deserializer.vala
+++ b/src/engine/imap/transport/imap-deserializer.vala
@@ -12,7 +12,7 @@
* The Deserializer will only begin reading from the stream when {@link start_async} is called.
* Calling {@link stop_async} will halt reading without closing the stream itself. A Deserializer
* may not be reused once stop_async has been invoked.
- *
+ *
* Since all results from the Deserializer are reported via signals, those signals should be
* connected to prior to calling start_async, or the caller risks missing early messages. (Note
* that since Deserializer uses async I/O, this isn't technically possible unless the signals are
@@ -25,14 +25,14 @@
public class Geary.Imap.Deserializer : BaseObject {
private const size_t MAX_BLOCK_READ_SIZE = 4096;
-
+
private enum Mode {
LINE,
BLOCK,
FAILED,
CLOSED
}
-
+
private enum State {
TAG,
START_PARAM,
@@ -49,11 +49,11 @@ public class Geary.Imap.Deserializer : BaseObject {
CLOSED,
COUNT
}
-
+
private static string state_to_string(uint state) {
return ((State) state).to_string();
}
-
+
private enum Event {
CHAR,
EOL,
@@ -62,15 +62,15 @@ public class Geary.Imap.Deserializer : BaseObject {
ERROR,
COUNT
}
-
+
private static string event_to_string(uint event) {
return ((Event) event).to_string();
}
-
+
private static Geary.State.MachineDescriptor machine_desc = new Geary.State.MachineDescriptor(
"Geary.Imap.Deserializer", State.TAG, State.COUNT, Event.COUNT,
state_to_string, event_to_string);
-
+
private string identifier;
private DataInputStream dins;
private Geary.State.Machine fsm;
@@ -152,47 +152,47 @@ public class Geary.Imap.Deserializer : BaseObject {
new Geary.State.Mapping(State.TAG, Event.CHAR, on_tag_char),
new Geary.State.Mapping(State.TAG, Event.EOS, on_eos),
new Geary.State.Mapping(State.TAG, Event.ERROR, on_error),
-
+
new Geary.State.Mapping(State.START_PARAM, Event.CHAR, on_first_param_char),
new Geary.State.Mapping(State.START_PARAM, Event.EOL, on_eol),
new Geary.State.Mapping(State.START_PARAM, Event.EOS, on_eos),
new Geary.State.Mapping(State.START_PARAM, Event.ERROR, on_error),
-
+
new Geary.State.Mapping(State.ATOM, Event.CHAR, on_atom_char),
new Geary.State.Mapping(State.ATOM, Event.EOL, on_atom_eol),
new Geary.State.Mapping(State.ATOM, Event.EOS, on_eos),
new Geary.State.Mapping(State.ATOM, Event.ERROR, on_error),
-
+
new Geary.State.Mapping(State.FLAG, Event.CHAR, on_first_flag_char),
new Geary.State.Mapping(State.FLAG, Event.EOL, on_atom_eol),
new Geary.State.Mapping(State.FLAG, Event.EOS, on_eos),
new Geary.State.Mapping(State.FLAG, Event.ERROR, on_error),
-
+
new Geary.State.Mapping(State.QUOTED, Event.CHAR, on_quoted_char),
new Geary.State.Mapping(State.QUOTED, Event.EOS, on_eos),
new Geary.State.Mapping(State.QUOTED, Event.ERROR, on_error),
-
+
new Geary.State.Mapping(State.QUOTED_ESCAPE, Event.CHAR, on_quoted_escape_char),
new Geary.State.Mapping(State.QUOTED_ESCAPE, Event.EOS, on_eos),
new Geary.State.Mapping(State.QUOTED_ESCAPE, Event.ERROR, on_error),
-
+
new Geary.State.Mapping(State.PARTIAL_BODY_ATOM, Event.CHAR, on_partial_body_atom_char),
new Geary.State.Mapping(State.PARTIAL_BODY_ATOM, Event.EOS, on_eos),
new Geary.State.Mapping(State.PARTIAL_BODY_ATOM, Event.ERROR, on_error),
-
+
new Geary.State.Mapping(State.PARTIAL_BODY_ATOM_TERMINATING, Event.CHAR,
on_partial_body_atom_terminating_char),
new Geary.State.Mapping(State.PARTIAL_BODY_ATOM_TERMINATING, Event.EOS, on_eos),
new Geary.State.Mapping(State.PARTIAL_BODY_ATOM_TERMINATING, Event.ERROR, on_error),
-
+
new Geary.State.Mapping(State.LITERAL, Event.CHAR, on_literal_char),
new Geary.State.Mapping(State.LITERAL, Event.EOS, on_eos),
new Geary.State.Mapping(State.LITERAL, Event.ERROR, on_error),
-
+
new Geary.State.Mapping(State.LITERAL_DATA_BEGIN, Event.EOL, on_literal_data_begin_eol),
new Geary.State.Mapping(State.LITERAL_DATA_BEGIN, Event.EOS, on_eos),
new Geary.State.Mapping(State.LITERAL_DATA_BEGIN, Event.ERROR, on_error),
-
+
new Geary.State.Mapping(State.LITERAL_DATA, Event.DATA, on_literal_data),
new Geary.State.Mapping(State.LITERAL_DATA, Event.EOS, on_eos),
new Geary.State.Mapping(State.LITERAL_DATA, Event.ERROR, on_error),
@@ -218,7 +218,7 @@ public class Geary.Imap.Deserializer : BaseObject {
public bool install_converter(Converter converter) {
return midstream.install(converter);
}
-
+
/**
* Begin deserializing IMAP responses from the input stream.
*
@@ -227,29 +227,29 @@ public class Geary.Imap.Deserializer : BaseObject {
public async void start_async(int priority = GLib.Priority.DEFAULT_IDLE) throws Error {
if (cancellable != null)
throw new EngineError.ALREADY_OPEN("Deserializer already open");
-
+
Mode mode = get_mode();
-
+
if (mode == Mode.FAILED)
throw new EngineError.ALREADY_CLOSED("Deserializer failed");
-
+
if ((mode == Mode.CLOSED) || (cancellable != null && cancellable.is_cancelled()))
throw new EngineError.ALREADY_CLOSED("Deserializer closed");
-
+
cancellable = new Cancellable();
ins_priority = priority;
-
+
next_deserialize_step();
}
-
+
public async void stop_async() throws Error {
// quietly fail when not opened or already closed
if (cancellable == null || cancellable.is_cancelled() || is_halted())
return;
-
+
// cancel any outstanding I/O
cancellable.cancel();
-
+
// wait for outstanding I/O to exit
yield closed_semaphore.wait_async();
debug("[%s] Deserializer closed", to_string());
@@ -281,40 +281,40 @@ public class Geary.Imap.Deserializer : BaseObject {
case Mode.LINE:
dins.read_line_async.begin(ins_priority, cancellable, on_read_line);
break;
-
+
case Mode.BLOCK:
// Can't merely skip zero-byte literal, need to go through async transaction to
// properly send events to the FSM
assert(literal_length_remaining >= 0);
-
+
if (block_buffer == null)
block_buffer = new Geary.Memory.GrowableBuffer();
-
+
current_buffer = block_buffer.allocate(
size_t.min(MAX_BLOCK_READ_SIZE, literal_length_remaining));
-
+
dins.read_async.begin(current_buffer, ins_priority, cancellable, on_read_block);
break;
-
+
case Mode.FAILED:
case Mode.CLOSED:
// do nothing; Deserializer is effectively closed
break;
-
+
default:
assert_not_reached();
}
}
-
+
private void on_read_line(Object? source, AsyncResult result) {
try {
size_t bytes_read;
string? line = dins.read_line_async.end(result, out bytes_read);
if (line == null) {
Logging.debug(Logging.Flag.DESERIALIZER, "[%s] line EOS", to_string());
-
+
push_eos();
-
+
return;
}
@@ -325,10 +325,10 @@ public class Geary.Imap.Deserializer : BaseObject {
push_error(err);
return;
}
-
+
next_deserialize_step();
}
-
+
private void on_read_block(Object? source, AsyncResult result) {
try {
// Zero-byte literals are legal (see note in next_deserialize_step()), so EOS only
@@ -336,12 +336,12 @@ public class Geary.Imap.Deserializer : BaseObject {
size_t bytes_read = dins.read_async.end(result);
if (bytes_read == 0 && literal_length_remaining > 0) {
Logging.debug(Logging.Flag.DESERIALIZER, "[%s] block EOS", to_string());
-
+
push_eos();
-
+
return;
}
-
+
Logging.debug(Logging.Flag.DESERIALIZER, "[%s] block %lub", to_string(), bytes_read);
bytes_received(bytes_read);
@@ -351,10 +351,10 @@ public class Geary.Imap.Deserializer : BaseObject {
push_data(bytes_read);
} catch (Error err) {
push_error(err);
-
+
return;
}
-
+
next_deserialize_step();
}
@@ -392,23 +392,23 @@ public class Geary.Imap.Deserializer : BaseObject {
private void push_eos() {
fsm.issue(Event.EOS);
}
-
+
// Push an Error event
private void push_error(Error err) {
fsm.issue(Event.ERROR, null, null, err);
}
-
+
private Mode get_mode() {
switch (fsm.get_state()) {
case State.LITERAL_DATA:
return Mode.BLOCK;
-
+
case State.FAILED:
return Mode.FAILED;
-
+
case State.CLOSED:
return Mode.CLOSED;
-
+
default:
return Mode.LINE;
}
@@ -417,37 +417,37 @@ public class Geary.Imap.Deserializer : BaseObject {
private bool is_current_string_empty() {
return (current_string == null) || String.is_empty(current_string.str);
}
-
+
// Case-insensitive compare
private bool is_current_string_ci(string cmp) {
if (current_string == null || String.is_empty(current_string.str))
return false;
-
+
return Ascii.stri_equal(current_string.str, cmp);
}
-
+
private void append_to_string(char ch) {
if (current_string == null)
current_string = new StringBuilder();
-
+
current_string.append_c(ch);
}
-
+
private void save_string_parameter(bool quoted) {
// deal with empty strings
if (!quoted && is_current_string_empty())
return;
-
+
// deal with empty quoted strings
string str = (quoted && current_string == null) ? "" : current_string.str;
-
+
if (quoted)
save_parameter(new QuotedStringParameter(str));
else if (NumberParameter.is_ascii_numeric(str, null))
save_parameter(new NumberParameter.from_ascii(str));
else
save_parameter(new UnquotedStringParameter(str));
-
+
current_string = null;
}
@@ -455,7 +455,7 @@ public class Geary.Imap.Deserializer : BaseObject {
save_parameter(new LiteralParameter(block_buffer));
block_buffer = null;
}
-
+
private void save_parameter(Parameter param) {
context.add(param);
}
@@ -578,24 +578,24 @@ public class Geary.Imap.Deserializer : BaseObject {
return State.FAILED;
}
}
-
+
private uint on_tag_char(uint state, uint event, void *user) {
char ch = *((char *) user);
-
+
// drop if not allowed for tags (allowing for continuations and watching for spaces, which
// indicate a change of state)
if (DataFormat.is_tag_special(ch, " +"))
return State.TAG;
-
+
// space indicates end of tag
if (ch == ' ') {
save_string_parameter(false);
-
+
return State.START_PARAM;
}
-
+
append_to_string(ch);
-
+
return State.TAG;
}
@@ -688,30 +688,30 @@ public class Geary.Imap.Deserializer : BaseObject {
private uint on_quoted_char(uint state, uint event, void *user) {
char ch = *((char *) user);
-
+
// drop anything above 0x7F, NUL, CR, and LF
if (ch > 0x7F || ch == '\0' || ch == '\r' || ch == '\n')
return State.QUOTED;
-
+
// look for escaped characters
if (ch == '\\')
return State.QUOTED_ESCAPE;
-
+
// DQUOTE ends quoted string and return to parsing atoms
if (ch == '\"') {
save_string_parameter(true);
-
+
return State.START_PARAM;
}
-
+
append_to_string(ch);
-
+
return State.QUOTED;
}
-
+
private uint on_quoted_escape_char(uint state, uint event, void *user) {
char ch = *((char *) user);
-
+
// only two accepted escaped characters: double-quote and backslash
// everything else dropped on the floor
switch (ch) {
@@ -720,19 +720,19 @@ public class Geary.Imap.Deserializer : BaseObject {
append_to_string(ch);
break;
}
-
+
return State.QUOTED;
}
-
+
private uint on_partial_body_atom_char(uint state, uint event, void *user) {
char ch = *((char *) user);
-
+
// decoding the partial body parameter ("BODY[section]" et al.) is simply to locate the
// terminating space after the closing square bracket or closing angle bracket
// TODO: stricter testing of atom special characters and such (much like on_tag_or_atom_char)
// but keeping in mind the looser rules and such with this variation
append_to_string(ch);
-
+
// Can't terminate the atom with a close square bracket because the partial span
// ("<...>") might be next
//
@@ -743,27 +743,27 @@ public class Geary.Imap.Deserializer : BaseObject {
case ']':
case '>':
return State.PARTIAL_BODY_ATOM_TERMINATING;
-
+
default:
return state;
}
}
-
+
private uint on_partial_body_atom_terminating_char(uint state, uint event, void *user) {
char ch = *((char *) user);
-
+
// anything but a space indicates the atom is continuing, therefore return to prior state
if (ch != ' ')
return on_partial_body_atom_char(State.PARTIAL_BODY_ATOM, event, user);
-
+
save_string_parameter(false);
-
+
return State.START_PARAM;
}
-
+
private uint on_literal_char(uint state, uint event, void *user) {
char ch = *((char *) user);
-
+
// if close-bracket, end of literal length field -- next event must be EOL
if (ch == '}') {
// empty literal treated as garbage
@@ -779,31 +779,31 @@ public class Geary.Imap.Deserializer : BaseObject {
return State.LITERAL_DATA_BEGIN;
}
-
+
// drop anything non-numeric
if (!ch.isdigit())
return State.LITERAL;
-
+
append_to_string(ch);
-
+
return State.LITERAL;
}
-
+
private uint on_literal_data_begin_eol(uint state, uint event, void *user) {
return State.LITERAL_DATA;
}
-
+
private uint on_literal_data(uint state, uint event, void *user) {
size_t *bytes_read = (size_t *) user;
-
+
assert(*bytes_read <= literal_length_remaining);
literal_length_remaining -= *bytes_read;
-
+
if (literal_length_remaining > 0)
return State.LITERAL_DATA;
-
+
save_literal_parameter();
-
+
return State.START_PARAM;
}
@@ -835,13 +835,13 @@ public class Geary.Imap.Deserializer : BaseObject {
// always signal as closed and notify
closed_semaphore.blind_notify();
eos();
-
+
return State.CLOSED;
}
-
+
private uint on_bad_transition(uint state, uint event, void *user) {
warning("Bad event %s at state %s", event_to_string(event), state_to_string(state));
-
+
return State.FAILED;
}
}
diff --git a/src/engine/memory/memory-buffer.vala b/src/engine/memory/memory-buffer.vala
index eb32dc00..e566a281 100644
--- a/src/engine/memory/memory-buffer.vala
+++ b/src/engine/memory/memory-buffer.vala
@@ -26,12 +26,12 @@ public abstract class Geary.Memory.Buffer : BaseObject {
* Returns the number of valid (usable) bytes in the buffer.
*/
public abstract size_t size { get; }
-
+
/**
* Returns the number of bytes allocated (usable and unusable) for the buffer.
*/
public abstract size_t allocated_size { get; }
-
+
/**
* Returns a Bytes object holding the buffer's contents.
*
@@ -39,7 +39,7 @@ public abstract class Geary.Memory.Buffer : BaseObject {
* the data.
*/
public abstract Bytes get_bytes();
-
+
/**
* Returns an InputStream that can read the buffer in its current entirety.
*
@@ -53,7 +53,7 @@ public abstract class Geary.Memory.Buffer : BaseObject {
public virtual InputStream get_input_stream() {
return new MemoryInputStream.from_bytes(get_bytes());
}
-
+
/**
* Returns a ByteArray storing the buffer in its entirety.
*
@@ -65,10 +65,10 @@ public abstract class Geary.Memory.Buffer : BaseObject {
public virtual ByteArray get_byte_array() {
ByteArray byte_array = new ByteArray();
byte_array.append(get_bytes().get_data());
-
+
return byte_array;
}
-
+
/**
* Returns an array of uint8 storing the buffer in its entirety.
*
@@ -82,7 +82,7 @@ public abstract class Geary.Memory.Buffer : BaseObject {
public virtual uint8[] get_uint8_array() {
return get_bytes().get_data();
}
-
+
/**
* Returns a copy of the contents of the buffer as though it was a null terminated string.
*
@@ -96,10 +96,10 @@ public abstract class Geary.Memory.Buffer : BaseObject {
public virtual string to_string() {
uint8[] buffer = get_uint8_array();
buffer += (uint8) '\0';
-
+
return (string) buffer;
}
-
+
/**
* Returns a copy of the contents of the buffer as though it was a UTF-8 string.
*
@@ -112,7 +112,7 @@ public abstract class Geary.Memory.Buffer : BaseObject {
*/
public virtual string get_valid_utf8() {
string str = to_string();
-
+
return str.validate() ? str : "";
}
}
diff --git a/src/engine/memory/memory-byte-buffer.vala b/src/engine/memory/memory-byte-buffer.vala
index e5c8c43a..28c75c59 100644
--- a/src/engine/memory/memory-byte-buffer.vala
+++ b/src/engine/memory/memory-byte-buffer.vala
@@ -17,7 +17,7 @@ public class Geary.Memory.ByteBuffer : Memory.Buffer, Memory.UnownedBytesBuffer
return bytes.length;
}
}
-
+
/**
* {@inheritDoc}
*/
@@ -26,10 +26,10 @@ public class Geary.Memory.ByteBuffer : Memory.Buffer, Memory.UnownedBytesBuffer
return allocated_bytes;
}
}
-
+
private Bytes bytes;
private size_t allocated_bytes;
-
+
/**
* filled is the number of usable bytes in the supplied buffer, allocated is the total size
* of the buffer.
@@ -41,11 +41,11 @@ public class Geary.Memory.ByteBuffer : Memory.Buffer, Memory.UnownedBytesBuffer
*/
public ByteBuffer(uint8[] data, size_t filled) {
assert(filled <= data.length);
-
+
bytes = new Bytes(data[0:filled]);
allocated_bytes = bytes.length;
}
-
+
/**
* filled is the number of usable bytes in the supplied buffer, allocated is the total size
* of the buffer.
@@ -54,11 +54,11 @@ public class Geary.Memory.ByteBuffer : Memory.Buffer, Memory.UnownedBytesBuffer
*/
public ByteBuffer.take(owned uint8[] data, size_t filled) {
assert(filled <= data.length);
-
+
bytes = new Bytes.take(data[0:filled]);
allocated_bytes = data.length;
}
-
+
/**
* Takes ownership and converts a ByteArray to a {@link ByteBuffer}.
*
@@ -68,7 +68,7 @@ public class Geary.Memory.ByteBuffer : Memory.Buffer, Memory.UnownedBytesBuffer
bytes = ByteArray.free_to_bytes(byte_array);
allocated_bytes = bytes.length;
}
-
+
/**
* Takes ownership and converts a MemoryOutputStream to a {@link ByteBuffer}.
*
@@ -76,18 +76,18 @@ public class Geary.Memory.ByteBuffer : Memory.Buffer, Memory.UnownedBytesBuffer
*/
public ByteBuffer.from_memory_output_stream(MemoryOutputStream mouts) {
assert(mouts.is_closed());
-
+
bytes = mouts.steal_as_bytes();
allocated_bytes = bytes.length;
}
-
+
/**
* {@inheritDoc}
*/
public override Bytes get_bytes() {
return bytes;
}
-
+
/**
* {@inheritDoc}
*/
diff --git a/src/engine/memory/memory-empty-buffer.vala b/src/engine/memory/memory-empty-buffer.vala
index e7bc7cd7..dffccc8a 100644
--- a/src/engine/memory/memory-empty-buffer.vala
+++ b/src/engine/memory/memory-empty-buffer.vala
@@ -18,7 +18,7 @@ public class Geary.Memory.EmptyBuffer : Memory.Buffer, Memory.UnownedStringBuffe
return (_instance != null) ? _instance : _instance = new EmptyBuffer();
}
}
-
+
/**
* {@inheritDoc}
*/
@@ -27,7 +27,7 @@ public class Geary.Memory.EmptyBuffer : Memory.Buffer, Memory.UnownedStringBuffe
return 0;
}
}
-
+
/**
* {@inheritDoc}
*/
@@ -36,34 +36,34 @@ public class Geary.Memory.EmptyBuffer : Memory.Buffer, Memory.UnownedStringBuffe
return 0;
}
}
-
+
private Bytes bytes = new Bytes(new uint8[0]);
private ByteArray byte_array = new ByteArray();
-
+
private EmptyBuffer() {
}
-
+
/**
* {@inheritDoc}
*/
public override Bytes get_bytes() {
return bytes;
}
-
+
/**
* {@inheritDoc}
*/
public unowned uint8[] to_unowned_uint8_array() {
return bytes.get_data();
}
-
+
/**
* {@inheritDoc}
*/
public unowned string to_unowned_string() {
return "";
}
-
+
/**
* {@inheritDoc}
*/
diff --git a/src/engine/memory/memory-file-buffer.vala b/src/engine/memory/memory-file-buffer.vala
index 90aac5f4..45726c18 100644
--- a/src/engine/memory/memory-file-buffer.vala
+++ b/src/engine/memory/memory-file-buffer.vala
@@ -13,38 +13,38 @@
public class Geary.Memory.FileBuffer : Memory.Buffer, Memory.UnownedBytesBuffer {
private File file;
private MappedFile mmap;
-
+
public override size_t size {
get {
return mmap.get_length();
}
}
-
+
public override size_t allocated_size {
get {
return mmap.get_length();
}
}
-
+
/**
* The File is immediately opened when this is called.
*/
public FileBuffer(File file, bool readonly) throws Error {
if (file.get_path() == null)
throw new IOError.NOT_FOUND("File for Geary.Memory.FileBuffer not found");
-
+
this.file = file;
mmap = new MappedFile(file.get_path(), !readonly);
}
-
+
public override Bytes get_bytes() {
return Bytes.new_with_owner(to_unowned_uint8_array(), mmap);
}
-
+
public unowned uint8[] to_unowned_uint8_array() {
unowned uint8[] buffer = (uint8[]) mmap.get_contents();
buffer.length = (int) mmap.get_length();
-
+
return buffer;
}
}
diff --git a/src/engine/memory/memory-growable-buffer.vala b/src/engine/memory/memory-growable-buffer.vala
index 20c680c8..885d34de 100644
--- a/src/engine/memory/memory-growable-buffer.vala
+++ b/src/engine/memory/memory-growable-buffer.vala
@@ -15,77 +15,77 @@
public class Geary.Memory.GrowableBuffer : Memory.Buffer, Memory.UnownedBytesBuffer,
Memory.UnownedStringBuffer {
private static uint8[] NUL_ARRAY = { '\0' };
-
+
private ByteArray? byte_array = new ByteArray();
private Bytes? bytes = null;
-
+
public override size_t size {
// account for trailing NUL, which is always kept in place for UnownedStringBuffer
get {
if (bytes != null)
return bytes.length - 1;
-
+
assert(byte_array != null);
-
+
return byte_array.len - 1;
}
}
-
+
public override size_t allocated_size {
get {
return size;
}
}
-
+
public GrowableBuffer() {
// add NUL for UnownedStringBuffer
byte_array.append(NUL_ARRAY);
}
-
+
private Bytes to_bytes() {
if (bytes != null) {
assert(byte_array == null);
-
+
return bytes;
}
-
+
assert(byte_array != null);
-
+
bytes = ByteArray.free_to_bytes(byte_array);
byte_array = null;
-
+
return bytes;
}
-
+
private unowned uint8[] get_bytes_no_nul() {
assert(bytes != null);
assert(bytes.get_size() > 0);
-
+
return bytes.get_data()[0:bytes.get_size() - 1];
}
-
+
private ByteArray to_byte_array() {
if (byte_array != null) {
assert(bytes == null);
-
+
return byte_array;
}
-
+
assert(bytes != null);
-
+
byte_array = Bytes.unref_to_array(bytes);
bytes = null;
-
+
return byte_array;
}
-
+
private unowned uint8[] get_byte_array_no_nul() {
assert(byte_array != null);
assert(byte_array.len > 0);
-
+
return byte_array.data[0:byte_array.len - 1];
}
-
+
/**
* Appends the data to the existing GrowableBuffer.
*
@@ -95,18 +95,18 @@ public class Geary.Memory.GrowableBuffer : Memory.Buffer, Memory.UnownedBytesBuf
public void append(uint8[] buffer) {
if (buffer.length <= 0)
return;
-
+
to_byte_array();
-
+
// account for existing NUL
assert(byte_array.len > 0);
byte_array.set_size(byte_array.len - 1);
-
+
// append buffer and new NUL for UnownedStringBuffer
byte_array.append(buffer);
byte_array.append(NUL_ARRAY);
}
-
+
/**
* Allocate data within the backing buffer for writing.
*
@@ -120,23 +120,23 @@ public class Geary.Memory.GrowableBuffer : Memory.Buffer, Memory.UnownedBytesBuf
*/
public unowned uint8[] allocate(size_t requested_bytes) {
to_byte_array();
-
+
// existing NUL must be there already
assert(byte_array.len > 0);
-
+
uint original_bytes = byte_array.len;
uint new_size = original_bytes + (uint) requested_bytes;
-
+
byte_array.set_size(new_size);
byte_array.data[new_size - 1] = String.EOS;
-
+
// only return portion request, not including new NUL, but overwriting existing NUL
unowned uint8[] buffer = byte_array.data[(original_bytes - 1):(new_size - 1)];
assert(buffer.length == requested_bytes);
-
+
return buffer;
}
-
+
/**
* Trim a previously allocated buffer.
*
@@ -157,29 +157,29 @@ public class Geary.Memory.GrowableBuffer : Memory.Buffer, Memory.UnownedBytesBuf
// ByteArray
assert(byte_array != null);
assert(filled_bytes <= allocation.length);
-
+
// don't need to worry about the NUL byte here (unless caller overran buffer, then we
// have bigger problems)
byte_array.set_size(byte_array.len - (uint) (allocation.length - filled_bytes));
}
-
+
/**
* {@inheritDoc}
*/
public override Bytes get_bytes() {
to_bytes();
assert(bytes.get_size() > 0);
-
+
// don't return trailing nul
return new Bytes.from_bytes(bytes, 0, bytes.get_size() - 1);
}
-
+
/**
* {@inheritDoc}
*/
public override ByteArray get_byte_array() {
ByteArray copy = new ByteArray();
-
+
// don't copy trailing NUL
if (bytes != null) {
copy.append(get_bytes_no_nul());
@@ -187,10 +187,10 @@ public class Geary.Memory.GrowableBuffer : Memory.Buffer, Memory.UnownedBytesBuf
assert(byte_array != null);
copy.append(get_byte_array_no_nul());
}
-
+
return copy;
}
-
+
/**
* {@inheritDoc}
*/
@@ -198,7 +198,7 @@ public class Geary.Memory.GrowableBuffer : Memory.Buffer, Memory.UnownedBytesBuf
// because returned array is not unowned, Vala will make a copy
return to_unowned_uint8_array();
}
-
+
/**
* {@inheritDoc}
*/
@@ -206,12 +206,12 @@ public class Geary.Memory.GrowableBuffer : Memory.Buffer, Memory.UnownedBytesBuf
// in any case, don't return trailing NUL
if (bytes != null)
return get_bytes_no_nul();
-
+
assert(byte_array != null);
-
+
return get_byte_array_no_nul();
}
-
+
/**
* {@inheritDoc}
*/
@@ -219,7 +219,7 @@ public class Geary.Memory.GrowableBuffer : Memory.Buffer, Memory.UnownedBytesBuf
// because returned string is not unowned, Vala will make a copy
return to_unowned_string();
}
-
+
/**
* {@inheritDoc}
*/
@@ -228,9 +228,9 @@ public class Geary.Memory.GrowableBuffer : Memory.Buffer, Memory.UnownedBytesBuf
// string without copy-and-append
if (bytes != null)
return (string) bytes.get_data();
-
+
assert(byte_array != null);
-
+
return (string) byte_array.data;
}
}
diff --git a/src/engine/memory/memory-offset-buffer.vala b/src/engine/memory/memory-offset-buffer.vala
index 9f11b409..a07225c6 100644
--- a/src/engine/memory/memory-offset-buffer.vala
+++ b/src/engine/memory/memory-offset-buffer.vala
@@ -13,22 +13,22 @@ public class Geary.Memory.OffsetBuffer : Geary.Memory.Buffer, Geary.Memory.Unown
* {@inheritDoc}
*/
public override size_t size { get { return buffer.size - offset; } }
-
+
/**
* {@inheritDoc}
*/
public override size_t allocated_size { get { return size; } }
-
+
private Geary.Memory.Buffer buffer;
private size_t offset;
private Bytes? bytes = null;
-
+
public OffsetBuffer(Geary.Memory.Buffer buffer, size_t offset) {
assert(offset < buffer.size);
this.buffer = buffer;
this.offset = offset;
}
-
+
/**
* {@inheritDoc}
*/
@@ -37,7 +37,7 @@ public class Geary.Memory.OffsetBuffer : Geary.Memory.Buffer, Geary.Memory.Unown
bytes = new Bytes.from_bytes(buffer.get_bytes(), offset, buffer.size - offset);
return bytes;
}
-
+
/**
* {@inheritDoc}
*/
diff --git a/src/engine/memory/memory-string-buffer.vala b/src/engine/memory/memory-string-buffer.vala
index 7f2a4636..a8cc498b 100644
--- a/src/engine/memory/memory-string-buffer.vala
+++ b/src/engine/memory/memory-string-buffer.vala
@@ -15,50 +15,50 @@ public class Geary.Memory.StringBuffer : Memory.Buffer, Memory.UnownedStringBuff
return length;
}
}
-
+
public override size_t allocated_size {
get {
return length;
}
}
-
+
private string str;
private size_t length;
private Bytes? bytes = null;
-
+
public StringBuffer(string str) {
this.str = str;
length = str.data.length;
}
-
+
/**
* {@inheritDoc}
*/
public override Bytes get_bytes() {
return (bytes != null) ? bytes : bytes = new Bytes(str.data);
}
-
+
/**
* {@inheritDoc}
*/
public override string to_string() {
return str;
}
-
+
/**
* {@inheritDoc}
*/
public override string get_valid_utf8() {
return str.validate() ? str : "";
}
-
+
/**
* {@inheritDoc}
*/
public unowned string to_unowned_string() {
return str;
}
-
+
/**
* {@inheritDoc}
*/
diff --git a/src/engine/memory/memory-unowned-string-buffer.vala
b/src/engine/memory/memory-unowned-string-buffer.vala
index 95f208ad..c3721ca8 100644
--- a/src/engine/memory/memory-unowned-string-buffer.vala
+++ b/src/engine/memory/memory-unowned-string-buffer.vala
@@ -21,13 +21,13 @@ public interface Geary.Memory.UnownedStringBuffer : Memory.Buffer {
* The returned string should not be modified or freed.
*/
public abstract unowned string to_unowned_string();
-
+
/**
* An unowned version of {@link Memory.Buffer.get_valid_utf8}.
*/
public virtual unowned string get_unowned_valid_utf8() {
string str = to_unowned_string();
-
+
return str.validate() ? str : "";
}
}
diff --git a/src/engine/mime/mime-content-disposition.vala b/src/engine/mime/mime-content-disposition.vala
index d8a54f01..9b34849f 100644
--- a/src/engine/mime/mime-content-disposition.vala
+++ b/src/engine/mime/mime-content-disposition.vala
@@ -17,50 +17,50 @@ public class Geary.Mime.ContentDisposition : Geary.BaseObject {
* See [[https://tools.ietf.org/html/rfc2183#section-2.3]]
*/
public const string FILENAME = "filename";
-
+
/**
* Creation-Date parameter name.
*
* See [[https://tools.ietf.org/html/rfc2183#section-2.4]]
*/
public const string CREATION_DATE = "creation-date";
-
+
/**
* Modification-Date parameter name.
*
* See [[https://tools.ietf.org/html/rfc2183#section-2.5]]
*/
public const string MODIFICATION_DATE = "modification-date";
-
+
/**
* Read-Date parameter name.
*
* See [[https://tools.ietf.org/html/rfc2183#section-2.6]]
*/
public const string READ_DATE = "read-date";
-
+
/**
* Size parameter name.
*
* See [[https://tools.ietf.org/html/rfc2183#section-2.7]]
*/
public const string SIZE = "size";
-
+
/**
* The {@link DispositionType}, which is {@link DispositionType.UNSPECIFIED} if not specified.
*/
public DispositionType disposition_type { get; private set; }
-
+
/**
* True if the original DispositionType was unknown.
*/
public bool is_unknown_disposition_type { get; private set; }
-
+
/**
* The original disposition type string.
*/
public string? original_disposition_type_string { get; private set; }
-
+
/**
* Various parameters associated with the content's disposition.
*
@@ -74,7 +74,7 @@ public class Geary.Mime.ContentDisposition : Geary.BaseObject {
* @see SIZE
*/
public ContentParameters params { get; private set; }
-
+
/**
* Create a Content-Disposition representation
*/
@@ -85,7 +85,7 @@ public class Geary.Mime.ContentDisposition : Geary.BaseObject {
original_disposition_type_string = disposition;
this.params = params ?? new ContentParameters();
}
-
+
/**
* Create a simplified Content-Disposition representation.
*/
@@ -95,7 +95,7 @@ public class Geary.Mime.ContentDisposition : Geary.BaseObject {
original_disposition_type_string = null;
this.params = new ContentParameters();
}
-
+
internal ContentDisposition.from_gmime(GMime.ContentDisposition content_disposition) {
bool is_unknown;
disposition_type = DispositionType.deserialize(content_disposition.get_disposition(),
diff --git a/src/engine/mime/mime-content-parameters.vala b/src/engine/mime/mime-content-parameters.vala
index 90d578ef..0267a9d5 100644
--- a/src/engine/mime/mime-content-parameters.vala
+++ b/src/engine/mime/mime-content-parameters.vala
@@ -15,17 +15,17 @@ public class Geary.Mime.ContentParameters : BaseObject {
return params.size;
}
}
-
+
public Gee.Collection<string> attributes {
owned get {
return params.keys;
}
}
-
+
// See get_parameters() for why the keys but not the values are stored case-insensitive
private Gee.HashMap<string, string> params = new Gee.HashMap<string, string>(
Ascii.stri_hash, Ascii.stri_equal);
-
+
/**
* Create a mapping of content parameters.
*
@@ -73,7 +73,7 @@ public class Geary.Mime.ContentParameters : BaseObject {
public Gee.Map<string, string> get_parameters() {
return params.read_only_view;
}
-
+
/**
* Returns the parameter value for the attribute name.
*
@@ -82,7 +82,7 @@ public class Geary.Mime.ContentParameters : BaseObject {
public string? get_value(string attribute) {
return params.get(attribute);
}
-
+
/**
* Returns true if the attribute has the supplied value (case-insensitive comparison).
*
@@ -90,10 +90,10 @@ public class Geary.Mime.ContentParameters : BaseObject {
*/
public bool has_value_ci(string attribute, string value) {
string? stored = params.get(attribute);
-
+
return (stored != null) ? Ascii.stri_equal(stored, value) : false;
}
-
+
/**
* Returns true if the attribute has the supplied value (case-sensitive comparison).
*
@@ -101,7 +101,7 @@ public class Geary.Mime.ContentParameters : BaseObject {
*/
public bool has_value_cs(string attribute, string value) {
string? stored = params.get(attribute);
-
+
return (stored != null) ? Ascii.str_equal(stored, value) : false;
}
diff --git a/src/engine/mime/mime-content-type.vala b/src/engine/mime/mime-content-type.vala
index 4fb82dc9..ed59a491 100644
--- a/src/engine/mime/mime-content-type.vala
+++ b/src/engine/mime/mime-content-type.vala
@@ -72,7 +72,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
throw new MimeError.PARSE("Empty MIME Content-Type");
if (!str.contains("/"))
- throw new MimeError.PARSE("Invalid MIME Content-Type: %s", str);
+ throw new MimeError.PARSE("Invalid MIME Content-Type: %s", str);
return new ContentType.from_gmime(new GMime.ContentType.from_string(str));
}
@@ -125,7 +125,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
* @see has_media_type
*/
public string media_type { get; private set; }
-
+
/**
* The subtype (extension-token or iana-token) portion of the Content-Type field.
*
@@ -137,7 +137,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
* @see has_media_subtype
*/
public string media_subtype { get; private set; }
-
+
/**
* Content parameters, if any, in the Content-Type field.
*
@@ -145,7 +145,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
* no parameters.
*/
public ContentParameters params { get; private set; }
-
+
/**
* Create a MIME Content-Type representative object.
*/
@@ -154,7 +154,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
this.media_subtype = media_subtype.strip();
this.params = params ?? new ContentParameters();
}
-
+
internal ContentType.from_gmime(GMime.ContentType content_type) {
media_type = content_type.get_media_type().strip();
media_subtype = content_type.get_media_subtype().strip();
@@ -171,7 +171,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
public bool has_media_type(string media_type) {
return (media_type != WILDCARD) ? Ascii.stri_equal(this.media_type, media_type) : true;
}
-
+
/**
* Compares the {@link media_subtype} with the supplied subtype.
*
@@ -182,7 +182,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
public bool has_media_subtype(string media_subtype) {
return (media_subtype != WILDCARD) ? Ascii.stri_equal(this.media_subtype, media_subtype) : true;
}
-
+
/**
* Returns the {@link ContentType}'s media content type (its "MIME type").
*
@@ -212,7 +212,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
public bool is_type(string media_type, string media_subtype) {
return has_media_type(media_type) && has_media_subtype(media_subtype);
}
-
+
/**
* Compares this {@link ContentType} with another instance.
*
@@ -224,7 +224,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
public bool is_same(ContentType other) {
return is_type(other.media_type, other.media_subtype);
}
-
+
/**
* Compares the supplied MIME type (i.e. "image/jpeg") with this instance.
*
@@ -237,52 +237,52 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
int index = mime_type.index_of_char('/');
if (index < 0)
throw new MimeError.PARSE("Invalid MIME type: %s", mime_type);
-
+
string mime_media_type = mime_type.substring(0, index).strip();
-
+
string mime_media_subtype = mime_type.substring(index + 1);
index = mime_media_subtype.index_of_char(';');
if (index >= 0)
mime_media_subtype = mime_media_subtype.substring(0, index);
mime_media_subtype = mime_media_subtype.strip();
-
+
if (String.is_empty(mime_media_type) || String.is_empty(mime_media_subtype))
throw new MimeError.PARSE("Invalid MIME type: %s", mime_type);
-
+
return is_type(mime_media_type, mime_media_subtype);
}
public string serialize() {
StringBuilder builder = new StringBuilder();
builder.append_printf("%s/%s", media_type, media_subtype);
-
+
if (params != null && params.size > 0) {
foreach (string attribute in params.attributes) {
string value = params.get_value(attribute);
-
+
switch (DataFormat.get_encoding_requirement(value)) {
case DataFormat.Encoding.QUOTING_OPTIONAL:
builder.append_printf("; %s=%s", attribute, value);
break;
-
+
case DataFormat.Encoding.QUOTING_REQUIRED:
builder.append_printf("; %s=\"%s\"", attribute, value);
break;
-
+
case DataFormat.Encoding.UNALLOWED:
message("Cannot encode ContentType param value %s=\"%s\": unallowed",
attribute, value);
break;
-
+
default:
assert_not_reached();
}
}
}
-
+
return builder.str;
}
-
+
public string to_string() {
return serialize();
}
diff --git a/src/engine/mime/mime-data-format.vala b/src/engine/mime/mime-data-format.vala
index 276d59bf..8ca491f6 100644
--- a/src/engine/mime/mime-data-format.vala
+++ b/src/engine/mime/mime-data-format.vala
@@ -23,22 +23,22 @@ public enum Encoding {
public Encoding get_encoding_requirement(string str) {
if (String.is_empty(str))
return Encoding.QUOTING_REQUIRED;
-
+
Encoding encoding = Encoding.QUOTING_OPTIONAL;
int index = 0;
for (;;) {
char ch = str[index++];
if (ch == String.EOS)
break;
-
+
if (ch.iscntrl())
return Encoding.UNALLOWED;
-
+
// don't return immediately, it's possible unallowed characters may still be ahead
if (ch.isspace() || ch in CONTENT_TYPE_TOKEN_SPECIALS)
encoding = Encoding.QUOTING_REQUIRED;
}
-
+
return encoding;
}
diff --git a/src/engine/mime/mime-disposition-type.vala b/src/engine/mime/mime-disposition-type.vala
index 18e27b24..c00591ad 100644
--- a/src/engine/mime/mime-disposition-type.vala
+++ b/src/engine/mime/mime-disposition-type.vala
@@ -21,7 +21,7 @@ public enum Geary.Mime.DispositionType {
UNSPECIFIED = -1,
ATTACHMENT = 0,
INLINE = 1;
-
+
/**
* Convert the disposition-type field into an internal representation.
*
@@ -31,24 +31,24 @@ public enum Geary.Mime.DispositionType {
*/
public static DispositionType deserialize(string? str, out bool is_unknown) {
is_unknown = false;
-
+
if (String.is_empty_or_whitespace(str))
return UNSPECIFIED;
-
+
switch (Ascii.strdown(str)) {
case "inline":
return INLINE;
-
+
case "attachment":
return ATTACHMENT;
-
+
default:
is_unknown = true;
-
+
return ATTACHMENT;
}
}
-
+
/**
* Returns null if value is {@link UNSPECIFIED}
*/
@@ -56,26 +56,26 @@ public enum Geary.Mime.DispositionType {
switch (this) {
case UNSPECIFIED:
return null;
-
+
case ATTACHMENT:
return "attachment";
-
+
case INLINE:
return "inline";
-
+
default:
assert_not_reached();
}
}
-
+
internal static DispositionType from_int(int i) {
switch (i) {
case INLINE:
return INLINE;
-
+
case UNSPECIFIED:
return UNSPECIFIED;
-
+
// see note in class description for why unknown content-dispositions are treated as
// attachments
case ATTACHMENT:
diff --git a/src/engine/mime/mime-multipart-subtype.vala b/src/engine/mime/mime-multipart-subtype.vala
index 4de09a3e..b46f1c11 100644
--- a/src/engine/mime/mime-multipart-subtype.vala
+++ b/src/engine/mime/mime-multipart-subtype.vala
@@ -39,7 +39,7 @@ public enum Geary.Mime.MultipartSubtype {
* See [[http://tools.ietf.org/html/rfc2387]]
*/
RELATED;
-
+
/**
* Converts a {@link ContentType} into a {@link MultipartSubtype}.
*
@@ -48,24 +48,24 @@ public enum Geary.Mime.MultipartSubtype {
public static MultipartSubtype from_content_type(ContentType? content_type, out bool is_unknown) {
if (content_type == null || !content_type.has_media_type("multipart")) {
is_unknown = true;
-
+
return MIXED;
}
-
+
is_unknown = false;
switch (Ascii.strdown(content_type.media_subtype)) {
case "mixed":
return MIXED;
-
+
case "alternative":
return ALTERNATIVE;
-
+
case "related":
return RELATED;
-
+
default:
is_unknown = true;
-
+
return MIXED;
}
}
diff --git a/src/engine/nonblocking/nonblocking-batch.vala b/src/engine/nonblocking/nonblocking-batch.vala
index 39770c78..dcfeb251 100644
--- a/src/engine/nonblocking/nonblocking-batch.vala
+++ b/src/engine/nonblocking/nonblocking-batch.vala
@@ -56,9 +56,9 @@ public class Geary.Nonblocking.Batch : BaseObject {
* An invalid {@link BatchOperation} identifier.
*/
public const int INVALID_ID = -1;
-
+
private const int START_ID = 1;
-
+
private class BatchContext : BaseObject {
public int id;
public Nonblocking.BatchOperation op;
@@ -66,77 +66,77 @@ public class Geary.Nonblocking.Batch : BaseObject {
public bool completed = false;
public Object? returned = null;
public Error? threw = null;
-
+
public BatchContext(int id, Nonblocking.BatchOperation op) {
this.id = id;
this.op = op;
}
-
+
public void schedule(Nonblocking.Batch owner, Cancellable? cancellable) {
// hold a strong ref to the owner until the operation is completed
this.owner = owner;
-
+
op.execute_async.begin(cancellable, on_op_completed);
}
-
+
private void on_op_completed(Object? source, AsyncResult result) {
completed = true;
-
+
try {
returned = op.execute_async.end(result);
} catch (Error err) {
threw = err;
}
-
+
owner.on_context_completed(this);
-
+
// drop the reference to the owner to prevent a reference loop
owner = null;
}
}
-
+
/**
* Returns the number of {@link BatchOperation}s added to the batch.
*/
public int size {
get { return contexts.size; }
}
-
+
/**
* Returns the first exception encountered after completing {@link execute_all_async}.
*/
public Error? first_exception { get; private set; default = null; }
-
+
private Gee.HashMap<int, BatchContext> contexts = new Gee.HashMap<int, BatchContext>();
private Nonblocking.Semaphore sem = new Nonblocking.Semaphore();
private int next_result_id = START_ID;
private bool locked = false;
private int completed_ops = 0;
-
+
/**
* Fired when a {@link BatchOperation} is added to the batch.
*/
public signal void added(Nonblocking.BatchOperation op, int id);
-
+
/**
* Fired when batch execution has started.
*/
public signal void started(int count);
-
+
/**
* Fired when a {@link BatchOperation} has completed.
*/
public signal void operation_completed(Nonblocking.BatchOperation op, Object? returned,
Error? threw);
-
+
/**
* Fired when all {@link BatchOperation}s have completed.
*/
public signal void completed(int count, Error? first_error);
-
+
public Batch() {
}
-
+
/**
* Adds a {@link BatchOperation} for later execution.
*
@@ -151,18 +151,18 @@ public class Geary.Nonblocking.Batch : BaseObject {
public int add(Nonblocking.BatchOperation op) {
if (locked) {
warning("NonblockingBatch already executed or executing");
-
+
return INVALID_ID;
}
-
+
int id = next_result_id++;
contexts.set(id, new BatchContext(id, op));
-
+
added(op, id);
-
+
return id;
}
-
+
/**
* Executes all the {@link BatchOperation}s added to the batch.
*
@@ -180,42 +180,42 @@ public class Geary.Nonblocking.Batch : BaseObject {
public async void execute_all_async(Cancellable? cancellable = null) throws Error {
if (locked)
throw new IOError.PENDING("NonblockingBatch already executed or executing");
-
+
locked = true;
-
+
// if empty, quietly exit (leaving the object locked; NonblockingBatch is a one-shot deal)
if (contexts.size == 0)
return;
-
+
// if already cancelled, not-so-quietly exit
if (cancellable != null && cancellable.is_cancelled())
throw new IOError.CANCELLED("NonblockingBatch cancelled before executing");
-
+
started(contexts.size);
-
+
// fire them off in order they were submitted; this may hide bugs, but it also makes other
// bugs reproducible
int count = 0;
for (int id = START_ID; id < next_result_id; id++) {
BatchContext? context = contexts.get(id);
assert(context != null);
-
+
context.schedule(this, cancellable);
count++;
}
-
+
assert(count == contexts.size);
-
+
yield sem.wait_async(cancellable);
}
-
+
/**
* Returns a Set of identifiers for all added {@link BatchOperation}s.
*/
public Gee.Set<int> get_ids() {
return contexts.keys;
}
-
+
/**
* Returns the NonblockingBatchOperation for the supplied identifier.
*
@@ -223,10 +223,10 @@ public class Geary.Nonblocking.Batch : BaseObject {
*/
public Nonblocking.BatchOperation? get_operation(int id) {
BatchContext? context = contexts.get(id);
-
+
return (context != null) ? context.op : null;
}
-
+
/**
* Returns the resulting Object from the operation for the supplied identifier.
*
@@ -242,16 +242,16 @@ public class Geary.Nonblocking.Batch : BaseObject {
BatchContext? context = contexts.get(id);
if (context == null)
return null;
-
+
if (!context.completed)
throw new IOError.BUSY("NonblockingBatchOperation %d not completed", id);
-
+
if (context.threw != null)
throw context.threw;
-
+
return context.returned;
}
-
+
/**
* If no results are examined via {@link get_result}, this method can be used to manually throw
* the first seen Error from the operations.
@@ -260,20 +260,20 @@ public class Geary.Nonblocking.Batch : BaseObject {
if (first_exception != null)
throw first_exception;
}
-
+
/**
* Returns the Error message if an exception was encountered, null otherwise.
*/
public string? get_first_exception_message() {
return (first_exception != null) ? first_exception.message : null;
}
-
+
private void on_context_completed(BatchContext context) {
if (first_exception == null && context.threw != null)
first_exception = context.threw;
-
+
operation_completed(context.op, context.returned, context.threw);
-
+
assert(completed_ops < contexts.size);
if (++completed_ops == contexts.size) {
try {
@@ -281,7 +281,7 @@ public class Geary.Nonblocking.Batch : BaseObject {
} catch (Error err) {
debug("Unable to notify NonblockingBatch semaphore: %s", err.message);
}
-
+
completed(completed_ops, first_exception);
}
}
diff --git a/src/engine/nonblocking/nonblocking-concurrent.vala
b/src/engine/nonblocking/nonblocking-concurrent.vala
index cdc2a4c9..669e8a32 100644
--- a/src/engine/nonblocking/nonblocking-concurrent.vala
+++ b/src/engine/nonblocking/nonblocking-concurrent.vala
@@ -13,7 +13,7 @@
public class Geary.Nonblocking.Concurrent : BaseObject {
public const int DEFAULT_MAX_THREADS = 4;
-
+
/**
* A callback invoked from a {@link Concurrent} background thread.
*
@@ -24,33 +24,33 @@ public class Geary.Nonblocking.Concurrent : BaseObject {
* foreground caller on behalf of the callback.
*/
public delegate void ConcurrentCallback(Cancellable? cancellable) throws Error;
-
+
private class ConcurrentOperation : BaseObject {
private unowned ConcurrentCallback cb;
private Cancellable? cancellable;
private Error? caught_err = null;
private Event event = new Event();
-
+
public ConcurrentOperation(ConcurrentCallback cb, Cancellable? cancellable) {
this.cb = cb;
this.cancellable = cancellable;
}
-
+
// Called from the foreground thread to wait for the background to complete.
//
// Can't cancel here because we *must* wait for the operation to be executed by the
// thread and complete
public async void wait_async() throws Error {
yield event.wait_async();
-
+
if (caught_err != null)
throw caught_err;
-
+
// now deal with cancellation
if (cancellable != null && cancellable.is_cancelled())
throw new IOError.CANCELLED("Geary.Nonblocking.Concurrent cancelled");
}
-
+
// Called from a background thread
public void execute() {
// only execute if not already cancelled
@@ -61,28 +61,28 @@ public class Geary.Nonblocking.Concurrent : BaseObject {
caught_err = err;
}
}
-
+
// can't notify event here, Nonblocking.Event is not thread safe
//
// artificially increment the ref count of this object, schedule a completion callback
// on the forground thread, and signal there
ref();
-
+
Idle.add(on_notify_completed);
}
-
+
// Called in the context of the Event loop in the foreground thread
private bool on_notify_completed() {
// alert waiters
event.blind_notify();
-
+
// unref; do not touch "self" from here on, it's possibly deallocated
unref();
-
+
return false;
}
}
-
+
private static Concurrent? _global = null;
/**
* Returns the global instance of a {@link Concurrent} scheduler.
@@ -95,10 +95,10 @@ public class Geary.Nonblocking.Concurrent : BaseObject {
return (_global != null) ? _global : _global = new Concurrent();
}
}
-
+
private ThreadPool<ConcurrentOperation>? thread_pool = null;
private ThreadError? init_err = null;
-
+
/**
* Creates a new Concurrent pool for scheduling background work.
*
@@ -111,11 +111,11 @@ public class Geary.Nonblocking.Concurrent : BaseObject {
max_threads, false);
} catch (ThreadError err) {
init_err = err;
-
+
warning("Unable to create Geary.Nonblocking.Concurrent: %s", err.message);
}
}
-
+
/**
* Schedule a callback to be invoked in a background thread.
*
@@ -128,14 +128,14 @@ public class Geary.Nonblocking.Concurrent : BaseObject {
throws Error {
if (init_err != null)
throw init_err;
-
+
// hold ConcurrentOperation ref until thread completes
ConcurrentOperation op = new ConcurrentOperation(cb, cancellable);
thread_pool.add(op);
-
+
yield op.wait_async();
}
-
+
private void on_work_ready(owned ConcurrentOperation op) {
op.execute();
}
diff --git a/src/engine/nonblocking/nonblocking-counting-semaphore.vala
b/src/engine/nonblocking/nonblocking-counting-semaphore.vala
index 0f64ce36..07b28a48 100644
--- a/src/engine/nonblocking/nonblocking-counting-semaphore.vala
+++ b/src/engine/nonblocking/nonblocking-counting-semaphore.vala
@@ -21,17 +21,17 @@ public class Geary.Nonblocking.CountingSemaphore : Geary.Nonblocking.Lock {
* The number of tasks which have {@link acquire} the semaphore.
*/
public int count { get; private set; default = 0; }
-
+
/**
* Indicates that the {@link count} has changed due to either {@link acquire} or
* {@link notify} being invoked.
*/
public signal void count_changed(int count);
-
+
public CountingSemaphore(Cancellable? cancellable) {
base (true, true, cancellable);
}
-
+
/**
* Called by a task to acquire (and, hence, lock) the semaphore.
*
@@ -39,15 +39,15 @@ public class Geary.Nonblocking.CountingSemaphore : Geary.Nonblocking.Lock {
*/
public int acquire() {
count++;
-
+
// store on stack in case of reentrancy from signal handler; also note that Vala doesn't
// deal well with properties, pre/post-inc, and assignment on same line
int new_count = count;
count_changed(new_count);
-
+
return new_count;
}
-
+
/**
* Called by a task which has previously {@link acquire}d the semaphore.
*
@@ -60,18 +60,18 @@ public class Geary.Nonblocking.CountingSemaphore : Geary.Nonblocking.Lock {
public override void notify() throws Error {
if (count == 0)
throw new NonblockingError.INVALID("notify() on a zeroed CountingSemaphore");
-
+
count--;
-
+
// store on stack in case of reentrancy from signal handler; also note that Vala doesn't
// deal well with properties, pre/post-inc, and assignment on same line
int new_count = count;
count_changed(new_count);
-
+
if (new_count == 0)
base.notify();
}
-
+
/**
* Wait for all tasks which have {@link acquire}d this semaphore to release it.
*
diff --git a/src/engine/nonblocking/nonblocking-reporting-semaphore.vala
b/src/engine/nonblocking/nonblocking-reporting-semaphore.vala
index 744bf1c0..041a2122 100644
--- a/src/engine/nonblocking/nonblocking-reporting-semaphore.vala
+++ b/src/engine/nonblocking/nonblocking-reporting-semaphore.vala
@@ -7,45 +7,45 @@
public class Geary.Nonblocking.ReportingSemaphore<G> : Geary.Nonblocking.Semaphore {
public G result { get; private set; }
public Error? err { get; private set; default = null; }
-
+
private G default_result;
-
+
public ReportingSemaphore(G default_result, Cancellable? cancellable = null) {
base (cancellable);
-
+
this.default_result = default_result;
result = default_result;
}
-
+
public override void reset() {
result = default_result;
err = null;
-
+
base.reset();
}
-
+
public void notify_result(G result, Error? err) throws Error {
this.result = result;
this.err = err;
-
+
notify();
}
-
+
public void throw_if_error() throws Error {
if (err != null)
throw err;
}
-
+
public async G wait_for_result_async(Cancellable? cancellable = null) throws Error {
// check before waiting
throw_if_error();
-
+
// wait
yield base.wait_async(cancellable);
-
+
// if notified of error while waiting, throw that
throw_if_error();
-
+
return result;
}
}
diff --git a/src/engine/rfc822/rfc822-gmime-filter-blockquotes.vala
b/src/engine/rfc822/rfc822-gmime-filter-blockquotes.vala
index 95dd5572..7d18342d 100644
--- a/src/engine/rfc822/rfc822-gmime-filter-blockquotes.vala
+++ b/src/engine/rfc822/rfc822-gmime-filter-blockquotes.vala
@@ -10,25 +10,25 @@ private class Geary.RFC822.FilterBlockquotes : GMime.Filter {
// Invariant: True iff we are either at the beginning of a line, or all characters seen so far
// have been quote markers or part of a tag.
private bool in_prefix;
-
+
// True if we're in a tag in the prefix only.
private bool in_tag;
-
+
// Invariant: The quote depth of the last complete line seen, or 0 if we have not yet seen a
// complete line.
private uint last_quote_level;
-
+
// Invariant: The number of QUOTE_MARKERs seen so far if we are parsing the prefix, or 0 if we
// are not parsing the prefix.
private uint current_quote_level;
-
+
// Have we inserted the initial element?
private bool initial_element;
-
+
public FilterBlockquotes() {
reset();
}
-
+
public override void reset() {
in_prefix = true;
in_tag = false;
@@ -36,25 +36,25 @@ private class Geary.RFC822.FilterBlockquotes : GMime.Filter {
current_quote_level = 0;
initial_element = false;
}
-
+
public override GMime.Filter copy() {
FilterBlockquotes new_filter = new FilterBlockquotes();
-
+
new_filter.in_prefix = in_prefix;
new_filter.in_tag = in_tag;
new_filter.last_quote_level = last_quote_level;
new_filter.current_quote_level = current_quote_level;
new_filter.initial_element = initial_element;
-
+
return new_filter;
}
-
+
private void do_filter(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
out size_t outprespace, bool flush) {
-
+
// This may not be strictly necessary.
set_size(inbuf.length, false);
-
+
uint out_index = 0;
if (!initial_element) {
// We set the style explicitly so it will be set in HTML emails. We also give it a
@@ -62,10 +62,10 @@ private class Geary.RFC822.FilterBlockquotes : GMime.Filter {
insert_string("<div class=\"plaintext\" style=\"white-space: pre-wrap;\">", ref out_index);
initial_element = true;
}
-
+
for (uint i = 0; i < inbuf.length; i++) {
char c = inbuf[i];
-
+
if (in_prefix && !in_tag) {
if (c == Geary.RFC822.Utils.QUOTE_MARKER) {
current_quote_level++;
@@ -76,7 +76,7 @@ private class Geary.RFC822.FilterBlockquotes : GMime.Filter {
outbuf[out_index++] = c;
continue;
}
-
+
while (current_quote_level > last_quote_level) {
insert_string("<blockquote>", ref out_index);
last_quote_level += 1;
@@ -85,11 +85,11 @@ private class Geary.RFC822.FilterBlockquotes : GMime.Filter {
insert_string("</blockquote>", ref out_index);
last_quote_level -= 1;
}
-
+
// We saw a character other than '>', so we're done scanning the prefix.
in_prefix = false;
}
-
+
if (c == '\n') {
// Was this last line a signature marker?
if(out_index > 3 &&
@@ -108,7 +108,7 @@ private class Geary.RFC822.FilterBlockquotes : GMime.Filter {
outbuf[out_index++] = c;
}
}
-
+
if (flush) {
while (last_quote_level > 0) {
insert_string("</blockquote>", ref out_index);
@@ -116,22 +116,22 @@ private class Geary.RFC822.FilterBlockquotes : GMime.Filter {
}
insert_string("</div>", ref out_index);
}
-
+
// Slicing the buffer is important, because the buffer is not null-terminated,
processed_buffer = outbuf[0:out_index];
outprespace = this.outpre;
}
-
+
public override void filter(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
out size_t outprespace) {
do_filter(inbuf, prespace, out processed_buffer, out outprespace, false);
}
-
+
public override void complete(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
out size_t outprespace) {
do_filter(inbuf, prespace, out processed_buffer, out outprespace, true);
}
-
+
private void insert_string(string str, ref uint out_index) {
set_size(outbuf.length + str.length, true);
for (int i = 0; i < str.length; i++)
diff --git a/src/engine/rfc822/rfc822-gmime-filter-plain.vala
b/src/engine/rfc822/rfc822-gmime-filter-plain.vala
index a018b000..d66e3284 100644
--- a/src/engine/rfc822/rfc822-gmime-filter-plain.vala
+++ b/src/engine/rfc822/rfc822-gmime-filter-plain.vala
@@ -9,53 +9,53 @@ private class Geary.RFC822.FilterPlain : GMime.Filter {
// Invariant: True iff we are either at the beginning of a line, or all characters seen so far
// have been quote markers.
private bool in_prefix;
-
+
public FilterPlain() {
reset();
}
-
+
public override void reset() {
in_prefix = true;
}
-
+
public override GMime.Filter copy() {
FilterPlain new_filter = new FilterPlain();
-
+
new_filter.in_prefix = in_prefix;
-
+
return new_filter;
}
-
+
public override void filter(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
out size_t outprespace) {
-
+
// This may not be strictly necessary.
set_size(inbuf.length, false);
-
+
uint out_index = 0;
for (uint i = 0; i < inbuf.length; i++) {
char c = inbuf[i];
-
+
if (in_prefix) {
if (c == '>') {
outbuf[out_index++] = Geary.RFC822.Utils.QUOTE_MARKER;
continue;
}
-
+
// We saw a character other than '>', so we're done scanning the prefix.
in_prefix = false;
}
-
+
if (c == '\n')
in_prefix = true;
outbuf[out_index++] = c;
}
-
+
// Slicing the buffer is important, because the buffer is not null-terminated,
processed_buffer = outbuf[0:out_index];
outprespace = this.outpre;
}
-
+
public override void complete(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
out size_t outprespace) {
filter(inbuf, prespace, out processed_buffer, out outprespace);
diff --git a/src/engine/rfc822/rfc822-message-data.vala b/src/engine/rfc822/rfc822-message-data.vala
index 705bac4c..97be9acd 100644
--- a/src/engine/rfc822/rfc822-message-data.vala
+++ b/src/engine/rfc822/rfc822-message-data.vala
@@ -24,14 +24,14 @@ public class Geary.RFC822.MessageID : Geary.MessageData.StringMessageData, Geary
string? normalized = normalize(value);
base (normalized ?? value);
}
-
+
// Adds brackets if required, null if no change required
private static string? normalize(string value) {
bool needs_prefix = !value.has_prefix("<");
bool needs_suffix = !value.has_suffix(">");
if (!needs_prefix && !needs_suffix)
return null;
-
+
return "%s%s%s".printf(needs_prefix ? "<" : "", value, needs_suffix ? ">" : "");
}
}
@@ -41,27 +41,27 @@ public class Geary.RFC822.MessageID : Geary.MessageData.StringMessageData, Geary
*/
public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData, Geary.RFC822.MessageData {
public Gee.List<MessageID> list { get; private set; }
-
+
public MessageIDList() {
list = new Gee.ArrayList<MessageID>();
}
-
+
public MessageIDList.from_collection(Gee.Collection<MessageID> collection) {
this ();
-
+
foreach(MessageID msg_id in collection)
this.list.add(msg_id);
}
-
+
public MessageIDList.single(MessageID msg_id) {
this ();
-
+
list.add(msg_id);
}
-
+
public MessageIDList.from_rfc822_string(string value) {
this ();
-
+
// Have seen some mailers use commas between Message-IDs and whitespace inside Message-IDs,
// meaning that the standard whitespace tokenizer is not sufficient. The only guarantee
// made of a Message-ID is that it's surrounded by angle brackets, so save anything inside
@@ -87,7 +87,7 @@ public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData,
in_message_id = true;
bracketed = true;
break;
-
+
case '(':
if (!in_message_id) {
in_message_id = true;
@@ -96,18 +96,18 @@ public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData,
add_char = true;
}
break;
-
+
case '>':
in_message_id = false;
break;
-
+
case ')':
if (in_message_id)
in_message_id = false;
else
add_char = true;
break;
-
+
default:
// deal with Message-IDs without brackets ... bracketed is set to true the
// moment the first one is found, so this doesn't deal with combinations of
@@ -119,26 +119,26 @@ public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData,
else if (in_message_id && ch.isspace())
in_message_id = false;
}
-
+
// only add characters inside the brackets or, if not bracketed, work around
add_char = in_message_id;
break;
}
-
+
if (add_char)
canonicalized.append_c(ch);
-
+
if (!in_message_id && !String.is_empty(canonicalized.str)) {
list.add(new MessageID(canonicalized.str));
-
+
canonicalized = new StringBuilder();
}
}
-
+
// pick up anything that doesn't end with brackets
if (!String.is_empty(canonicalized.str))
list.add(new MessageID(canonicalized.str));
-
+
// don't assert that list.size > 0; even though this method should generated a decoded ID
// from any non-empty string, an empty Message-ID (i.e. "<>") won't.
}
@@ -156,12 +156,12 @@ public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData,
public override string to_string() {
return "MessageIDList (%d)".printf(list.size);
}
-
+
public virtual string to_rfc822_string() {
string[] strings = new string[list.size];
for(int i = 0; i < list.size; ++i)
strings[i] = list[i].value;
-
+
return string.joinv(" ", strings);
}
}
@@ -223,15 +223,15 @@ public class Geary.RFC822.Date : Geary.RFC822.MessageData, Geary.MessageData.Abs
public virtual string serialize() {
return to_iso_8601();
}
-
+
public virtual bool equal_to(Geary.RFC822.Date other) {
return (this != other) ? value.equal(other.value) : true;
}
-
+
public virtual uint hash() {
return value.hash();
}
-
+
public override string to_string() {
return original ?? value.to_string();
}
@@ -247,37 +247,37 @@ public class Geary.RFC822.Subject : Geary.MessageData.StringMessageData,
Geary.MessageData.SearchableMessageData, Geary.RFC822.MessageData {
public const string REPLY_PREFACE = "Re:";
public const string FORWARD_PREFACE = "Fwd:";
-
+
public string original { get; private set; }
-
+
public Subject(string value) {
base (value);
original = value;
}
-
+
public Subject.decode(string value) {
base (GMime.utils_header_decode_text(value));
original = value;
}
-
+
public bool is_reply() {
return value.down().has_prefix(REPLY_PREFACE.down());
}
-
+
public Subject create_reply() {
return is_reply() ? new Subject(value) : new Subject("%s %s".printf(REPLY_PREFACE,
value));
}
-
+
public bool is_forward() {
return value.down().has_prefix(FORWARD_PREFACE.down());
}
-
+
public Subject create_forward() {
return is_forward() ? new Subject(value) : new Subject("%s %s".printf(FORWARD_PREFACE,
value));
}
-
+
/**
* Returns the Subject: line stripped of reply and forwarding prefixes.
*
@@ -293,23 +293,23 @@ public class Geary.RFC822.Subject : Geary.MessageData.StringMessageData,
try {
Regex re_regex = new Regex("^(?i:Re:\\s*)+");
stripped = re_regex.replace(subject_base, -1, 0, "");
-
+
Regex fwd_regex = new Regex("^(?i:Fwd:\\s*)+");
stripped = fwd_regex.replace(stripped, -1, 0, "");
} catch (RegexError e) {
debug("Failed to clean up subject line \"%s\": %s", value, e.message);
-
+
break;
}
-
+
changed = (stripped != subject_base);
if (changed)
subject_base = stripped;
} while (changed);
-
+
return String.reduce_whitespace(subject_base);
}
-
+
/**
* See Geary.MessageData.SearchableMessageData.
*/
@@ -321,44 +321,44 @@ public class Geary.RFC822.Subject : Geary.MessageData.StringMessageData,
public class Geary.RFC822.Header : Geary.MessageData.BlockMessageData, Geary.RFC822.MessageData {
private GMime.Message? message = null;
private string[]? names = null;
-
+
public Header(Memory.Buffer buffer) {
base ("RFC822.Header", buffer);
}
-
+
private unowned GMime.HeaderList get_headers() throws RFC822Error {
if (message != null)
return message.get_header_list();
-
+
GMime.Parser parser = new GMime.Parser.with_stream(Utils.create_stream_mem(buffer));
parser.set_respect_content_length(false);
parser.set_scan_from(false);
-
+
message = parser.construct_message();
if (message == null)
throw new RFC822Error.INVALID("Unable to parse RFC 822 headers");
-
+
return message.get_header_list();
}
-
+
public string? get_header(string name) throws RFC822Error {
return get_headers().get(name);
}
-
+
public string[] get_header_names() throws RFC822Error {
if (names != null)
return names;
-
+
names = new string[0];
-
+
unowned GMime.HeaderIter iter;
if (!get_headers().get_iter(out iter))
return names;
-
+
do {
names += iter.get_name();
} while (iter.next());
-
+
return names;
}
}
diff --git a/src/engine/rfc822/rfc822-message.vala b/src/engine/rfc822/rfc822-message.vala
index 15c90277..3afdec4e 100644
--- a/src/engine/rfc822/rfc822-message.vala
+++ b/src/engine/rfc822/rfc822-message.vala
@@ -34,7 +34,7 @@ public class Geary.RFC822.Message : BaseObject {
private const string HEADER_REFERENCES = "References";
private const string HEADER_MAILER = "X-Mailer";
private const string HEADER_BCC = "Bcc";
-
+
// Internal note: If a field is added here, it *must* be set in stock_from_gmime().
public RFC822.MailboxAddress? sender { get; private set; default = null; }
public RFC822.MailboxAddresses? from { get; private set; default = null; }
@@ -56,30 +56,30 @@ public class Geary.RFC822.Message : BaseObject {
// set these easily, so sometimes get_email() won't work.
private Memory.Buffer? body_buffer = null;
private size_t? body_offset = null;
-
+
public Message(Full full) throws RFC822Error {
GMime.Parser parser = new GMime.Parser.with_stream(Utils.create_stream_mem(full.buffer));
-
+
message = parser.construct_message();
if (message == null)
throw new RFC822Error.INVALID("Unable to parse RFC 822 message");
-
+
// See the declaration of these fields for why we do this.
body_buffer = full.buffer;
body_offset = (size_t) parser.get_headers_end();
-
+
stock_from_gmime();
}
-
+
public Message.from_gmime_message(GMime.Message message) {
this.message = message;
stock_from_gmime();
}
-
+
public Message.from_buffer(Memory.Buffer full_email) throws RFC822Error {
this(new Geary.RFC822.Full(full_email));
}
-
+
public Message.from_parts(Header header, Text body) throws RFC822Error {
GMime.StreamCat stream_cat = new GMime.StreamCat();
stream_cat.add_source(new GMime.StreamMem.with_buffer(header.buffer.get_bytes().get_data()));
@@ -89,10 +89,10 @@ public class Geary.RFC822.Message : BaseObject {
message = parser.construct_message();
if (message == null)
throw new RFC822Error.INVALID("Unable to parse RFC 822 message");
-
+
body_buffer = body.buffer;
body_offset = 0;
-
+
stock_from_gmime();
}
@@ -295,7 +295,7 @@ public class Geary.RFC822.Message : BaseObject {
} catch (Error e) {
error("Error creating a memory buffer from a message: %s", e.message);
}
-
+
// GMime also drops the ball for the *new* message. When it comes out of the GMime
// Parser, its "mime part" somehow isn't realizing it has a Content-Type header
// already, so whenever you manipulate the headers, it adds a duplicate one. This
@@ -306,10 +306,10 @@ public class Geary.RFC822.Message : BaseObject {
GMime.Object original_mime_part = message.get_mime_part();
GMime.Message empty = new GMime.Message(true);
message.set_mime_part(empty.get_mime_part());
-
+
message.remove_header(HEADER_BCC);
bcc = null;
-
+
message.set_mime_part(original_mime_part);
}
@@ -360,7 +360,7 @@ public class Geary.RFC822.Message : BaseObject {
part.set_content_encoding(Geary.RFC822.Utils.get_best_encoding(stream));
return part;
}
-
+
/**
* Construct a Geary.Email from a Message. NOTE: this requires you to have created
* the Message in such a way that its body_buffer and body_offset fields will be filled
@@ -370,9 +370,9 @@ public class Geary.RFC822.Message : BaseObject {
public Geary.Email get_email(Geary.EmailIdentifier id) throws Error {
assert(body_buffer != null);
assert(body_offset != null);
-
+
Geary.Email email = new Geary.Email(id);
-
+
email.set_message_header(new Geary.RFC822.Header(new Geary.Memory.StringBuffer(
message.get_headers())));
email.set_send_date(date);
@@ -431,19 +431,19 @@ public class Geary.RFC822.Message : BaseObject {
public Gee.List<RFC822.MailboxAddress>? get_recipients() {
Gee.List<RFC822.MailboxAddress> addrs = new Gee.ArrayList<RFC822.MailboxAddress>();
-
+
if (to != null)
addrs.add_all(to.get_all());
-
+
if (cc != null)
addrs.add_all(cc.get_all());
-
+
if (bcc != null)
addrs.add_all(bcc.get_all());
-
+
return (addrs.size > 0) ? addrs : null;
}
-
+
/**
* Returns the {@link Message} as a {@link Memory.Buffer} suitable for in-memory use (i.e.
* with native linefeed characters).
@@ -451,7 +451,7 @@ public class Geary.RFC822.Message : BaseObject {
public Memory.Buffer get_native_buffer() throws RFC822Error {
return message_to_memory_buffer(false, false);
}
-
+
/**
* Returns the {@link Message} as a {@link Memory.Buffer} suitable for transmission or
* storage (i.e. using protocol-specific linefeeds).
@@ -546,22 +546,22 @@ public class Geary.RFC822.Message : BaseObject {
Mime.MultipartSubtype.from_content_type(content_type, null);
bool found_text_subtype = false;
-
+
StringBuilder builder = new StringBuilder();
int count = multipart.get_count();
for (int i = 0; i < count; ++i) {
GMime.Object child = multipart.get_part(i);
-
+
string? child_body = null;
found_text_subtype |= construct_body_from_mime_parts(child, this_subtype, text_subtype,
to_html, replacer, ref child_body);
if (child_body != null)
builder.append(child_body);
}
-
+
if (!String.is_empty(builder.str))
body = builder.str;
-
+
return found_text_subtype;
}
@@ -670,10 +670,10 @@ public class Geary.RFC822.Message : BaseObject {
// Ignore.
}
}
-
+
if (body != null && html)
body = Geary.HTML.html_to_text(body);
-
+
if (include_sub_messages) {
foreach (Message sub_message in get_sub_messages()) {
// We index a rough approximation of what a client would be
@@ -701,7 +701,7 @@ public class Geary.RFC822.Message : BaseObject {
string? sub_body = sub_message.get_searchable_body(false);
if (sub_body != null)
sub_full.append(sub_body);
-
+
if (sub_full.len > 0) {
if (body == null)
body = "";
@@ -709,7 +709,7 @@ public class Geary.RFC822.Message : BaseObject {
}
}
}
-
+
return body;
}
@@ -835,7 +835,7 @@ public class Geary.RFC822.Message : BaseObject {
// This is often the case, and we'll treat these as attached
disposition = Mime.DispositionType.ATTACHMENT;
}
-
+
if (requested_disposition == Mime.DispositionType.UNSPECIFIED || disposition ==
requested_disposition) {
GMime.Stream stream = new GMime.StreamMem();
message.write_to_stream(stream);
@@ -880,7 +880,7 @@ public class Geary.RFC822.Message : BaseObject {
find_sub_messages(messages, message.get_mime_part());
return messages;
}
-
+
private void find_sub_messages(Gee.List<Geary.RFC822.Message> messages, GMime.Object root) {
// If this is a multipart container, check each of its children.
GMime.Multipart? multipart = root as GMime.Multipart;
@@ -891,7 +891,7 @@ public class Geary.RFC822.Message : BaseObject {
}
return;
}
-
+
GMime.MessagePart? messagepart = root as GMime.MessagePart;
if (messagepart != null) {
GMime.Message sub_message = messagepart.get_message();
@@ -902,21 +902,21 @@ public class Geary.RFC822.Message : BaseObject {
}
}
}
-
+
private Memory.Buffer message_to_memory_buffer(bool encoded, bool dotstuffed) throws RFC822Error {
ByteArray byte_array = new ByteArray();
GMime.StreamMem stream = new GMime.StreamMem.with_byte_array(byte_array);
stream.set_owner(false);
-
+
GMime.StreamFilter stream_filter = new GMime.StreamFilter(stream);
stream_filter.add(new GMime.FilterCRLF(encoded, dotstuffed));
-
+
if (message.write_to_stream(stream_filter) < 0)
throw new RFC822Error.FAILED("Unable to write RFC822 message to memory buffer");
-
+
if (stream_filter.flush() != 0)
throw new RFC822Error.FAILED("Unable to flush RFC822 message to memory buffer");
-
+
return new Memory.ByteBuffer.from_byte_array(byte_array);
}
diff --git a/src/engine/rfc822/rfc822-utils.vala b/src/engine/rfc822/rfc822-utils.vala
index 85301913..fc5cdb6b 100644
--- a/src/engine/rfc822/rfc822-utils.vala
+++ b/src/engine/rfc822/rfc822-utils.vala
@@ -54,10 +54,10 @@ public GMime.StreamMem create_stream_mem(Memory.Buffer buffer) {
// the best of all possible worlds, assuming the Memory.Buffer is not destroyed first
GMime.StreamMem stream = new GMime.StreamMem();
stream.set_byte_array(unowned_bytes_array_buffer.to_unowned_byte_array());
-
+
return stream;
}
-
+
Memory.UnownedBytesBuffer? unowned_bytes_buffer = buffer as Memory.UnownedBytesBuffer;
if (unowned_bytes_buffer != null) {
// StreamMem.with_buffer does do a buffer copy (there's not set_buffer() call like
@@ -65,7 +65,7 @@ public GMime.StreamMem create_stream_mem(Memory.Buffer buffer) {
// Memory.Buffer
return new GMime.StreamMem.with_buffer(unowned_bytes_buffer.to_unowned_uint8_array());
}
-
+
// do plain-old buffer copy
return new GMime.StreamMem.with_buffer(buffer.get_uint8_array());
}
@@ -102,7 +102,7 @@ public Geary.RFC822.MailboxAddresses create_to_addresses_for_reply(Geary.Email e
Gee.List< Geary.RFC822.MailboxAddress>? sender_addresses = null) {
Gee.List<Geary.RFC822.MailboxAddress> new_to =
new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
-
+
// If we're replying to something we sent, send it to the same people we originally did.
// Otherwise, we'll send to the reply-to address or the from address.
if (email.to != null && email_is_from_sender(email, sender_addresses))
@@ -111,35 +111,35 @@ public Geary.RFC822.MailboxAddresses create_to_addresses_for_reply(Geary.Email e
new_to.add_all(email.reply_to.get_all());
else if (email.from != null)
new_to.add_all(email.from.get_all());
-
+
// Exclude the current sender. No need to receive the mail they're sending.
if (sender_addresses != null) {
foreach (RFC822.MailboxAddress address in sender_addresses)
remove_address(new_to, address);
}
-
+
return new Geary.RFC822.MailboxAddresses(new_to);
}
public Geary.RFC822.MailboxAddresses create_cc_addresses_for_reply_all(Geary.Email email,
Gee.List<Geary.RFC822.MailboxAddress>? sender_addresses = null) {
Gee.List<Geary.RFC822.MailboxAddress> new_cc = new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
-
+
// If we're replying to something we received, also add other recipients. Don't do this for
// emails we sent, since everyone we sent it to is already covered in
// create_to_addresses_for_reply().
if (email.to != null && !email_is_from_sender(email, sender_addresses))
new_cc.add_all(email.to.get_all());
-
+
if (email.cc != null)
new_cc.add_all(email.cc.get_all());
-
+
// Again, exclude the current sender.
if (sender_addresses != null) {
foreach (RFC822.MailboxAddress address in sender_addresses)
remove_address(new_cc, address, true);
}
-
+
return new Geary.RFC822.MailboxAddresses(new_cc);
}
@@ -156,7 +156,7 @@ public Geary.RFC822.MailboxAddresses merge_addresses(Geary.RFC822.MailboxAddress
} else if (second != null) {
result.add_all(second.get_all());
}
-
+
return new Geary.RFC822.MailboxAddresses(result);
}
@@ -175,11 +175,11 @@ public Geary.RFC822.MailboxAddresses remove_addresses(Geary.RFC822.MailboxAddres
public string reply_references(Geary.Email source) {
// generate list for References
Gee.ArrayList<RFC822.MessageID> list = new Gee.ArrayList<RFC822.MessageID>();
-
+
// 1. Start with the source's References list
if (source.references != null && source.references.list.size > 0)
list.add_all(source.references.list);
-
+
// 2. If there are In-Reply-To Message-IDs and they're not in the References list, append them
if (source.in_reply_to != null) {
foreach (RFC822.MessageID reply_id in source.in_reply_to.list) {
@@ -187,29 +187,29 @@ public string reply_references(Geary.Email source) {
list.add(reply_id);
}
}
-
+
// 3. Append the source's Message-ID, if available.
if (source.message_id != null)
list.add(source.message_id);
-
+
string[] strings = new string[list.size];
for(int i = 0; i < list.size; ++i)
strings[i] = list[i].value;
-
+
return (list.size > 0) ? string.joinv(" ", strings) : "";
}
public string email_addresses_for_reply(Geary.RFC822.MailboxAddresses? addresses, TextFormat format) {
if (addresses == null)
return "";
-
+
switch (format) {
case TextFormat.HTML:
return HTML.escape_markup(addresses.to_string());
-
+
case TextFormat.PLAIN:
return addresses.to_string();
-
+
default:
assert_not_reached();
}
@@ -221,7 +221,7 @@ public string email_addresses_for_reply(Geary.RFC822.MailboxAddresses? addresses
*
* If there's no message body in the supplied email or quote text, this
* function will return the empty string.
- *
+ *
* If html_format is true, the message will be quoted in HTML format.
* Otherwise it will be in plain text.
*/
@@ -349,7 +349,7 @@ public bool comp_char_arr_slice(char[] array, uint start, string comp) {
if (array[start + i] != comp[i])
return false;
}
-
+
return true;
}
diff --git a/src/engine/smtp/smtp-authenticator.vala b/src/engine/smtp/smtp-authenticator.vala
index 76dbee08..a4089c37 100644
--- a/src/engine/smtp/smtp-authenticator.vala
+++ b/src/engine/smtp/smtp-authenticator.vala
@@ -15,22 +15,22 @@ public abstract class Geary.Smtp.Authenticator : BaseObject {
* The user-visible name for this {@link Authenticator}.
*/
public string name { get; private set; }
-
+
public Credentials credentials { get; private set; }
-
+
public Authenticator(string name, Credentials credentials) {
this.name = name;
this.credentials = credentials;
-
+
if (!credentials.is_complete())
message("Incomplete credentials supplied to SMTP authenticator %s", name);
}
-
+
/**
* Returns a Request that is used to initiate the challenge-response.
*/
public abstract Request initiate();
-
+
/**
* Returns a block of data that will be sent for that stage of the authentication challenge.
* No line terminators should be present. Various authentication schemes may also have their
@@ -38,14 +38,14 @@ public abstract class Geary.Smtp.Authenticator : BaseObject {
*
* step is the zero-based step number of the challenge-response. Response is the *last*
* SMTP response from the server from the prior step (or from the initiate() request).
- *
+ *
* Returns null if the Authenticator chooses to end the process in an orderly fashion.
- *
+ *
* If an error is thrown, the entire process is aborted without any further I/O with the
* server. Generally this leaves the connection in a bad state and should be closed.
*/
public abstract Memory.Buffer? challenge(int step, Response response) throws SmtpError;
-
+
public virtual string to_string() {
return name;
}
diff --git a/src/engine/smtp/smtp-capabilities.vala b/src/engine/smtp/smtp-capabilities.vala
index c7be093b..adfc7e19 100644
--- a/src/engine/smtp/smtp-capabilities.vala
+++ b/src/engine/smtp/smtp-capabilities.vala
@@ -15,11 +15,11 @@ public class Geary.Smtp.Capabilities : Geary.GenericCapabilities {
public const string NAME_SEPARATOR = " ";
public const string VALUE_SEPARATOR = " ";
-
+
public Capabilities() {
base (NAME_SEPARATOR, VALUE_SEPARATOR);
}
-
+
/**
* Returns number of response lines added.
*/
@@ -30,10 +30,10 @@ public class Geary.Smtp.Capabilities : Geary.GenericCapabilities {
if (add_response_line(response.lines[ctr]))
count++;
}
-
+
return count;
}
-
+
public bool add_response_line(ResponseLine line) {
return !String.is_empty(line.explanation) ? parse_and_add_capability(line.explanation) : false;
}
diff --git a/src/engine/smtp/smtp-client-connection.vala b/src/engine/smtp/smtp-client-connection.vala
index 56e40ca3..1165e7a8 100644
--- a/src/engine/smtp/smtp-client-connection.vala
+++ b/src/engine/smtp/smtp-client-connection.vala
@@ -7,56 +7,56 @@
public class Geary.Smtp.ClientConnection {
public const uint DEFAULT_TIMEOUT_SEC = 20;
-
+
public Geary.Smtp.Capabilities? capabilities { get; private set; default = null; }
-
+
private Geary.Endpoint endpoint;
private IOStream? cx = null;
private SocketConnection? socket_cx = null;
private DataInputStream? dins = null;
private DataOutputStream douts = null;
-
+
public ClientConnection(Geary.Endpoint endpoint) {
this.endpoint = endpoint;
}
-
+
public bool is_connected() {
return (cx != null);
}
-
+
public async Greeting? connect_async(Cancellable? cancellable = null) throws Error {
if (cx != null) {
debug("Already connected to %s", to_string());
-
+
return null;
}
-
+
cx = socket_cx = yield endpoint.connect_async(cancellable);
set_data_streams(cx);
-
+
// read and deserialize the greeting
Greeting greeting = new Greeting(yield recv_response_lines_async(cancellable));
Logging.debug(Logging.Flag.NETWORK, "[%s] SMTP Greeting: %s", to_string(), greeting.to_string());
-
+
return greeting;
}
-
+
public async bool disconnect_async(Cancellable? cancellable = null) throws Error {
if (cx == null)
return false;
-
+
Error? disconnect_error = null;
try {
yield cx.close_async(Priority.DEFAULT, cancellable);
} catch (Error err) {
disconnect_error = err;
}
-
+
cx = null;
-
+
if (disconnect_error != null)
throw disconnect_error;
-
+
return true;
}
@@ -66,12 +66,12 @@ public class Geary.Smtp.ClientConnection {
public async Response authenticate_async(Authenticator authenticator, Cancellable? cancellable = null)
throws Error {
check_connected();
-
+
Response response = yield transaction_async(authenticator.initiate(), cancellable);
-
+
Logging.debug(Logging.Flag.NETWORK, "[%s] Initiated SMTP %s authentication", to_string(),
authenticator.to_string());
-
+
// Possible for initiate() Request to:
// (a) immediately generate success (due to valid authentication being passed in Request);
// (b) immediately fails;
@@ -84,16 +84,16 @@ public class Geary.Smtp.ClientConnection {
Memory.Buffer? data = authenticator.challenge(step++, response);
if (data == null || data.size == 0)
data = new Memory.StringBuffer(DataFormat.CANCEL_AUTHENTICATION);
-
+
Logging.debug(Logging.Flag.NETWORK, "[%s] SMTP AUTH Challenge recvd", to_string());
-
+
yield Stream.write_all_async(douts, data, cancellable);
douts.put_string(DataFormat.LINE_TERMINATOR);
yield douts.flush_async(Priority.DEFAULT, cancellable);
-
+
response = yield recv_response_async(cancellable);
}
-
+
return response;
}
@@ -110,7 +110,7 @@ public class Geary.Smtp.ClientConnection {
public async Response send_data_async(Memory.Buffer data, bool already_dotstuffed,
Cancellable? cancellable = null) throws Error {
check_connected();
-
+
// In the case of DATA, want to receive an intermediate response code, specifically 354
Response response = yield transaction_async(new Request(Command.DATA), cancellable);
if (!response.code.is_start_data())
@@ -123,18 +123,18 @@ public class Geary.Smtp.ClientConnection {
// a proper line terminator for SMTP
DataInputStream dins = new DataInputStream(data.get_input_stream());
dins.set_newline_type(DataStreamNewlineType.ANY);
-
+
// Read each line and dot-stuff if necessary
for (;;) {
size_t length;
string? line = yield dins.read_line_async(Priority.DEFAULT, cancellable, out length);
if (line == null)
break;
-
+
// stuffing
if (line[0] == '.')
yield Stream.write_string_async(douts, ".", cancellable);
-
+
yield Stream.write_string_async(douts, line, cancellable);
yield Stream.write_string_async(douts, DataFormat.LINE_TERMINATOR, cancellable);
}
@@ -142,50 +142,50 @@ public class Geary.Smtp.ClientConnection {
// ready to go, send and commit
yield Stream.write_all_async(douts, data, cancellable);
}
-
+
// terminate buffer and flush to server
yield Stream.write_string_async(douts, DataFormat.DATA_TERMINATOR, cancellable);
yield douts.flush_async(Priority.DEFAULT, cancellable);
-
+
return yield recv_response_async(cancellable);
}
-
+
public async void send_request_async(Request request, Cancellable? cancellable = null) throws Error {
check_connected();
-
+
Logging.debug(Logging.Flag.NETWORK, "[%s] SMTP Request: %s", to_string(), request.to_string());
-
+
douts.put_string(request.serialize());
douts.put_string(DataFormat.LINE_TERMINATOR);
yield douts.flush_async(Priority.DEFAULT, cancellable);
}
-
+
private async Gee.List<ResponseLine> recv_response_lines_async(Cancellable? cancellable) throws Error {
check_connected();
-
+
Gee.List<ResponseLine> lines = new Gee.ArrayList<ResponseLine>();
for (;;) {
ResponseLine line = ResponseLine.deserialize(yield read_line_async(cancellable));
lines.add(line);
-
+
if (!line.continued)
break;
}
-
+
// lines should never be empty; if it is, then somebody didn't throw an exception
assert(lines.size > 0);
-
+
return lines;
}
-
+
public async Response recv_response_async(Cancellable? cancellable = null) throws Error {
Response response = new Response(yield recv_response_lines_async(cancellable));
-
+
Logging.debug(Logging.Flag.NETWORK, "[%s] SMTP Response: %s", to_string(), response.to_string());
-
+
return response;
}
-
+
/**
* Sends the appropriate HELO/EHLO command and returns the response of the one that worked.
* Also saves the server's capabilities in the capabilities property (overwriting any that may
@@ -195,7 +195,7 @@ public class Geary.Smtp.ClientConnection {
// get local address as FQDN to greet server ... note that this merely returns the DHCP address
// for machines behind a NAT
InetAddress local_addr = ((InetSocketAddress) socket_cx.get_local_address()).get_address();
-
+
// only attempt to produce a FQDN if not a local address and use the local address if
// unavailable
string? fqdn = null;
@@ -207,7 +207,7 @@ public class Geary.Smtp.ClientConnection {
local_addr.to_string(), err.message);
}
}
-
+
// try EHLO first, then fall back on HELO
EhloRequest ehlo = !String.is_empty(fqdn) ? new EhloRequest(fqdn) : new
EhloRequest.for_local_address(local_addr);
Response response = yield transaction_async(ehlo, cancellable);
@@ -224,10 +224,10 @@ public class Geary.Smtp.ClientConnection {
response.to_string().strip());
}
}
-
+
return response;
}
-
+
/**
* Sends the appropriate hello command to the server (EHLO / HELO) and establishes whatever
* additional connection features are available (STARTTLS, compression). For general-purpose
@@ -277,25 +277,25 @@ public class Geary.Smtp.ClientConnection {
public async Response transaction_async(Request request, Cancellable? cancellable = null)
throws Error {
yield send_request_async(request, cancellable);
-
+
return yield recv_response_async(cancellable);
}
-
+
private async string read_line_async(Cancellable? cancellable) throws Error {
size_t length;
string? line = yield dins.read_line_async(Priority.DEFAULT, cancellable, out length);
-
+
if (String.is_empty(line))
throw new IOError.CLOSED("End of stream detected on %s", to_string());
-
+
return line;
}
-
+
private void check_connected() throws Error {
if (cx == null)
throw new SmtpError.NOT_CONNECTED("Not connected to %s", to_string());
}
-
+
public string to_string() {
return endpoint.to_string();
}
diff --git a/src/engine/smtp/smtp-client-session.vala b/src/engine/smtp/smtp-client-session.vala
index 8ae3093a..e17a13f0 100644
--- a/src/engine/smtp/smtp-client-session.vala
+++ b/src/engine/smtp/smtp-client-session.vala
@@ -7,51 +7,51 @@
public class Geary.Smtp.ClientSession {
private ClientConnection cx;
private bool rset_required = false;
-
+
public virtual signal void connected(Greeting greeting) {
}
-
+
public virtual signal void authenticated(Authenticator authenticator) {
}
-
+
public virtual signal void disconnected() {
}
-
+
public ClientSession(Geary.Endpoint endpoint) {
cx = new ClientConnection(endpoint);
}
-
+
protected virtual void notify_connected(Greeting greeting) {
connected(greeting);
}
-
+
protected virtual void notify_authenticated(Authenticator authenticator) {
authenticated(authenticator);
}
-
+
protected virtual void notify_disconnected() {
disconnected();
}
-
+
public async Greeting? login_async(Credentials? creds, Cancellable? cancellable = null) throws Error {
if (cx.is_connected())
throw new SmtpError.ALREADY_CONNECTED("Connection to %s already exists", to_string());
-
+
// Greet the SMTP server.
Greeting? greeting = yield cx.connect_async(cancellable);
if (greeting == null)
throw new SmtpError.ALREADY_CONNECTED("Connection to %s already exists", to_string());
yield cx.establish_connection_async(cancellable);
-
+
notify_connected(greeting);
-
+
// authenticate if credentials supplied (they should be if ESMTP is supported)
if (creds != null)
notify_authenticated(yield attempt_authentication_async(creds, cancellable));
-
+
return greeting;
}
-
+
// Returns authenticator used for successful authentication, otherwise throws exception
private async Authenticator attempt_authentication_async(Credentials creds, Cancellable? cancellable)
throws Error {
@@ -132,17 +132,17 @@ public class Geary.Smtp.ClientSession {
default:
assert_not_reached();
}
-
+
debug("[%s] Attempting %s authenticator", to_string(), authenticator.to_string());
-
+
Response response = yield cx.authenticate_async(authenticator, cancellable);
if (response.code.is_success_completed())
return authenticator;
} while (auth_order.size > 0);
-
+
throw new SmtpError.AUTHENTICATION_FAILED("Unable to authenticate with %s", to_string());
}
-
+
public async Response? logout_async(bool force, Cancellable? cancellable = null) throws Error {
Response? response = null;
try {
@@ -152,7 +152,7 @@ public class Geary.Smtp.ClientSession {
// catch because although error occurred, still attempt to close the connection
message("Unable to QUIT: %s", err.message);
}
-
+
try {
if (yield cx.disconnect_async(cancellable))
disconnected();
@@ -160,7 +160,7 @@ public class Geary.Smtp.ClientSession {
// again, catch error but still shut down
message("Unable to disconnect: %s", err2.message);
}
-
+
rset_required = false;
return response;
@@ -172,13 +172,13 @@ public class Geary.Smtp.ClientSession {
throws GLib.Error {
if (!cx.is_connected())
throw new SmtpError.NOT_CONNECTED("Not connected to %s", to_string());
-
+
// RSET if required
if (rset_required) {
Response rset_response = yield cx.transaction_async(new Request(Command.RSET), cancellable);
if (!rset_response.code.is_success_completed())
rset_response.throw_error("Unable to RSET");
-
+
rset_required = false;
}
@@ -187,18 +187,18 @@ public class Geary.Smtp.ClientSession {
Response response = yield cx.transaction_async(mail_request, cancellable);
if (!response.code.is_success_completed())
response.throw_error("\"%s\" failed".printf(mail_request.to_string()));
-
+
// at this point in the session state machine, a RSET is required to start a new
// transmission if this fails at any point
rset_required = true;
-
+
// RCPTs
Gee.List<RFC822.MailboxAddress>? addrlist = email.get_recipients();
if (addrlist == null || addrlist.size == 0)
throw new SmtpError.REQUIRED_FIELD("No recipients in message");
-
+
yield send_rcpts_async(addrlist, cancellable);
-
+
// DATA
Geary.RFC822.Message email_copy = new Geary.RFC822.Message.without_bcc(email);
response = yield cx.send_data_async(email_copy.get_network_buffer(true), true,
@@ -209,12 +209,12 @@ public class Geary.Smtp.ClientSession {
// if message was transmitted successfully, the state machine resets automatically
rset_required = false;
}
-
+
private async void send_rcpts_async(Gee.List<RFC822.MailboxAddress>? addrlist,
Cancellable? cancellable) throws Error {
if (addrlist == null)
return;
-
+
// TODO: Support mailbox groups
foreach (RFC822.MailboxAddress mailbox in addrlist) {
RcptRequest rcpt_request = new RcptRequest.plain(mailbox.address);
@@ -229,7 +229,7 @@ public class Geary.Smtp.ClientSession {
}
}
}
-
+
public string to_string() {
return cx.to_string();
}
diff --git a/src/engine/smtp/smtp-command.vala b/src/engine/smtp/smtp-command.vala
index 9761dacc..9a74bba6 100644
--- a/src/engine/smtp/smtp-command.vala
+++ b/src/engine/smtp/smtp-command.vala
@@ -16,36 +16,36 @@ public enum Geary.Smtp.Command {
RCPT,
DATA,
STARTTLS;
-
+
public string serialize() {
switch (this) {
case HELO:
return "helo";
-
+
case EHLO:
return "ehlo";
-
+
case QUIT:
return "quit";
-
+
case HELP:
return "help";
-
+
case NOOP:
return "noop";
-
+
case RSET:
return "rset";
-
+
case AUTH:
return "AUTH";
-
+
case MAIL:
return "mail";
-
+
case RCPT:
return "rcpt";
-
+
case DATA:
return "data";
@@ -56,36 +56,36 @@ public enum Geary.Smtp.Command {
assert_not_reached();
}
}
-
+
public static Command deserialize(string str) throws SmtpError {
switch (Ascii.strdown(str)) {
case "helo":
return HELO;
-
+
case "ehlo":
return EHLO;
-
+
case "quit":
return QUIT;
-
+
case "help":
return HELP;
-
+
case "noop":
return NOOP;
-
+
case "rset":
return RSET;
-
+
case "auth":
return AUTH;
-
+
case "mail":
return MAIL;
-
+
case "rcpt":
return RCPT;
-
+
case "data":
return DATA;
diff --git a/src/engine/smtp/smtp-greeting.vala b/src/engine/smtp/smtp-greeting.vala
index 676ef250..78ea7fa4 100644
--- a/src/engine/smtp/smtp-greeting.vala
+++ b/src/engine/smtp/smtp-greeting.vala
@@ -9,7 +9,7 @@ public class Geary.Smtp.Greeting : Response {
SMTP,
ESMTP,
UNSPECIFIED;
-
+
/**
* Returns an empty string if UNSPECIFIED.
*/
@@ -17,45 +17,45 @@ public class Geary.Smtp.Greeting : Response {
switch (this) {
case SMTP:
return "SMTP";
-
+
case ESMTP:
return "ESMTP";
-
+
default:
return "";
}
}
-
+
public static ServerFlavor deserialize(string str) {
switch (Ascii.strup(str)) {
case "SMTP":
return SMTP;
-
+
case "ESMTP":
return ESMTP;
-
+
default:
return UNSPECIFIED;
}
}
}
-
+
public string? domain { get; private set; default = null; }
public ServerFlavor flavor { get; private set; default = ServerFlavor.UNSPECIFIED; }
public string? message { get; private set; default = null; }
-
+
public Greeting(Gee.List<ResponseLine> lines) {
base (lines);
-
+
// tokenize first line explanation for domain, server flavor, and greeting message
if (!String.is_empty(first_line.explanation)) {
string[] tokens = first_line.explanation.substring(ResponseCode.STRLEN + 1, -1).split(" ");
int length = tokens.length;
int index = 0;
-
+
if (index < length)
domain = tokens[index++];
-
+
if (index < length) {
string f = tokens[index++];
flavor = ServerFlavor.deserialize(f);
@@ -64,7 +64,7 @@ public class Geary.Smtp.Greeting : Response {
message = f;
}
}
-
+
while (index < length) {
if (String.is_empty(message))
message = tokens[index++];
diff --git a/src/engine/smtp/smtp-login-authenticator.vala b/src/engine/smtp/smtp-login-authenticator.vala
index 8adcc43a..7b7405ab 100644
--- a/src/engine/smtp/smtp-login-authenticator.vala
+++ b/src/engine/smtp/smtp-login-authenticator.vala
@@ -16,19 +16,19 @@ public class Geary.Smtp.LoginAuthenticator : Geary.Smtp.Authenticator {
public LoginAuthenticator(Credentials credentials) {
base ("LOGIN", credentials);
}
-
+
public override Request initiate() {
return new Request(Command.AUTH, { "login" });
}
-
+
public override Memory.Buffer? challenge(int step, Response response) throws SmtpError {
switch (step) {
case 0:
return new Memory.StringBuffer(Base64.encode(credentials.user.data));
-
+
case 1:
return new Memory.StringBuffer(Base64.encode((credentials.token ?? "").data));
-
+
default:
return null;
}
diff --git a/src/engine/smtp/smtp-plain-authenticator.vala b/src/engine/smtp/smtp-plain-authenticator.vala
index 5435e352..a8603e92 100644
--- a/src/engine/smtp/smtp-plain-authenticator.vala
+++ b/src/engine/smtp/smtp-plain-authenticator.vala
@@ -12,27 +12,27 @@
public class Geary.Smtp.PlainAuthenticator : Geary.Smtp.Authenticator {
private static uint8[] nul = { '\0' };
-
+
public PlainAuthenticator(Credentials credentials) {
base ("PLAIN", credentials);
}
-
+
public override Request initiate() {
return new Request(Command.AUTH, { "PLAIN" });
}
-
+
public override Memory.Buffer? challenge(int step, Response response) throws SmtpError {
// only a single challenge is issued in PLAIN
if (step > 0)
return null;
-
+
Memory.GrowableBuffer growable = new Memory.GrowableBuffer();
// skip the "authorize" field, which we don't support
growable.append(nul);
growable.append(credentials.user.data);
growable.append(nul);
growable.append((credentials.token ?? "").data);
-
+
// convert to Base64
return new Memory.StringBuffer(Base64.encode(growable.get_bytes().get_data()));
}
diff --git a/src/engine/smtp/smtp-request.vala b/src/engine/smtp/smtp-request.vala
index 444ea7b5..cf09b9b7 100644
--- a/src/engine/smtp/smtp-request.vala
+++ b/src/engine/smtp/smtp-request.vala
@@ -7,29 +7,29 @@
public class Geary.Smtp.Request {
public Command cmd { get; private set; }
public string[]? args { get; private set; }
-
+
public Request(Command cmd, string[]? args = null) {
this.cmd = cmd;
this.args = args;
}
-
+
public string serialize() {
// fast-path
if (args == null || args.length == 0)
return cmd.serialize();
-
+
StringBuilder builder = new StringBuilder();
-
+
builder.append(cmd.serialize());
-
+
foreach (string arg in args) {
builder.append_c(' ');
builder.append(arg);
}
-
+
return builder.str;
}
-
+
public string to_string() {
return serialize();
}
@@ -39,7 +39,7 @@ public class Geary.Smtp.HeloRequest : Geary.Smtp.Request {
public HeloRequest(string domain) {
base (Command.HELO, { domain });
}
-
+
public HeloRequest.for_local_address(InetAddress local_addr) {
this ("[%s]".printf(local_addr.to_string()));
}
@@ -49,7 +49,7 @@ public class Geary.Smtp.EhloRequest : Geary.Smtp.Request {
public EhloRequest(string domain) {
base (Command.EHLO, { domain });
}
-
+
public EhloRequest.for_local_address(InetAddress local_addr) {
string prefix = (local_addr.family == SocketFamily.IPV6) ? "IPv6:" : "";
this ("[%s%s]".printf(prefix, local_addr.to_string()));
@@ -70,7 +70,7 @@ public class Geary.Smtp.RcptRequest : Geary.Smtp.Request {
public RcptRequest(Geary.RFC822.MailboxAddress to) {
base (Command.RCPT, { "to:%s".printf(to.to_address_display("<", ">")) });
}
-
+
public RcptRequest.plain(string addr) {
base (Command.RCPT, { "to:<%s>".printf(addr) });
}
diff --git a/src/engine/smtp/smtp-response-code.vala b/src/engine/smtp/smtp-response-code.vala
index 9a4fd6a0..cbacded5 100644
--- a/src/engine/smtp/smtp-response-code.vala
+++ b/src/engine/smtp/smtp-response-code.vala
@@ -6,14 +6,14 @@
public class Geary.Smtp.ResponseCode {
public const int STRLEN = 3;
-
+
public const int MIN = 100;
public const int MAX = 599;
-
+
public const string START_DATA_CODE = "354";
public const string STARTTLS_READY_CODE = "220";
public const string DENIED_CODE = "550";
-
+
public enum Status {
POSITIVE_PRELIMINARY = 1,
POSITIVE_COMPLETION = 2,
@@ -22,7 +22,7 @@ public class Geary.Smtp.ResponseCode {
PERMANENT_FAILURE = 5,
UNKNOWN = -1;
}
-
+
public enum Condition {
SYNTAX = 0,
ADDITIONAL_INFO = 1,
@@ -30,69 +30,69 @@ public class Geary.Smtp.ResponseCode {
MAIL_SYSTEM = 5,
UNKNOWN = -1
}
-
+
private string str;
-
+
public ResponseCode(string str) throws SmtpError {
// these two checks are sufficient to make sure the Status is valid, but not the Condition
if (str.length != STRLEN)
throw new SmtpError.PARSE_ERROR("Reply code wrong length: %s (%d)", str, str.length);
-
+
int as_int = int.parse(str);
if (as_int < MIN || as_int > MAX)
throw new SmtpError.PARSE_ERROR("Reply code out of range: %s", str);
-
+
this.str = str;
}
-
+
public Status get_status() {
int i = Ascii.digit_to_int(str[0]);
-
+
// This works because of the checks in the constructor; Condition can't be checked so
// easily
return (i != -1) ? (Status) i : Status.UNKNOWN;
}
-
+
public Condition get_condition() {
switch (Ascii.digit_to_int(str[1])) {
case Condition.SYNTAX:
return Condition.SYNTAX;
-
+
case Condition.ADDITIONAL_INFO:
return Condition.ADDITIONAL_INFO;
-
+
case Condition.COMM_CHANNEL:
return Condition.COMM_CHANNEL;
-
+
case Condition.MAIL_SYSTEM:
return Condition.MAIL_SYSTEM;
-
+
default:
return Condition.UNKNOWN;
}
}
-
+
public bool is_success_completed() {
return get_status() == Status.POSITIVE_COMPLETION;
}
-
+
public bool is_success_intermediate() {
switch (get_status()) {
case Status.POSITIVE_PRELIMINARY:
case Status.POSITIVE_INTERMEDIATE:
return true;
-
+
default:
return false;
}
}
-
+
public bool is_failure() {
switch (get_status()) {
case Status.PERMANENT_FAILURE:
case Status.TRANSIENT_NEGATIVE:
return true;
-
+
default:
return false;
}
@@ -105,11 +105,11 @@ public class Geary.Smtp.ResponseCode {
public bool is_starttls_ready() {
return str == STARTTLS_READY_CODE;
}
-
+
public bool is_denied() {
return str == DENIED_CODE;
}
-
+
/**
* Returns true for [@link Status.PERMANENT_FAILURE} {@link Condition.SYNTAX} errors.
*
@@ -122,11 +122,11 @@ public class Geary.Smtp.ResponseCode {
return get_status() == ResponseCode.Status.PERMANENT_FAILURE
&& get_condition() == ResponseCode.Condition.SYNTAX;
}
-
+
public string serialize() {
return str;
}
-
+
public string to_string() {
return str;
}
diff --git a/src/engine/smtp/smtp-response-line.vala b/src/engine/smtp/smtp-response-line.vala
index 468c3e03..79ab3ba4 100644
--- a/src/engine/smtp/smtp-response-line.vala
+++ b/src/engine/smtp/smtp-response-line.vala
@@ -7,17 +7,17 @@
public class Geary.Smtp.ResponseLine {
public const char CONTINUED_CHAR = '-';
public const char NOT_CONTINUED_CHAR = ' ';
-
+
public ResponseCode code { get; private set; }
public string? explanation { get; private set; }
public bool continued { get; private set; }
-
+
public ResponseLine(ResponseCode code, string? explanation, bool continued) {
this.code = code;
this.explanation = explanation;
this.continued = continued;
}
-
+
/**
* Converts a serialized line into something usable. The CRLF should *not* be included in
* the input.
@@ -26,7 +26,7 @@ public class Geary.Smtp.ResponseLine {
// the ResponseCode is mandatory
if (line.length < ResponseCode.STRLEN)
throw new SmtpError.PARSE_ERROR("Line too short: %s", line);
-
+
// Only one of two separators allowed, as well as no separator (which means no explanation
// and not continued)
string? explanation;
@@ -36,25 +36,25 @@ public class Geary.Smtp.ResponseLine {
explanation = line.substring(ResponseCode.STRLEN + 1, -1);
continued = false;
break;
-
+
case String.EOS:
explanation = null;
continued = false;
break;
-
+
case CONTINUED_CHAR:
explanation = explanation = line.substring(ResponseCode.STRLEN + 1, -1);
continued = true;
break;
-
+
default:
throw new SmtpError.PARSE_ERROR("Invalid response line separator: %s", line);
}
-
+
return new ResponseLine(new ResponseCode(line.substring(0, ResponseCode.STRLEN)),
explanation, continued);
}
-
+
/**
* Serializes the Reply into a line for transmission. Note that the CRLF is *not* included.
*/
@@ -64,7 +64,7 @@ public class Geary.Smtp.ResponseLine {
continued ? CONTINUED_CHAR : NOT_CONTINUED_CHAR,
explanation ?? "");
}
-
+
public string to_string() {
return serialize();
}
diff --git a/src/engine/smtp/smtp-response.vala b/src/engine/smtp/smtp-response.vala
index 717c0f37..0c293c4f 100644
--- a/src/engine/smtp/smtp-response.vala
+++ b/src/engine/smtp/smtp-response.vala
@@ -8,28 +8,28 @@ public class Geary.Smtp.Response {
public ResponseCode code { get; private set; }
public ResponseLine first_line { get; private set; }
public Gee.List<ResponseLine> lines { get; private set; }
-
+
public Response(Gee.List<ResponseLine> lines) {
assert(lines.size > 0);
-
+
code = lines[0].code;
first_line = lines[0];
this.lines = lines.read_only_view;
}
-
+
[NoReturn]
public void throw_error(string msg) throws SmtpError {
throw new SmtpError.SERVER_ERROR("%s: %s", msg, first_line.to_string());
}
-
+
public string to_string() {
StringBuilder builder = new StringBuilder();
-
+
foreach (ResponseLine line in lines) {
builder.append(line.to_string());
builder.append("\n");
}
-
+
return builder.str;
}
}
diff --git a/src/engine/state/state-machine-descriptor.vala b/src/engine/state/state-machine-descriptor.vala
index 940e9f1f..0e34f22e 100644
--- a/src/engine/state/state-machine-descriptor.vala
+++ b/src/engine/state/state-machine-descriptor.vala
@@ -11,10 +11,10 @@ public class Geary.State.MachineDescriptor : BaseObject {
public uint start_state { get; private set; }
public uint state_count { get; private set; }
public uint event_count { get; private set; }
-
+
private unowned StateEventToString? state_to_string;
private unowned StateEventToString? event_to_string;
-
+
public MachineDescriptor(string name, uint start_state, uint state_count, uint event_count,
StateEventToString? state_to_string, StateEventToString? event_to_string) {
this.name = name;
@@ -23,15 +23,15 @@ public class Geary.State.MachineDescriptor : BaseObject {
this.event_count = event_count;
this.state_to_string = state_to_string;
this.event_to_string = event_to_string;
-
+
// starting state should be valid
assert(start_state < state_count);
}
-
+
public string get_state_string(uint state) {
return (state_to_string != null) ? state_to_string(state) : "%s STATE %u".printf(name, state);
}
-
+
public string get_event_string(uint event) {
return (event_to_string != null) ? event_to_string(event) : "%s EVENT %u".printf(name, event);
}
diff --git a/src/engine/state/state-machine.vala b/src/engine/state/state-machine.vala
index b88a4d06..351babb8 100644
--- a/src/engine/state/state-machine.vala
+++ b/src/engine/state/state-machine.vala
@@ -16,19 +16,19 @@ public class Geary.State.Machine : BaseObject {
private void *post_user = null;
private Object? post_object = null;
private Error? post_err = null;
-
+
public Machine(MachineDescriptor descriptor, Mapping[] mappings, Transition? default_transition) {
this.descriptor = descriptor;
this.default_transition = default_transition;
-
+
// verify that each state and event in the mappings are valid
foreach (Mapping mapping in mappings) {
assert(mapping.state < descriptor.state_count);
assert(mapping.event < descriptor.event_count);
}
-
+
state = descriptor.start_state;
-
+
// build a transition map with state/event IDs (i.e. offsets) pointing directly into the
// map
transitions = new Mapping[descriptor.state_count, descriptor.event_count];
@@ -38,46 +38,46 @@ public class Geary.State.Machine : BaseObject {
transitions[mapping.state, mapping.event] = mapping;
}
}
-
+
public uint get_state() {
return state;
}
-
+
public bool get_abort_on_no_transition() {
return abort_on_no_transition;
}
-
+
public void set_abort_on_no_transition(bool abort) {
abort_on_no_transition = abort;
}
-
+
public void set_logging(bool logging) {
this.logging = logging;
}
-
+
public bool is_logging() {
return logging;
}
-
+
public uint issue(uint event, void *user = null, Object? object = null, Error? err = null) {
assert(event < descriptor.event_count);
assert(state < descriptor.state_count);
-
+
unowned Mapping? mapping = transitions[state, event];
-
+
unowned Transition? transition = (mapping != null) ? mapping.transition : default_transition;
if (transition == null) {
string msg = "%s: No transition defined for %s@%s".printf(to_string(),
descriptor.get_event_string(event), descriptor.get_state_string(state));
-
+
if (get_abort_on_no_transition())
error(msg);
else
critical(msg);
-
+
return state;
}
-
+
// guard against reentrancy ... don't want to use a non-reentrant lock because then
// the machine will simply hang; assertion is better to ferret out design flaws
if (locked) {
@@ -85,20 +85,20 @@ public class Geary.State.Machine : BaseObject {
get_event_issued_string(state, event));
}
locked = true;
-
+
uint old_state = state;
state = transition(state, event, user, object, err);
assert(state < descriptor.state_count);
-
+
if (!locked) {
error("Exited transition to unlocked state machine %s: %s", descriptor.name,
get_transition_string(old_state, event, state));
}
locked = false;
-
+
if (is_logging())
message("%s: %s", to_string(), get_transition_string(old_state, event, state));
-
+
// Perform post-transition if registered
if (post_transition != null) {
// clear post-transition before calling, in case this method is re-entered
@@ -106,18 +106,18 @@ public class Geary.State.Machine : BaseObject {
void* perform_user = post_user;
Object? perform_object = post_object;
Error? perform_err = post_err;
-
+
post_transition = null;
post_user = null;
post_object = null;
post_err = null;
-
+
perform(perform_user, perform_object, perform_err);
}
-
+
return state;
}
-
+
// Must be used carefully: this allows for a delegate (callback) to be called after the
// *current* transition has occurred; thus, this method can *only* be called from within
// a TransitionHandler.
@@ -131,35 +131,35 @@ public class Geary.State.Machine : BaseObject {
Object? object = null, Error? err = null) {
if (!locked) {
warning("%s: Attempt to register post-transition while machine is unlocked", to_string());
-
+
return false;
}
-
+
this.post_transition = post_transition;
post_user = user;
post_object = object;
post_err = err;
-
+
return true;
}
-
+
public string get_state_string(uint state) {
return descriptor.get_state_string(state);
}
-
+
public string get_event_string(uint event) {
return descriptor.get_event_string(event);
}
-
+
public string get_event_issued_string(uint state, uint event) {
return "%s@%s".printf(descriptor.get_state_string(state), descriptor.get_event_string(event));
}
-
+
public string get_transition_string(uint old_state, uint event, uint new_state) {
return "%s@%s -> %s".printf(descriptor.get_state_string(old_state),
descriptor.get_event_string(event),
descriptor.get_state_string(new_state));
}
-
+
public string to_string() {
return "Machine %s [%s]".printf(descriptor.name, descriptor.get_state_string(state));
}
diff --git a/src/engine/state/state-mapping.vala b/src/engine/state/state-mapping.vala
index f4b2ce77..66fcee24 100644
--- a/src/engine/state/state-mapping.vala
+++ b/src/engine/state/state-mapping.vala
@@ -14,7 +14,7 @@ public class Geary.State.Mapping : BaseObject {
public uint state;
public uint event;
public unowned Transition transition;
-
+
public Mapping(uint state, uint event, Transition transition) {
this.state = state;
this.event = event;
diff --git a/src/engine/util/util-ascii.vala b/src/engine/util/util-ascii.vala
index 4c803fed..13ab7b7c 100644
--- a/src/engine/util/util-ascii.vala
+++ b/src/engine/util/util-ascii.vala
@@ -50,7 +50,7 @@ public int last_index_of(string str, char ch) {
public bool get_next_char(string str, ref int index, out char ch) {
ch = str[index++];
-
+
return ch != String.EOS;
}
@@ -73,11 +73,11 @@ public inline bool stri_equal(string a, string b) {
public bool nullable_stri_equal(string? a, string? b) {
if (a == null)
return (b == null);
-
+
// a != null, so always false
if (b == null)
return false;
-
+
return stri_equal(a, b);
}
@@ -111,16 +111,16 @@ public bool is_numeric(string str) {
char *strptr = str;
for (;;) {
char ch = *strptr++;
-
+
if (ch == String.EOS)
break;
-
+
if (ch.isdigit())
numeric_found = true;
else if (!ch.isspace())
return false;
}
-
+
return numeric_found;
}
diff --git a/src/engine/util/util-collection.vala b/src/engine/util/util-collection.vala
index 8d750c0b..0ba4e09e 100644
--- a/src/engine/util/util-collection.vala
+++ b/src/engine/util/util-collection.vala
@@ -36,7 +36,7 @@ public Gee.ArrayList<G> array_list_wrap<G>(G[] a, owned Gee.EqualDataFunc<G>? eq
public Gee.ArrayList<G> to_array_list<G>(Gee.Collection<G> c) {
Gee.ArrayList<G> list = new Gee.ArrayList<G>();
list.add_all(c);
-
+
return list;
}
@@ -55,7 +55,7 @@ public void add_all_array<G>(Gee.Collection<G> c, G[] ar) {
public G? get_first<G>(Gee.Collection<G> c) {
Gee.Iterator<G> iter = c.iterator();
-
+
return iter.next() ? iter.get() : null;
}
@@ -70,19 +70,19 @@ public G? find_first<G>(Gee.Collection<G> c, owned Gee.Predicate<G> pred) {
if (pred(iter.get()))
return iter.get();
}
-
+
return null;
}
public bool are_sets_equal<G>(Gee.Set<G> a, Gee.Set<G> b) {
if (a.size != b.size)
return false;
-
+
foreach (G element in a) {
if (!b.contains(element))
return false;
}
-
+
return true;
}
@@ -97,7 +97,7 @@ public Gee.Collection<G> remove_if<G>(Gee.Collection<G> c, owned Gee.Predicate<G
if (pred(iter.get()))
iter.remove();
}
-
+
return c;
}
@@ -134,7 +134,7 @@ public Gee.MultiMap<V, K> reverse_multi_map<K, V>(Gee.MultiMap<K, V> map) {
foreach (V value in map.get(key))
reverse.set(value, key);
}
-
+
return reverse;
}
@@ -158,7 +158,7 @@ public inline uint int64_hash_func(int64? n) {
public bool int64_equal_func(int64? a, int64? b) {
int64 *bia = (int64 *) a;
int64 *bib = (int64 *) b;
-
+
return (*bia) == (*bib);
}
@@ -168,14 +168,14 @@ public bool int64_equal_func(int64? a, int64? b) {
public uint hash_memory(void *ptr, size_t bytes) {
if (ptr == null || bytes == 0)
return 0;
-
+
uint8 *u8 = (uint8 *) ptr;
-
+
// initialize hash to first byte value and then rotate-XOR from there
uint hash = *u8;
for (int ctr = 1; ctr < bytes; ctr++)
hash = (hash << 4) ^ (hash >> 28) ^ (*u8++);
-
+
return hash;
}
@@ -189,19 +189,19 @@ public uint hash_memory(void *ptr, size_t bytes) {
*/
public uint hash_memory_stream(void *ptr, uint8 terminator, ByteTransformer? cb) {
uint8 *u8 = (uint8 *) ptr;
-
+
uint hash = 0;
for (;;) {
uint8 b = *u8++;
if (b == terminator)
break;
-
+
if (cb != null)
b = cb(b);
-
+
hash = (hash << 4) ^ (hash >> 28) ^ b;
}
-
+
return hash;
}
diff --git a/src/engine/util/util-files.vala b/src/engine/util/util-files.vala
index 386d0f25..836ed527 100644
--- a/src/engine/util/util-files.vala
+++ b/src/engine/util/util-files.vala
@@ -23,11 +23,11 @@ public async void recursive_delete_async(GLib.File folder,
file_type = yield query_file_type_async(folder, true, cancellable);
} catch (Error err) {
debug("Unable to get file type of %s: %s", folder.get_path(), err.message);
-
+
if (err is IOError.CANCELLED)
return;
}
-
+
if (file_type == FileType.DIRECTORY) {
FileEnumerator? enumerator = null;
try {
@@ -40,7 +40,7 @@ public async void recursive_delete_async(GLib.File folder,
} catch (Error e) {
debug("Error enumerating files for deletion: %s", e.message);
}
-
+
// Iterate the enumerated files in batches.
if (enumerator != null) {
try {
@@ -64,7 +64,7 @@ public async void recursive_delete_async(GLib.File folder,
}
} catch (Error e) {
debug("Error enumerating batch of files: %s", e.message);
-
+
if (e is IOError.CANCELLED)
return;
}
@@ -91,7 +91,7 @@ public async bool query_exists_async(File file, Cancellable? cancellable = null)
else
throw err;
}
-
+
// exists if got this far
return true;
}
@@ -104,7 +104,7 @@ public async FileType query_file_type_async(File file, bool follow_symlinks, Can
FileInfo info = yield file.query_info_async("standard::type",
follow_symlinks ? FileQueryInfoFlags.NONE : FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
Priority.DEFAULT, cancellable);
-
+
return info.get_file_type();
}
@@ -152,10 +152,10 @@ public uint nullable_hash(File? file) {
public bool nullable_equal(File? a, File? b) {
if (a == null && b == null)
return true;
-
+
if (a == null || b == null)
return false;
-
+
return a.equal(b);
}
diff --git a/src/engine/util/util-generic-capabilities.vala b/src/engine/util/util-generic-capabilities.vala
index 77d49841..b4b48ae4 100644
--- a/src/engine/util/util-generic-capabilities.vala
+++ b/src/engine/util/util-generic-capabilities.vala
@@ -11,34 +11,34 @@
public class Geary.GenericCapabilities : BaseObject {
public string name_separator { get; private set; }
public string? value_separator { get; private set; }
-
+
// All params must be nullable to support both libgee 0.8.0 and 0.8.6 (for Quantal and Rarring,
respectively.)
// This behavior was changed in the following libgee commit:
// https://git.gnome.org/browse/libgee/commit/?id=5a35303cb04154d0e929a7d8895d4a4812ba7a1c
private Gee.HashMultiMap<string, string?> map = new Gee.HashMultiMap<string, string?>(
Ascii.nullable_stri_hash, Ascii.nullable_stri_equal, Ascii.nullable_stri_hash,
Ascii.nullable_stri_equal);
-
+
/**
* Creates an empty set of capabilities.
*/
public GenericCapabilities(string name_separator, string? value_separator) {
assert(!String.is_empty(name_separator));
-
+
this.name_separator = name_separator;
this.value_separator = !String.is_empty(value_separator) ? value_separator : null;
}
-
+
public bool is_empty() {
return (map.size == 0);
}
-
+
public bool parse_and_add_capability(string text) {
string[] name_values = text.split(name_separator, 2);
switch (name_values.length) {
case 1:
add_capability(name_values[0]);
break;
-
+
case 2:
if (value_separator == null) {
add_capability(name_values[0], name_values[1]);
@@ -53,25 +53,25 @@ public class Geary.GenericCapabilities : BaseObject {
}
}
break;
-
+
default:
return false;
}
-
+
return true;
}
-
+
public void add_capability(string name, string? setting = null) {
map.set(name, String.is_empty(setting) ? null : setting);
}
-
+
/**
* Returns true only if the capability was named as available by the server.
*/
public bool has_capability(string name) {
return map.contains(name);
}
-
+
/**
* Returns true only if the capability and the associated setting were both named as available
* by the server.
@@ -79,13 +79,13 @@ public class Geary.GenericCapabilities : BaseObject {
public bool has_setting(string name, string? setting) {
if (!map.contains(name))
return false;
-
+
if (String.is_empty(setting))
return true;
-
+
return map.get(name).contains(setting);
}
-
+
/**
* Returns null if either the capability is available but has no associated settings, or if the
* capability is not available. Thus, use has_capability() to determine if available, then
@@ -93,28 +93,28 @@ public class Geary.GenericCapabilities : BaseObject {
*/
public Gee.Collection<string>? get_settings(string name) {
Gee.Collection<string> settings = map.get(name);
-
+
return (settings.size > 0) ? settings : null;
}
-
+
public Gee.Set<string>? get_all_names() {
Gee.Set<string> names = map.get_keys();
-
+
return (names.size > 0) ? names : null;
}
-
+
private void append(StringBuilder builder, string text) {
if (!String.is_empty(builder.str))
builder.append(String.is_empty(value_separator) ? " " : value_separator);
-
+
builder.append(text);
}
-
+
public virtual string to_string() {
Gee.Set<string>? names = get_all_names();
if (names == null || names.size == 0)
return "";
-
+
StringBuilder builder = new StringBuilder();
foreach (string name in names) {
Gee.Collection<string>? settings = get_settings(name);
@@ -129,7 +129,7 @@ public class Geary.GenericCapabilities : BaseObject {
}
}
}
-
+
return builder.str;
}
}
diff --git a/src/engine/util/util-imap-utf7.vala b/src/engine/util/util-imap-utf7.vala
index 72ee8f38..bd9a4239 100644
--- a/src/engine/util/util-imap-utf7.vala
+++ b/src/engine/util/util-imap-utf7.vala
@@ -100,7 +100,7 @@ public string utf8_to_imap_utf7(string str) {
/* no characters that need to be encoded */
return str;
}
-
+
/* at least one encoded character */
StringBuilder dest = new StringBuilder();
dest.append_len(str, p);
@@ -115,7 +115,7 @@ public string utf8_to_imap_utf7(string str) {
p++;
continue;
}
-
+
uint8[] utf16 = {};
while ((uint8) str[p] >= 0x80) {
int next_p = p;
@@ -143,7 +143,7 @@ public string utf8_to_imap_utf7(string str) {
private void utf16buf_to_utf8(StringBuilder dest, uint8[] output, ref int pos, int len) throws ConvertError {
if (len % 2 != 0)
throw new ConvertError.ILLEGAL_SEQUENCE("Odd number of bytes in UTF-16 data");
-
+
uint16 high = (output[pos % 4] << 8) | output[(pos+1) % 4];
if (high < UTF16_SURROGATE_HIGH_FIRST ||
high > UTF16_SURROGATE_HIGH_MAX) {
@@ -155,18 +155,18 @@ private void utf16buf_to_utf8(StringBuilder dest, uint8[] output, ref int pos, i
pos = (pos + 2) % 4;
return;
}
-
+
if (high > UTF16_SURROGATE_HIGH_LAST)
throw new ConvertError.ILLEGAL_SEQUENCE("UTF-16 data out of range");
if (len != 4) {
/* missing the second character */
throw new ConvertError.ILLEGAL_SEQUENCE("Truncated UTF-16 data");
}
-
+
uint16 low = (output[(pos+2)%4] << 8) | output[(pos+3) % 4];
if (low < UTF16_SURROGATE_LOW_FIRST || low > UTF16_SURROGATE_LOW_LAST)
throw new ConvertError.ILLEGAL_SEQUENCE("Illegal UTF-16 surrogate");
-
+
unichar chr = UTF16_SURROGATE_BASE +
(((high & UTF16_SURROGATE_MASK) << UTF16_SURROGATE_SHIFT) |
(low & UTF16_SURROGATE_MASK));
@@ -179,52 +179,52 @@ private void utf16buf_to_utf8(StringBuilder dest, uint8[] output, ref int pos, i
private void mbase64_decode_to_utf8(StringBuilder dest, string str, ref int p) throws ConvertError {
uint8 input[4], output[4];
int outstart = 0, outpos = 0;
-
+
while (str[p] != '-') {
input[0] = imap_b64dec[(uint8) str[p + 0]];
input[1] = imap_b64dec[(uint8) str[p + 1]];
if (input[0] == 0xff || input[1] == 0xff)
throw new ConvertError.ILLEGAL_SEQUENCE("Illegal character in IMAP base-64 encoded sequence");
-
+
output[outpos % 4] = (input[0] << 2) | (input[1] >> 4);
if (++outpos % 4 == outstart) {
utf16buf_to_utf8(dest, output, ref outstart, 4);
}
-
+
input[2] = imap_b64dec[(uint8) str[p + 2]];
if (input[2] == 0xff) {
if (str[p + 2] != '-')
throw new ConvertError.ILLEGAL_SEQUENCE("Illegal character in IMAP base-64 encoded
sequence");
-
+
p += 2;
break;
}
-
+
output[outpos % 4] = (input[1] << 4) | (input[2] >> 2);
if (++outpos % 4 == outstart) {
utf16buf_to_utf8(dest, output, ref outstart, 4);
}
-
+
input[3] = imap_b64dec[(uint8) str[p + 3]];
if (input[3] == 0xff) {
if (str[p + 3] != '-')
throw new ConvertError.ILLEGAL_SEQUENCE("Illegal character in IMAP base-64 encoded
sequence");
-
+
p += 3;
break;
}
-
+
output[outpos % 4] = ((input[2] << 6) & 0xc0) | input[3];
if (++outpos % 4 == outstart) {
utf16buf_to_utf8(dest, output, ref outstart, 4);
}
-
+
p += 4;
}
if (outstart != outpos % 4) {
utf16buf_to_utf8(dest, output, ref outstart, (4 + outpos - outstart) % 4);
}
-
+
/* found ending '-' */
p++;
}
@@ -243,7 +243,7 @@ public string imap_utf7_to_utf8(string str) throws ConvertError {
/* 8bit characters - the input is broken */
throw new ConvertError.ILLEGAL_SEQUENCE("IMAP UTF-7 input string contains 8-bit data");
}
-
+
/* at least one encoded character */
StringBuilder dest = new StringBuilder();
dest.append_len(str, p);
diff --git a/src/engine/util/util-iterable.vala b/src/engine/util/util-iterable.vala
index 2cc6a027..2cff4b16 100644
--- a/src/engine/util/util-iterable.vala
+++ b/src/engine/util/util-iterable.vala
@@ -11,7 +11,7 @@ namespace Geary {
public Geary.Iterable<G> traverse<G>(Gee.Iterable<G> i) {
return new Geary.Iterable<G>(i.iterator());
}
-
+
/**
* Take some non-null items (all must be of type G) and return a
* Geary.Iterable for convenience.
@@ -19,15 +19,15 @@ namespace Geary {
public Geary.Iterable<G> iterate<G>(G g, ...) {
va_list args = va_list();
G arg = g;
-
+
Gee.ArrayList<G> list = new Gee.ArrayList<G>();
do {
list.add(arg);
} while((arg = args.arg()) != null);
-
+
return Geary.traverse<G>(list);
}
-
+
/**
* Take an array of items and return a Geary.Iterable for convenience.
*/
@@ -53,15 +53,15 @@ public class Geary.Iterable<G> : BaseObject {
*/
private class GeeIterable<G> : Gee.Traversable<G>, Gee.Iterable<G>, BaseObject {
private Gee.Iterator<G> i;
-
+
public GeeIterable(Gee.Iterator<G> iterator) {
i = iterator;
}
-
+
public Gee.Iterator<G> iterator() {
return i;
}
-
+
// Unfortunately necessary for Gee.Traversable.
public virtual bool @foreach(Gee.ForallFunc<G> f) {
foreach (G g in this) {
@@ -218,7 +218,7 @@ public class Geary.Iterable<G> : BaseObject {
public Gee.ArrayList<G> to_array_list(owned Gee.EqualDataFunc<G>? equal_func = null) {
return (Gee.ArrayList<G>) add_all_to(new Gee.ArrayList<G>((owned) equal_func));
}
-
+
/** Returns a new linked list containing all elements. */
public Gee.LinkedList<G> to_linked_list(owned Gee.EqualDataFunc<G>? equal_func = null) {
return (Gee.LinkedList<G>) add_all_to(new Gee.LinkedList<G>((owned) equal_func));
diff --git a/src/engine/util/util-object.vala b/src/engine/util/util-object.vala
index c45b9abd..2bbc0dd8 100644
--- a/src/engine/util/util-object.vala
+++ b/src/engine/util/util-object.vala
@@ -9,7 +9,7 @@ namespace Geary.ObjectUtils {
/**
* Creates a set of property bindings from source to dest with the given binding flags.
*/
-public Gee.List<Binding>? mirror_properties(Object source, Object dest, BindingFlags
+public Gee.List<Binding>? mirror_properties(Object source, Object dest, BindingFlags
flags = GLib.BindingFlags.DEFAULT | GLib.BindingFlags.SYNC_CREATE) {
// Make sets of both object's properties.
Gee.HashSet<ParamSpec> source_properties = new Gee.HashSet<ParamSpec>();
@@ -18,17 +18,17 @@ public Gee.List<Binding>? mirror_properties(Object source, Object dest, BindingF
Gee.HashSet<ParamSpec> dest_properties = new Gee.HashSet<ParamSpec>();
Collection.add_all_array<ParamSpec>(dest_properties,
dest.get_class().list_properties());
-
+
// Remove properties from source_properties that are not in both sets.
source_properties.retain_all(dest_properties);
-
+
// Create all bindings.
Gee.List<Binding> bindings = new Gee.ArrayList<Binding>();
foreach(ParamSpec ps in source_properties) {
if ((ps.flags & ParamFlags.WRITABLE) != 0)
bindings.add(source.bind_property(ps.name, dest, ps.name, flags));
}
-
+
return bindings.size > 0 ? bindings : null;
}
@@ -38,7 +38,7 @@ public Gee.List<Binding>? mirror_properties(Object source, Object dest, BindingF
public void unmirror_properties(Gee.List<Binding> bindings) {
foreach(Binding b in bindings)
b.unref();
-
+
bindings.clear();
}
diff --git a/src/engine/util/util-reference-semantics.vala b/src/engine/util/util-reference-semantics.vala
index 56b3ed90..05093207 100644
--- a/src/engine/util/util-reference-semantics.vala
+++ b/src/engine/util/util-reference-semantics.vala
@@ -36,30 +36,30 @@
*/
public interface Geary.ReferenceSemantics : BaseObject {
protected abstract int manual_ref_count { get; protected set; }
-
+
/**
* A ReferenceSemantics object can fire this signal for force all SmartReferences to drop their
* reference to it.
*/
public signal void release_now();
-
+
/**
* This signal is fired when all SmartReferences to the ReferenceSemantics object have dropped
* their reference.
*/
public signal void freed();
-
+
internal void claim() {
manual_ref_count++;
}
-
+
internal void release() {
assert(manual_ref_count > 0);
-
+
if (--manual_ref_count == 0)
freed();
}
-
+
public bool is_freed() {
return (manual_ref_count == 0);
}
@@ -71,7 +71,7 @@ public interface Geary.ReferenceSemantics : BaseObject {
*/
public abstract class Geary.SmartReference : BaseObject {
private ReferenceSemantics? reffed;
-
+
/**
* This signal is fired when the SmartReference drops its reference to a ReferenceSemantics
* object due to it firing "release-now".
@@ -80,28 +80,28 @@ public abstract class Geary.SmartReference : BaseObject {
*/
public virtual signal void reference_broken() {
}
-
+
public SmartReference(ReferenceSemantics reffed) {
this.reffed = reffed;
-
+
reffed.release_now.connect(on_release_now);
-
+
reffed.claim();
}
-
+
~SmartReference() {
if (reffed != null)
reffed.release();
}
-
+
public ReferenceSemantics? get_reference() {
return reffed;
}
-
+
private void on_release_now() {
reffed.release();
reffed = null;
-
+
reference_broken();
}
}
diff --git a/src/engine/util/util-scheduler.vala b/src/engine/util/util-scheduler.vala
index a50ad8d3..aa0a0142 100644
--- a/src/engine/util/util-scheduler.vala
+++ b/src/engine/util/util-scheduler.vala
@@ -16,60 +16,60 @@ private Gee.HashSet<ScheduledInstance>? scheduled_map = null;
private class ScheduledInstance : BaseObject, Geary.ReferenceSemantics {
protected int manual_ref_count { get; protected set; }
-
+
private unowned SourceFunc? cb;
private uint sched_id;
-
+
// Can't rely on ReferenceSemantic's "freed" signal because it's possible all the SmartReferences
// have been dropped but the callback is still pending. This signal is fired when all references
// are dropped and the callback is not pending or has been cancelled.
public signal void dead();
-
+
public ScheduledInstance.on_idle(SourceFunc cb, int priority) {
this.cb = cb;
sched_id = Idle.add(on_callback, priority);
-
+
freed.connect(on_freed);
}
-
+
public ScheduledInstance.after_msec(uint msec, SourceFunc cb, int priority) {
this.cb = cb;
sched_id = Timeout.add(msec, on_callback, priority);
-
+
freed.connect(on_freed);
}
-
+
public ScheduledInstance.after_sec(uint sec, SourceFunc cb, int priority) {
this.cb = cb;
sched_id = Timeout.add_seconds(sec, on_callback, priority);
-
+
freed.connect(on_freed);
}
-
+
public void cancel() {
if (sched_id == 0)
return;
-
+
// cancel callback
Source.remove(sched_id);
-
+
// mark as cancelled
cb = null;
sched_id = 0;
-
+
// tell SmartReferences to drop their refs
// (this in turn will call "freed", firing the "dead" signal)
release_now();
}
-
+
private bool on_callback() {
bool again = (cb != null) ? cb() : false;
-
+
if (!again) {
// mark as cancelled
cb = null;
sched_id = 0;
-
+
// tell the SmartReferences to drop their refs
// (this in turn will call "freed", firing the "dead" signal, unless all refs were
// released earlier and the callback was pending, so fire "dead" now)
@@ -78,10 +78,10 @@ private class ScheduledInstance : BaseObject, Geary.ReferenceSemantics {
else
release_now();
}
-
+
return again;
}
-
+
private void on_freed() {
// only fire "dead" if marked as cancelled, otherwise wait until callback completes
if (sched_id == 0)
@@ -93,7 +93,7 @@ public class Scheduled : Geary.SmartReference {
internal Scheduled(ScheduledInstance instance) {
base (instance);
}
-
+
public void cancel() {
ScheduledInstance? instance = get_reference() as ScheduledInstance;
if (instance != null)
@@ -115,18 +115,18 @@ public Scheduled after_sec(uint sec, SourceFunc cb, int priority = GLib.Priority
private Scheduled schedule_instance(ScheduledInstance inst) {
inst.dead.connect(on_scheduled_dead);
-
+
if (scheduled_map == null)
scheduled_map = new Gee.HashSet<ScheduledInstance>();
-
+
scheduled_map.add(inst);
-
+
return new Scheduled(inst);
}
private void on_scheduled_dead(ScheduledInstance inst) {
inst.dead.disconnect(on_scheduled_dead);
-
+
bool removed = scheduled_map.remove(inst);
assert(removed);
}
diff --git a/src/engine/util/util-string.vala b/src/engine/util/util-string.vala
index 0a2ef833..6b4cdfe2 100644
--- a/src/engine/util/util-string.vala
+++ b/src/engine/util/util-string.vala
@@ -39,7 +39,7 @@ public bool contains_any_char(string str, unichar[] chars) {
if (ch in chars)
return true;
}
-
+
return false;
}
@@ -81,7 +81,7 @@ public string reduce_whitespace(string? str) {
public string safe_byte_substring(string s, ssize_t max_length) {
if (s.length < max_length)
return s;
-
+
return glib_substring(s, 0, s.char_count(max_length));
}
diff --git a/src/engine/util/util-synchronization.vala b/src/engine/util/util-synchronization.vala
index 9b75a424..252804d5 100644
--- a/src/engine/util/util-synchronization.vala
+++ b/src/engine/util/util-synchronization.vala
@@ -15,13 +15,13 @@ namespace Geary.Synchronization {
public class SpinWaiter : BaseObject {
public delegate bool PollService();
-
+
private int poll_msec;
private PollService cb;
private Mutex mutex = Mutex();
private Cond cond = Cond();
private bool notified = false;
-
+
/**
* Create a {@link SpinWaiter}.
*
@@ -50,15 +50,15 @@ public class SpinWaiter : BaseObject {
public bool wait(Cancellable? cancellable = null) throws Error {
// normalize poll_msec; negative values are zeroed
int64 actual_poll_msec = Numeric.int64_floor(0, poll_msec);
-
+
bool result;
-
+
mutex.lock();
-
+
while (!notified) {
if (cancellable != null && cancellable.is_cancelled())
break;
-
+
int64 end_time = get_monotonic_time() + (actual_poll_msec * TimeSpan.MILLISECOND);
if (!cond.wait_until(mutex, end_time)) {
// timeout passed, allow the callback to run
@@ -66,23 +66,23 @@ public class SpinWaiter : BaseObject {
if (!cb()) {
// PollService returned false, abort
mutex.lock();
-
+
break;
}
mutex.lock();
}
}
-
+
result = notified;
-
+
mutex.unlock();
-
+
if (cancellable.is_cancelled())
throw new IOError.CANCELLED("SpinWaiter.wait cancelled");
-
+
return result;
}
-
+
/**
* Signals a completion state to a thread calling {@link wait}.
*
@@ -91,13 +91,13 @@ public class SpinWaiter : BaseObject {
*/
public new void notify() {
mutex.lock();
-
+
notified = true;
cond.broadcast();
-
+
mutex.unlock();
}
-
+
/**
* Indicates if the {@link SpinWaiter} has been notified.
*
@@ -110,13 +110,13 @@ public class SpinWaiter : BaseObject {
*/
public bool is_notified() {
bool result;
-
+
mutex.lock();
-
+
result = notified;
-
+
mutex.unlock();
-
+
return result;
}
}
diff --git a/src/engine/util/util-time.vala b/src/engine/util/util-time.vala
index cc9b6bd1..ea4e7be5 100644
--- a/src/engine/util/util-time.vala
+++ b/src/engine/util/util-time.vala
@@ -23,7 +23,7 @@ public time_t datetime_to_time_t(DateTime datetime) {
// Time's year is number of years after 1900
tm.year = Numeric.int_floor(datetime.get_year() - 1900, 0);
tm.isdst = datetime.is_daylight_savings() ? 1 : 0;
-
+
return tm.mktime();
}
diff --git a/src/engine/util/util-trillian.vala b/src/engine/util/util-trillian.vala
index 320ec5af..1f2abbf5 100644
--- a/src/engine/util/util-trillian.vala
+++ b/src/engine/util/util-trillian.vala
@@ -13,71 +13,71 @@ public enum Geary.Trillian {
UNKNOWN = -1,
FALSE = 0,
TRUE = 1;
-
+
public bool to_boolean(bool if_unknown) {
switch (this) {
case UNKNOWN:
return if_unknown;
-
+
case FALSE:
return false;
-
+
case TRUE:
return true;
-
+
default:
assert_not_reached();
}
}
-
+
public inline static Trillian from_boolean(bool b) {
return b ? TRUE : FALSE;
}
-
+
public inline int to_int() {
return (int) this;
}
-
+
public static Trillian from_int(int i) {
switch (i) {
case 0:
return FALSE;
-
+
case 1:
return TRUE;
-
+
default:
return UNKNOWN;
}
}
-
+
public inline bool is_certain() {
return this == TRUE;
}
-
+
public inline bool is_uncertain() {
return this != TRUE;
}
-
+
public inline bool is_possible() {
return this != FALSE;
}
-
+
public inline bool is_impossible() {
return this == FALSE;
}
-
+
public string to_string() {
switch (this) {
case UNKNOWN:
return "unknown";
-
+
case FALSE:
return "false";
-
+
case TRUE:
return "true";
-
+
default:
assert_not_reached();
}
diff --git a/src/mailer/main.vala b/src/mailer/main.vala
index bfd97277..e91d20e2 100644
--- a/src/mailer/main.vala
+++ b/src/mailer/main.vala
@@ -12,13 +12,13 @@ Geary.ComposedEmail? composed_email = null;
async void main_async() throws Error {
Geary.Smtp.ClientSession session = new Geary.Smtp.ClientSession(endpoint);
-
+
Geary.Smtp.Greeting? greeting = yield session.login_async(credentials);
stdout.printf("%s\n", greeting.to_string());
-
+
for (int ctr = 0; ctr < arg_count; ctr++) {
Geary.RFC822.Message msg;
-
+
if (arg_full_file != null) {
debug("%s", arg_full_file);
msg = new Geary.RFC822.Message.from_buffer(new Geary.Memory.FileBuffer(
@@ -26,33 +26,33 @@ async void main_async() throws Error {
} else {
string subj_msg = "#%d".printf(ctr + 1);
composed_email.subject = subj_msg;
-
+
if (Geary.String.is_empty(arg_file)) {
composed_email.body_text = subj_msg;
} else {
string contents;
FileUtils.get_contents(arg_file, out contents);
-
+
composed_email.body_text = contents;
}
-
+
if (!Geary.String.is_empty(arg_html)) {
string contents;
FileUtils.get_contents(arg_html, out contents);
-
+
composed_email.body_html = contents;
}
-
+
msg = new Geary.RFC822.Message.from_composed_email(composed_email, null);
}
-
+
stdout.printf("\n\n%s\n\n", msg.to_string());
-
+
yield session.send_email_async(msg.from.get(0), msg);
-
+
stdout.printf("Sent email #%d\n", ctr);
}
-
+
Geary.Smtp.Response? logout = yield session.logout_async(false);
stdout.printf("%s\n", logout.to_string());
}
@@ -64,7 +64,7 @@ void on_main_completed(Object? object, AsyncResult result) {
stderr.printf("%s\n", err.message);
ec = 1;
}
-
+
if (main_loop != null)
main_loop.quit();
}
@@ -104,9 +104,9 @@ const int SMTP_TIMEOUT_SEC = 30;
bool verify_required(string? arg, string name) {
if (!Geary.String.is_empty(arg))
return true;
-
+
stdout.printf("%s required\n", name);
-
+
return false;
}
@@ -119,25 +119,25 @@ int main(string[] args) {
} catch (Error err) {
error ("Failed to parse command line: %s", err.message);
}
-
+
if (!arg_gmail && !verify_required(arg_hostname, "Hostname"))
return 1;
-
+
if (arg_full_file == null && !verify_required(arg_from, "From:"))
return 1;
-
+
if (arg_full_file == null && !verify_required(arg_to, "To:"))
return 1;
-
+
if (!verify_required(arg_user, "Username"))
return 1;
-
+
if (!verify_required(arg_pass, "Password"))
return 1;
-
+
if (arg_count < 1)
arg_count = 1;
-
+
if (arg_gmail) {
endpoint = new Geary.Endpoint(
new GLib.NetworkAddress("smtp.gmail.com", Geary.Smtp.SUBMISSION_PORT),
@@ -176,13 +176,13 @@ int main(string[] args) {
composed_email.to = new Geary.RFC822.MailboxAddresses.single(
new Geary.RFC822.MailboxAddress(null, arg_to));
}
-
+
main_loop = new MainLoop();
-
+
main_async.begin(on_main_completed);
-
+
main_loop.run();
-
+
return ec;
}
diff --git a/test/engine/app/app-conversation-monitor-test.vala
b/test/engine/app/app-conversation-monitor-test.vala
index 6bf85bda..dfdcfe89 100644
--- a/test/engine/app/app-conversation-monitor-test.vala
+++ b/test/engine/app/app-conversation-monitor-test.vala
@@ -356,11 +356,11 @@ class Geary.App.ConversationMonitorTest : TestCase {
new Gee.HashMap<EmailIdentifier,EmailFlags>();
flags_changed.set(e1.id, new EmailFlags.with(EmailFlags.DELETED));
this.account.email_flags_changed(this.base_folder, flags_changed);
-
+
this.base_folder.expect_call("list_email_by_sparse_id_async");
this.base_folder.expect_call("list_email_by_id_async");
- wait_for_signal(monitor, "email-flags-changed");
+ wait_for_signal(monitor, "email-flags-changed");
assert_int(0, monitor.size, "Conversation count should now be zero after being marked deleted.");
}
@@ -427,7 +427,7 @@ class Geary.App.ConversationMonitorTest : TestCase {
foreach (Email base_email in base_folder_email) {
base_email_ids.add(base_email.message_id);
}
-
+
int base_i = 0;
bool has_related = (
base_folder_email.length == related_paths.length
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]