From 76190409fd52c68616b5fee2a3cd01e60fc52d44 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Thu, 7 Feb 2019 14:00:08 +1100 Subject: [PATCH 01/16] Handle the expected untagged IMAP BYE status on LOGOUT --- .../imap/transport/imap-client-session.vala | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/engine/imap/transport/imap-client-session.vala b/src/engine/imap/transport/imap-client-session.vala index d26aef7a..3f1bc598 100644 --- a/src/engine/imap/transport/imap-client-session.vala +++ b/src/engine/imap/transport/imap-client-session.vala @@ -452,7 +452,7 @@ public class Geary.Imap.ClientSession : BaseObject { new Geary.State.Mapping(State.LOGGING_OUT, Event.CLOSE_MAILBOX, on_late_command), new Geary.State.Mapping(State.LOGGING_OUT, Event.LOGOUT, Geary.State.nop), new Geary.State.Mapping(State.LOGGING_OUT, Event.DISCONNECT, on_disconnect), - new Geary.State.Mapping(State.LOGGING_OUT, Event.RECV_STATUS, on_dropped_response), + new Geary.State.Mapping(State.LOGGING_OUT, Event.RECV_STATUS, on_logging_out_recv_status), 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), @@ -1352,7 +1352,8 @@ public class Geary.Imap.ClientSession : BaseObject { break; case Status.BYE: - debug("[%s] Received BYE from server: %s", to_string(), status_response.to_string()); + debug("[%s] Received unilateral 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); @@ -1556,6 +1557,34 @@ public class Geary.Imap.ClientSession : BaseObject { return State.LOGGING_OUT; } + private uint on_logging_out_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: + // We're expecting this bye, but don't disconnect yet + // since we'll do that when the command is complete + debug("[%s] Received bye from server on logout: %s", + to_string(), status_response.to_string()); + break; + + default: + debug("[%s] Received error from server on logout: %s", + to_string(), status_response.to_string()); + break; + } + + return state; + } + private uint on_logging_out_recv_completion(uint state, uint event, void *user, Object? object) { StatusResponse completion_response = (StatusResponse) object; From 3f9be2c0993baa665297adfd680925139cd217d9 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Thu, 7 Feb 2019 14:01:17 +1100 Subject: [PATCH 02/16] Disconnect and signal logout once the command successfully completed --- src/engine/imap/transport/imap-client-session.vala | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/engine/imap/transport/imap-client-session.vala b/src/engine/imap/transport/imap-client-session.vala index 3f1bc598..53b24bf9 100644 --- a/src/engine/imap/transport/imap-client-session.vala +++ b/src/engine/imap/transport/imap-client-session.vala @@ -1540,8 +1540,17 @@ public class Geary.Imap.ClientSession : BaseObject { if (params.err != null) throw params.err; - if(params.proceed) + if (params.proceed) { yield command_transaction_async(cmd, cancellable); + logged_out(); + this.cx.disconnect_async.begin( + cancellable, (obj, res) => { + dispatch_disconnect_results( + DisconnectReason.LOCAL_CLOSE, res + ); + } + ); + } } private uint on_logout(uint state, uint event, void *user, Object? object) { @@ -1591,8 +1600,6 @@ public class Geary.Imap.ClientSession : BaseObject { if (!validate_state_change_cmd(completion_response)) return state; - fsm.do_post_transition(() => { logged_out(); }); - return State.LOGGED_OUT; } From 3176667e3855ce27e84959f94406d157f8aa0986 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Thu, 7 Feb 2019 14:02:49 +1100 Subject: [PATCH 03/16] Treat LOGGED_OUT as a valid terminal FSM state --- src/engine/imap/transport/imap-client-session.vala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/imap/transport/imap-client-session.vala b/src/engine/imap/transport/imap-client-session.vala index 53b24bf9..a752fa7f 100644 --- a/src/engine/imap/transport/imap-client-session.vala +++ b/src/engine/imap/transport/imap-client-session.vala @@ -490,6 +490,7 @@ public class Geary.Imap.ClientSession : BaseObject { ~ClientSession() { switch (fsm.get_state()) { case State.NOT_CONNECTED: + case State.LOGGED_OUT: case State.CLOSED: // no problem-o break; From 0ebbc9b67cd1b36f96e4b7e159166632c091da6f Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Thu, 7 Feb 2019 14:04:21 +1100 Subject: [PATCH 04/16] Don't bother terminating IDLE when logging out, will be disconnecting --- src/engine/imap/transport/imap-client-session.vala | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/engine/imap/transport/imap-client-session.vala b/src/engine/imap/transport/imap-client-session.vala index a752fa7f..2806e901 100644 --- a/src/engine/imap/transport/imap-client-session.vala +++ b/src/engine/imap/transport/imap-client-session.vala @@ -1561,9 +1561,6 @@ public class Geary.Imap.ClientSession : BaseObject { if (!reserve_state_change_cmd(params, state, event)) return state; - // Leaving AUTHORIZED state, turn off IDLE - this.cx.enable_idle_when_quiet(false); - return State.LOGGING_OUT; } From 8306365ce8891474a647ad71d2c8de9e1bd6d2ff Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Thu, 7 Feb 2019 14:08:50 +1100 Subject: [PATCH 05/16] Log out IMAP connections when stopping Imap.ClientService cleanly When closing the a session cleanly (i.e. since the service is being stopped or a session is no longer needed, issue a logout instead of simply terminating the connection. Use a second cancellable for terminating logouts however when closing so we don't hang. --- src/engine/imap/api/imap-client-service.vala | 80 +++++++++++++++----- 1 file changed, 61 insertions(+), 19 deletions(-) diff --git a/src/engine/imap/api/imap-client-service.vala b/src/engine/imap/api/imap-client-service.vala index ea891425..724942f2 100644 --- a/src/engine/imap/api/imap-client-service.vala +++ b/src/engine/imap/api/imap-client-service.vala @@ -89,7 +89,8 @@ internal class Geary.Imap.ClientService : Geary.ClientService { private Nonblocking.Queue free_queue = new Nonblocking.Queue.fifo(); - private Cancellable? pool_cancellable = null; + private GLib.Cancellable? pool_cancellable = null; + private GLib.Cancellable? close_cancellable = null; public ClientService(AccountInformation account, @@ -109,7 +110,8 @@ internal class Geary.Imap.ClientService : Geary.ClientService { ); } - this.pool_cancellable = new Cancellable(); + this.pool_cancellable = new GLib.Cancellable(); + this.close_cancellable = new GLib.Cancellable(); notify_started(); } @@ -125,7 +127,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { notify_stopped(); this.pool_cancellable.cancel(); - yield close_pool(); + yield close_pool(true); // TODO: This isn't the best (deterministic) way to deal with // this, but it's easy and works for now @@ -139,6 +141,12 @@ internal class Geary.Imap.ClientService : Geary.ClientService { if (++attempts > 12) break; } + + if (this.all_sessions.size > 0) { + debug("[%s] Cancelling remaining client sessions...", + this.account.id); + this.close_cancellable.cancel(); + } } /** @@ -216,7 +224,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { ); if (!this.is_running || this.discard_returned_sessions || too_many_free) { - yield force_disconnect(session); + yield disconnect_session(session); } else if (yield check_session(session, false)) { bool free = true; MailboxSpecifier? mailbox = null; @@ -227,18 +235,30 @@ internal class Geary.Imap.ClientService : Geary.ClientService { proto == ClientSession.ProtocolState.SELECTING) { // always close mailbox to return to authorized state try { - yield session.close_mailbox_async(pool_cancellable); + yield session.close_mailbox_async(this.close_cancellable); } catch (ImapError imap_error) { debug("Error attempting to close released session %s: %s", session.to_string(), imap_error.message); free = false; } - if (session.get_protocol_state(null) != - ClientSession.ProtocolState.AUTHORIZED) { - // Closing it didn't work, so drop it - yield force_disconnect(session); + // Double check the session after closing it + switch (session.get_protocol_state(null)) { + case AUTHORIZED: + // This is the desired state, so all good + break; + + case NOT_CONNECTED: + // No longer connected, so just drop it free = false; + break; + + default: + // Closing it didn't leave it in the desired + // state, so log out and drop it + yield disconnect_session(session); + free = false; + break; } } @@ -256,7 +276,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { /** Closes the client session pool. */ protected override void became_unreachable() { - this.close_pool.begin(); + this.close_pool.begin(false); } private async void check_pool(bool is_claiming) { @@ -340,7 +360,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { if (new_session == null) { // An error was thrown, so close the pool - this.close_pool.begin(); + this.close_pool.begin(true); } else { try { yield this.sessions_mutex.execute_locked(() => { @@ -354,7 +374,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { context.format_full_error()); notify_connection_failed(context); new_session.disconnect_async.begin(null); - this.close_pool.begin(); + this.close_pool.begin(true); } } } @@ -371,7 +391,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { case ClientSession.ProtocolState.SELECTED: case ClientSession.ProtocolState.SELECTING: if (claiming) { - yield force_disconnect(target); + yield disconnect_session(target); } else { valid = true; } @@ -387,7 +407,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { break; default: - yield force_disconnect(target); + yield disconnect_session(target); break; } @@ -408,7 +428,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { try { debug("Sending NOOP when claiming a session"); yield target.send_command_async( - new NoopCommand(), this.pool_cancellable + new NoopCommand(), this.close_cancellable ); } catch (Error err) { debug("Error sending NOOP: %s", err.message); @@ -455,7 +475,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { return new_session; } - private async void close_pool() { + private async void close_pool(bool clean_disconnect) { debug("Closing the pool, disconnecting %d sessions", this.all_sessions.size); @@ -475,12 +495,34 @@ internal class Geary.Imap.ClientService : Geary.ClientService { // waiting for any since we don't want to delay closing the // others. foreach (ClientSession session in to_close) { - session.disconnect_async.begin(null); + if (clean_disconnect) { + this.disconnect_session.begin(session); + } else { + this.force_disconnect_session.begin(session); + } } } - private async void force_disconnect(ClientSession session) { - debug("Dropping session %s", session.to_string()); + private async void disconnect_session(ClientSession session) { + debug("[%s] Logging out session %s", + this.account.id, session.to_string()); + + // Log out before removing the session since close() only + // hangs around until all sessions have been removed before + // exiting. + try { + yield session.logout_async(this.close_cancellable); + yield remove_session_async(session); + } catch (GLib.Error err) { + debug("[%s] Error logging out of session: %s", + this.account.id, err.message); + yield force_disconnect_session(session); + } + + } + + private async void force_disconnect_session(ClientSession session) { + debug("[%s] Dropping session %s", this.account.id, session.to_string()); try { yield remove_session_async(session); From 2261d0cb64b2c898643a889cf759a84065227fa6 Mon Sep 17 00:00:00 2001 From: Chris Heywood <15127-creywood@users.noreply.gitlab.gnome.org> Date: Sat, 30 Nov 2019 14:17:12 +0100 Subject: [PATCH 06/16] Fix for conversation DND copy/move icon bug Fixes bug brought in with folder highlight on conversation DND recent merge --- src/client/folder-list/folder-list-tree.vala | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/client/folder-list/folder-list-tree.vala b/src/client/folder-list/folder-list-tree.vala index e9163da1..269ee831 100644 --- a/src/client/folder-list/folder-list-tree.vala +++ b/src/client/folder-list/folder-list-tree.vala @@ -25,7 +25,7 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface { private Application.NotificationContext? monitor = null; public Tree() { - base(TARGET_ENTRY_LIST, Gdk.DragAction.ASK, drop_handler); + base(TARGET_ENTRY_LIST, Gdk.DragAction.COPY | Gdk.DragAction.MOVE, drop_handler); base_ref(); entry_selected.connect(on_entry_selected); @@ -209,6 +209,22 @@ public class FolderList.Tree : Sidebar.Tree, Geary.BaseInterface { folder_selected(null); } + 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); + + // Update the cursor for copy or move. + Gdk.ModifierType mask; + double[] axes = new double[2]; + context.get_device().get_state(context.get_dest_window(), axes, out mask); + if ((mask & Gdk.ModifierType.CONTROL_MASK) != 0) { + Gdk.drag_status(context, Gdk.DragAction.COPY, time); + } else { + Gdk.drag_status(context, Gdk.DragAction.MOVE, time); + } + return ret; + } + private void on_ordinal_changed() { if (account_branches.size <= 1) return; From c080cd56bd3778cbf2d60b15ef9135e0c634b502 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Sun, 1 Dec 2019 17:27:52 +1100 Subject: [PATCH 07/16] Merge Geary.Imap.ClientSession logout states Merge LOGGING_OUT and LOGGED_OUT into single LOGOUT state to match RFC 3501. This leaves CLOSED as the only terminal state, and simplifies the FSM a bit. --- .../imap/transport/imap-client-session.vala | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/src/engine/imap/transport/imap-client-session.vala b/src/engine/imap/transport/imap-client-session.vala index 2806e901..c368ab32 100644 --- a/src/engine/imap/transport/imap-client-session.vala +++ b/src/engine/imap/transport/imap-client-session.vala @@ -82,7 +82,7 @@ public class Geary.Imap.ClientSession : BaseObject { * * These don't exactly match the states in the IMAP specification. For one, they count * transitions as states unto themselves (due to network latency and the asynchronous nature - * of ClientSession's interface). Also, the LOGOUT (and logging out) state has been melded + * of ClientSession's interface). Also, the LOGOUT state has been melded * into {@link ProtocolState.NOT_CONNECTED} on the presumption that the nuances of a disconnected or * disconnecting session is uninteresting to the caller. * @@ -172,14 +172,13 @@ public class Geary.Imap.ClientSession : BaseObject { NOAUTH, AUTHORIZED, SELECTED, - LOGGED_OUT, + LOGOUT, // transitional states CONNECTING, AUTHORIZING, SELECTING, CLOSING_MAILBOX, - LOGGING_OUT, // terminal state CLOSED, @@ -445,29 +444,18 @@ public class Geary.Imap.ClientSession : BaseObject { 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), - new Geary.State.Mapping(State.LOGGING_OUT, Event.SELECT, on_late_command), - new Geary.State.Mapping(State.LOGGING_OUT, Event.CLOSE_MAILBOX, on_late_command), - new Geary.State.Mapping(State.LOGGING_OUT, Event.LOGOUT, Geary.State.nop), - new Geary.State.Mapping(State.LOGGING_OUT, Event.DISCONNECT, on_disconnect), - new Geary.State.Mapping(State.LOGGING_OUT, Event.RECV_STATUS, on_logging_out_recv_status), - 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), - new Geary.State.Mapping(State.LOGGED_OUT, Event.SELECT, on_late_command), - new Geary.State.Mapping(State.LOGGED_OUT, Event.CLOSE_MAILBOX, on_late_command), - new Geary.State.Mapping(State.LOGGED_OUT, Event.LOGOUT, on_late_command), - new Geary.State.Mapping(State.LOGGED_OUT, Event.DISCONNECT, on_disconnect), - new Geary.State.Mapping(State.LOGGED_OUT, Event.RECV_STATUS, on_dropped_response), - 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.LOGOUT, Event.CONNECT, on_already_connected), + new Geary.State.Mapping(State.LOGOUT, Event.LOGIN, on_already_logged_in), + new Geary.State.Mapping(State.LOGOUT, Event.SEND_CMD, on_late_command), + new Geary.State.Mapping(State.LOGOUT, Event.SELECT, on_late_command), + new Geary.State.Mapping(State.LOGOUT, Event.CLOSE_MAILBOX, on_late_command), + new Geary.State.Mapping(State.LOGOUT, Event.LOGOUT, on_late_command), + new Geary.State.Mapping(State.LOGOUT, Event.DISCONNECT, on_disconnect), + new Geary.State.Mapping(State.LOGOUT, Event.DISCONNECTED, on_disconnected), + new Geary.State.Mapping(State.LOGOUT, Event.RECV_STATUS, on_logging_out_recv_status), + new Geary.State.Mapping(State.LOGOUT, Event.RECV_COMPLETION, on_logging_out_recv_completion), + new Geary.State.Mapping(State.LOGOUT, Event.RECV_ERROR, on_recv_error), + new Geary.State.Mapping(State.LOGOUT, Event.SEND_ERROR, on_send_error), new Geary.State.Mapping(State.CLOSED, Event.CONNECT, on_late_command), new Geary.State.Mapping(State.CLOSED, Event.LOGIN, on_late_command), @@ -490,7 +478,6 @@ public class Geary.Imap.ClientSession : BaseObject { ~ClientSession() { switch (fsm.get_state()) { case State.NOT_CONNECTED: - case State.LOGGED_OUT: case State.CLOSED: // no problem-o break; @@ -596,8 +583,7 @@ public class Geary.Imap.ClientSession : BaseObject { switch (fsm.get_state()) { case State.NOT_CONNECTED: - case State.LOGGED_OUT: - case State.LOGGING_OUT: + case State.LOGOUT: case State.CLOSED: return ProtocolState.NOT_CONNECTED; @@ -717,7 +703,7 @@ public class Geary.Imap.ClientSession : BaseObject { 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, + // original Error ... connect_async shouldn't leave the session in the CONNECTING state, // but completely disconnected if unsuccessful if (connect_err != null) { try { @@ -794,7 +780,7 @@ public class Geary.Imap.ClientSession : BaseObject { imap_endpoint.to_string()); // stay in current state -- wait for initial status response - // to move into NOAUTH or LOGGED OUT + // to move into NOAUTH or LOGOUT return state; } @@ -806,7 +792,7 @@ public class Geary.Imap.ClientSession : BaseObject { debug("[%s] Disconnected from %s", to_string(), this.imap_endpoint.to_string()); - return state; + return State.CLOSED; } private uint on_connecting_recv_status(uint state, uint event, void *user, Object? object) { @@ -829,11 +815,14 @@ public class Geary.Imap.ClientSession : BaseObject { debug("[%s] Connect denied: %s", to_string(), status_response.to_string()); fsm.do_post_transition(() => { session_denied(status_response.get_text()); }); - connect_err = new ImapError.UNAVAILABLE( + + // Don't need to manually disconnect here, by setting + // connect_err here that will be done in connect_async + this.connect_err = new ImapError.UNAVAILABLE( "Session denied: %s", status_response.get_text() ); - return State.LOGGED_OUT; + return State.LOGOUT; } private uint on_connecting_timeout(uint state, uint event) { @@ -847,10 +836,14 @@ public class Geary.Imap.ClientSession : BaseObject { debug("[%s] Connect timed-out", to_string()); - connect_err = new IOError.TIMED_OUT("Session greeting not seen in %u seconds", - GREETING_TIMEOUT_SEC); + // Don't need to manually disconnect here, by setting + // connect_err here that will be done in connect_async + this.connect_err = new IOError.TIMED_OUT( + "Session greeting not seen in %u seconds", + GREETING_TIMEOUT_SEC + ); - return State.LOGGED_OUT; + return State.LOGOUT; } /** @@ -1561,7 +1554,7 @@ public class Geary.Imap.ClientSession : BaseObject { if (!reserve_state_change_cmd(params, state, event)) return state; - return State.LOGGING_OUT; + return State.LOGOUT; } private uint on_logging_out_recv_status(uint state, @@ -1598,7 +1591,7 @@ public class Geary.Imap.ClientSession : BaseObject { if (!validate_state_change_cmd(completion_response)) return state; - return State.LOGGED_OUT; + return State.CLOSED; } // From 978ff12810cf175a14815952f36fb67a91ba9479 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Sun, 1 Dec 2019 19:37:15 +1100 Subject: [PATCH 08/16] Rename Geary.Loggable to Geary.Logging.Source Rename the class because we need to distinguish between logging sources and logging state, for cases when the logged object's state may change between being logged and being displayed. Rename util souce file to match new name space per source code convernstion. --- po/POTFILES.in | 2 +- .../components-inspector-log-view.vala | 2 +- src/engine/api/geary-account.vala | 6 +-- src/engine/api/geary-client-service.vala | 12 ++--- src/engine/api/geary-folder.vala | 6 +-- src/engine/api/geary-logging.vala | 54 ++++++++++--------- .../imap-engine-account-operation.vala | 6 +-- .../imap-engine-generic-account.vala | 4 +- src/engine/meson.build | 2 +- .../{util-loggable.vala => util-logging.vala} | 38 ++++++------- 10 files changed, 67 insertions(+), 65 deletions(-) rename src/engine/util/{util-loggable.vala => util-logging.vala} (79%) diff --git a/po/POTFILES.in b/po/POTFILES.in index c4b1b920..5ec9b69e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -396,7 +396,7 @@ src/engine/util/util-idle-manager.vala src/engine/util/util-imap-utf7.vala src/engine/util/util-inet.vala src/engine/util/util-iterable.vala -src/engine/util/util-loggable.vala +src/engine/util/util-logging.vala src/engine/util/util-numeric.vala src/engine/util/util-object.vala src/engine/util/util-reference-semantics.vala diff --git a/src/client/components/components-inspector-log-view.vala b/src/client/components/components-inspector-log-view.vala index e09649af..c1e3d996 100644 --- a/src/client/components/components-inspector-log-view.vala +++ b/src/client/components/components-inspector-log-view.vala @@ -214,7 +214,7 @@ public class Components.InspectorLogView : Gtk.Grid { // Blacklist GdkPixbuf since it spams us e.g. when window // focus changes, including between MainWindow and the // Inspector, which is very annoying. - record.fill_well_known_loggables(); + record.fill_well_known_sources(); return ( record.domain != "GdkPixbuf" && (record.account == null || diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala index e5a0fbd5..e4223cf7 100644 --- a/src/engine/api/geary-account.vala +++ b/src/engine/api/geary-account.vala @@ -22,7 +22,7 @@ * A list of all Accounts may be retrieved from the {@link Engine} singleton. */ -public abstract class Geary.Account : BaseObject, Loggable { +public abstract class Geary.Account : BaseObject, Logging.Source { /** Number of times to attempt re-authentication. */ @@ -247,12 +247,12 @@ public abstract class Geary.Account : BaseObject, Loggable { Gee.Map map); /** {@inheritDoc} */ - public Logging.Flag loggable_flags { + public Logging.Flag logging_flags { get; protected set; default = Logging.Flag.ALL; } /** {@inheritDoc} */ - public Loggable? loggable_parent { get { return null; } } + public Logging.Source? logging_parent { get { return null; } } protected Account(AccountInformation information, diff --git a/src/engine/api/geary-client-service.vala b/src/engine/api/geary-client-service.vala index 6f6da006..c9dbcf29 100644 --- a/src/engine/api/geary-client-service.vala +++ b/src/engine/api/geary-client-service.vala @@ -14,7 +14,7 @@ * itself, rather manages the configuration, status tracking, and * life-cycle of concrete implementations. */ -public abstract class Geary.ClientService : BaseObject, Loggable { +public abstract class Geary.ClientService : BaseObject, Logging.Source { private const int BECAME_REACHABLE_TIMEOUT_SEC = 1; @@ -198,13 +198,13 @@ public abstract class Geary.ClientService : BaseObject, Loggable { public ErrorContext? last_error { get; private set; default = null; } /** {@inheritDoc} */ - public Logging.Flag loggable_flags { + public Logging.Flag logging_flags { get; protected set; default = Logging.Flag.ALL; } /** {@inheritDoc} */ - public Loggable? loggable_parent { get { return _loggable_parent; } } - private weak Loggable? _loggable_parent = null; + public Logging.Source? logging_parent { get { return _logging_parent; } } + private weak Logging.Source? _logging_parent = null; protected ClientService(AccountInformation account, @@ -301,8 +301,8 @@ public abstract class Geary.ClientService : BaseObject, Loggable { } /** Sets the service's logging parent. */ - internal void set_loggable_parent(Loggable parent) { - this._loggable_parent = parent; + internal void set_logging_parent(Logging.Source parent) { + this._logging_parent = parent; } /** diff --git a/src/engine/api/geary-folder.vala b/src/engine/api/geary-folder.vala index a7f4d7fe..a7aeb632 100644 --- a/src/engine/api/geary-folder.vala +++ b/src/engine/api/geary-folder.vala @@ -61,7 +61,7 @@ * * @see Geary.SpecialFolderType */ -public abstract class Geary.Folder : BaseObject, Loggable { +public abstract class Geary.Folder : BaseObject, Logging.Source { /** * Indicates if a folder has been opened, and if so in which way. @@ -239,12 +239,12 @@ public abstract class Geary.Folder : BaseObject, Loggable { public abstract Geary.ProgressMonitor opening_monitor { get; } /** {@inheritDoc} */ - public Logging.Flag loggable_flags { + public Logging.Flag logging_flags { get; protected set; default = Logging.Flag.ALL; } /** {@inheritDoc} */ - public Loggable? loggable_parent { + public Logging.Source? logging_parent { get { return this.account; } } diff --git a/src/engine/api/geary-logging.vala b/src/engine/api/geary-logging.vala index bdbe8744..146b58a5 100644 --- a/src/engine/api/geary-logging.vala +++ b/src/engine/api/geary-logging.vala @@ -153,7 +153,7 @@ public class Record { /** The next log record in the buffer, if any. */ public Record? next { get; internal set; default = null; } - private Loggable[] loggables; + private Source[] sources; private bool filled = false; private bool old_log_api = false; @@ -170,12 +170,12 @@ public class Record { // Since GLib.LogField only retains a weak ref to its value, // find and ref any values we wish to keep around. - this.loggables = new Loggable[fields.length]; - int loggable_count = 0; + this.sources = new Source[fields.length]; + int source_count = 0; foreach (GLib.LogField field in fields) { switch (field.key) { - case "GEARY_LOGGABLE": - this.loggables[loggable_count++] = (Loggable) field.value; + case "GEARY_LOGGING_SOURCE": + this.sources[source_count++] = (Source) field.value; break; case "GEARY_FLAGS": @@ -204,20 +204,20 @@ public class Record { } } - this.loggables.length = loggable_count; + this.sources.length = source_count; } - /** Returns the record's loggables that aren't well-known. */ - public Loggable[] get_other_loggables() { - fill_well_known_loggables(); + /** Returns the record's sources that aren't well-known. */ + public Source[] get_other_sources() { + fill_well_known_sources(); - Loggable[] copy = new Loggable[this.loggables.length]; + Source[] copy = new Source[this.sources.length]; int count = 0; - foreach (Loggable loggable in this.loggables) { - if (loggable != this.account && - loggable != this.service && - loggable != this.folder) { - copy[count++] = loggable; + foreach (Source source in this.sources) { + if (source != this.account && + source != this.service && + source != this.folder) { + copy[count++] = source; } } copy.length = count; @@ -225,22 +225,22 @@ public class Record { } /** - * Sets the well-known loggable properties. + * Sets the well-known logging source properties. * * Call this before trying to access {@link account}, {@link * folder} and {@link service}. Determining these can be * computationally complex and hence is not done by default. */ - public void fill_well_known_loggables() { + public void fill_well_known_sources() { if (!this.filled) { - foreach (Loggable loggable in this.loggables) { - GLib.Type type = loggable.get_type(); + foreach (Source source in this.sources) { + GLib.Type type = source.get_type(); if (type.is_a(typeof(Account))) { - this.account = (Account) loggable; + this.account = (Account) source; } else if (type.is_a(typeof(ClientService))) { - this.service = (ClientService) loggable; + this.service = (ClientService) source; } else if (type.is_a(typeof(Folder))) { - this.folder = (Folder) loggable; + this.folder = (Folder) source; } } this.filled = true; @@ -249,7 +249,7 @@ public class Record { /** Returns a formatted string representation of this record. */ public string format() { - fill_well_known_loggables(); + fill_well_known_sources(); string domain = this.domain ?? "[no domain]"; Flag flags = this.flags ?? Flag.NONE; @@ -271,7 +271,7 @@ public class Record { domain ); - if (flags != NONE && flags != ALL) { + if (flags != NONE) { str.append_printf("[%s]: ", flags.to_string()); } else { str.append(": "); @@ -299,8 +299,10 @@ public class Record { str.append(": "); } - foreach (Loggable loggable in get_other_loggables()) { - str.append(loggable.to_string()); + // Append in reverse so leaf sources appears last + var sources = get_other_sources(); + for (int i = sources.length - 1; i >= 0; i--) { + str.append(sources[i].to_string()); str.append_c(' '); } diff --git a/src/engine/imap-engine/imap-engine-account-operation.vala b/src/engine/imap-engine/imap-engine-account-operation.vala index 99a7fdb3..38c4b047 100644 --- a/src/engine/imap-engine/imap-engine-account-operation.vala +++ b/src/engine/imap-engine/imap-engine-account-operation.vala @@ -33,16 +33,16 @@ * equal_to} method to specify which instances are considered to be * duplicates. */ -public abstract class Geary.ImapEngine.AccountOperation : BaseObject, Loggable { +public abstract class Geary.ImapEngine.AccountOperation : BaseObject, Logging.Source { /** {@inheritDoc} */ - public Logging.Flag loggable_flags { + public Logging.Flag logging_flags { get; protected set; default = Logging.Flag.ALL; } /** {@inheritDoc} */ - public Loggable? loggable_parent { get { return account; } } + public Logging.Source? logging_parent { get { return account; } } /** The account this operation applies to. */ protected weak Geary.Account account { get; private set; } diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala b/src/engine/imap-engine/imap-engine-generic-account.vala index b790d647..ae9f0cb2 100644 --- a/src/engine/imap-engine/imap-engine-generic-account.vala +++ b/src/engine/imap-engine/imap-engine-generic-account.vala @@ -91,12 +91,12 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account { imap.notify["current-status"].connect( on_imap_status_notify ); - imap.set_loggable_parent(this); + imap.set_logging_parent(this); this.imap = imap; smtp.outbox = new Outbox.Folder(this, local_folder_root, local); smtp.report_problem.connect(notify_report_problem); - smtp.set_loggable_parent(this); + smtp.set_logging_parent(this); this.smtp = smtp; this.sync = new AccountSynchronizer(this); diff --git a/src/engine/meson.build b/src/engine/meson.build index 7515ee72..3d83d81e 100644 --- a/src/engine/meson.build +++ b/src/engine/meson.build @@ -307,7 +307,7 @@ geary_engine_vala_sources = files( 'util/util-imap-utf7.vala', 'util/util-inet.vala', 'util/util-iterable.vala', - 'util/util-loggable.vala', + 'util/util-logging.vala', 'util/util-numeric.vala', 'util/util-object.vala', 'util/util-reference-semantics.vala', diff --git a/src/engine/util/util-loggable.vala b/src/engine/util/util-logging.vala similarity index 79% rename from src/engine/util/util-loggable.vala rename to src/engine/util/util-logging.vala index d559f41b..3fac688d 100644 --- a/src/engine/util/util-loggable.vala +++ b/src/engine/util/util-logging.vala @@ -8,14 +8,14 @@ /** * Mixin interface for objects that support structured logging. * - * Loggable objects provide both a standard means to obtain a string + * Logging sources provide both a standard means to obtain a string * representation of the object for display to humans, and keep a weak - * reference to some parent loggable, enabling this context to be + * reference to some parent source, enabling context to be * automatically added to logging calls. For example, if a Foo object - * is the loggable parent of a Bar object, log calls made by Bar will - * automatically be decorated with Foo. + * is the logging source parent of a Bar object, log calls made by Bar + * will automatically be decorated with Foo. */ -public interface Geary.Loggable : GLib.Object { +public interface Geary.Logging.Source : GLib.Object { // Based on function from with the same name from GLib's @@ -87,8 +87,8 @@ public interface Geary.Loggable : GLib.Object { this.count++; } - public inline void append_loggable(Loggable value) { - this.append("GEARY_LOGGABLE", value); + public inline void append_source(Source value) { + this.append("GEARY_LOGGING_SOURCE", value); } public GLib.LogField[] to_array() { @@ -101,17 +101,17 @@ public interface Geary.Loggable : GLib.Object { /** - * Default flags to use for this loggable when logging messages. + * Default flags to use for this source when logging messages. */ - public abstract Logging.Flag loggable_flags { get; protected set; } + public abstract Logging.Flag logging_flags { get; protected set; } /** - * The parent of this loggable. + * The parent of this source. * * If not null, the parent and its ancestors recursively will be * added to to log message context. */ - public abstract Loggable? loggable_parent { get; } + public abstract Source? logging_parent { get; } /** * Returns a string representation of the service, for debugging. @@ -125,7 +125,7 @@ public interface Geary.Loggable : GLib.Object { [PrintfFormat] public inline void debug(string fmt, ...) { log_structured( - this.loggable_flags, LogLevelFlags.LEVEL_DEBUG, fmt, va_list() + this.logging_flags, LogLevelFlags.LEVEL_DEBUG, fmt, va_list() ); } @@ -135,7 +135,7 @@ public interface Geary.Loggable : GLib.Object { [PrintfFormat] public inline void message(string fmt, ...) { log_structured( - this.loggable_flags, LogLevelFlags.LEVEL_MESSAGE, fmt, va_list() + this.logging_flags, LogLevelFlags.LEVEL_MESSAGE, fmt, va_list() ); } @@ -145,7 +145,7 @@ public interface Geary.Loggable : GLib.Object { [PrintfFormat] public inline void warning(string fmt, ...) { log_structured( - this.loggable_flags, LogLevelFlags.LEVEL_WARNING, fmt, va_list() + this.logging_flags, LogLevelFlags.LEVEL_WARNING, fmt, va_list() ); } @@ -156,7 +156,7 @@ public interface Geary.Loggable : GLib.Object { [NoReturn] public inline void error(string fmt, ...) { log_structured( - this.loggable_flags, LogLevelFlags.LEVEL_ERROR, fmt, va_list() + this.logging_flags, LogLevelFlags.LEVEL_ERROR, fmt, va_list() ); } @@ -166,7 +166,7 @@ public interface Geary.Loggable : GLib.Object { [PrintfFormat] public inline void critical(string fmt, ...) { log_structured( - this.loggable_flags, LogLevelFlags.LEVEL_CRITICAL, fmt, va_list() + this.logging_flags, LogLevelFlags.LEVEL_CRITICAL, fmt, va_list() ); } @@ -175,10 +175,10 @@ public interface Geary.Loggable : GLib.Object { string fmt, va_list args) { Context context = Context(Logging.DOMAIN, flags, levels, fmt, args); - Loggable? decorated = this; + Source? decorated = this; while (decorated != null) { - context.append_loggable(decorated); - decorated = decorated.loggable_parent; + context.append_source(decorated); + decorated = decorated.logging_parent; } GLib.log_structured_array(levels, context.to_array()); From 56601cfb5c6eae27688ac0b7b4bea900a5cf1f78 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Mon, 2 Dec 2019 10:40:31 +1100 Subject: [PATCH 09/16] Ensure AccountProcessorTest stops its test article Add ::tear_down, stop the test article from running, so it doesn't keep executing after tests have run. --- .../imap-engine/account-processor-test.vala | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/test/engine/imap-engine/account-processor-test.vala b/test/engine/imap-engine/account-processor-test.vala index 35577c26..4f2ed50a 100644 --- a/test/engine/imap-engine/account-processor-test.vala +++ b/test/engine/imap-engine/account-processor-test.vala @@ -26,7 +26,6 @@ public class Geary.ImapEngine.AccountProcessorTest : TestCase { public override async void execute(Cancellable cancellable) throws Error { - print("Test op/"); this.execute_called = true; if (this.wait_for_cancel) { yield this.spinlock.wait_async(cancellable); @@ -62,10 +61,6 @@ public class Geary.ImapEngine.AccountProcessorTest : TestCase { add_test("failure", failure); add_test("duplicate", duplicate); add_test("stop", stop); - - // XXX this has to be here instead of in set_up for some - // reason... - this.processor = new AccountProcessor("processor"); } public override void set_up() { @@ -76,6 +71,19 @@ public class Geary.ImapEngine.AccountProcessorTest : TestCase { new RFC822.MailboxAddress(null, "test1@example.com") ); this.account = new Geary.MockAccount(this.info); + this.processor = new AccountProcessor("processor"); + + this.succeeded = 0; + this.failed = 0; + this.completed = 0; + } + + public override void tear_down() { + this.processor.stop(); + this.processor = null; + + this.account = null; + this.info = null; this.succeeded = 0; this.failed = 0; From 70dffd783a9f734780ec5b63dfb5471b226aaeb1 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Mon, 2 Dec 2019 11:38:55 +1100 Subject: [PATCH 10/16] Add new Geary.Logging.State object for snapshotting source state Add State object and Geary.Logging.State::to_logging_state factory method. Use this in LogRecord to print formatted log messages so that sources can log state that may change between being logged and the log message being displayed/saved. Provide a default implementation of Source::to_string that uses the instance's current state. Update implementing classes. --- src/engine/api/geary-account.vala | 7 +- src/engine/api/geary-client-service.vala | 7 +- src/engine/api/geary-folder.vala | 8 +- src/engine/api/geary-logging.vala | 70 ++++------------ .../imap-engine-account-operation.vala | 20 ++--- .../imap-engine-minimal-folder.vala | 11 ++- src/engine/util/util-logging.vala | 79 ++++++++++++++++++- 7 files changed, 117 insertions(+), 85 deletions(-) diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala index e4223cf7..4d5d6aad 100644 --- a/src/engine/api/geary-account.vala +++ b/src/engine/api/geary-account.vala @@ -474,11 +474,8 @@ public abstract class Geary.Account : BaseObject, Logging.Source { Gee.Collection ids, Cancellable? cancellable) throws Error; /** {@inheritDoc} */ - public virtual string to_string() { - return "%s(%s)".printf( - this.get_type().name(), - this.information.id - ); + public virtual Logging.State to_logging_state() { + return new Logging.State(this, this.information.id); } /** Fires a {@link opened} signal. */ diff --git a/src/engine/api/geary-client-service.vala b/src/engine/api/geary-client-service.vala index c9dbcf29..b5bee4fc 100644 --- a/src/engine/api/geary-client-service.vala +++ b/src/engine/api/geary-client-service.vala @@ -293,11 +293,8 @@ public abstract class Geary.ClientService : BaseObject, Logging.Source { } /** {@inheritDoc} */ - public virtual string to_string() { - return "%s(%s)".printf( - this.get_type().name(), - this.configuration.protocol.to_value() - ); + public virtual Logging.State to_logging_state() { + return new Logging.State(this, this.configuration.protocol.to_value()); } /** Sets the service's logging parent. */ diff --git a/src/engine/api/geary-folder.vala b/src/engine/api/geary-folder.vala index a7aeb632..f2762f09 100644 --- a/src/engine/api/geary-folder.vala +++ b/src/engine/api/geary-folder.vala @@ -696,12 +696,8 @@ public abstract class Geary.Folder : BaseObject, Logging.Source { Geary.Email.Field required_fields, ListFlags flags, Cancellable? cancellable = null) throws Error; /** {@inheritDoc} */ - public virtual string to_string() { - return "%s(%s:%s)".printf( - this.get_type().name(), - this.account.information.id, - this.path.to_string() - ); + public virtual Logging.State to_logging_state() { + return new Logging.State(this, this.path.to_string()); } } diff --git a/src/engine/api/geary-logging.vala b/src/engine/api/geary-logging.vala index 146b58a5..f5b1e097 100644 --- a/src/engine/api/geary-logging.vala +++ b/src/engine/api/geary-logging.vala @@ -153,7 +153,7 @@ public class Record { /** The next log record in the buffer, if any. */ public Record? next { get; internal set; default = null; } - private Source[] sources; + private State[] states; private bool filled = false; private bool old_log_api = false; @@ -170,12 +170,13 @@ public class Record { // Since GLib.LogField only retains a weak ref to its value, // find and ref any values we wish to keep around. - this.sources = new Source[fields.length]; - int source_count = 0; + this.states = new State[fields.length]; + int state_count = 0; foreach (GLib.LogField field in fields) { switch (field.key) { case "GEARY_LOGGING_SOURCE": - this.sources[source_count++] = (Source) field.value; + this.states[state_count++] = + ((Source) field.value).to_logging_state(); break; case "GEARY_FLAGS": @@ -204,24 +205,7 @@ public class Record { } } - this.sources.length = source_count; - } - - /** Returns the record's sources that aren't well-known. */ - public Source[] get_other_sources() { - fill_well_known_sources(); - - Source[] copy = new Source[this.sources.length]; - int count = 0; - foreach (Source source in this.sources) { - if (source != this.account && - source != this.service && - source != this.folder) { - copy[count++] = source; - } - } - copy.length = count; - return copy; + this.states.length = state_count; } /** @@ -233,14 +217,14 @@ public class Record { */ public void fill_well_known_sources() { if (!this.filled) { - foreach (Source source in this.sources) { - GLib.Type type = source.get_type(); + foreach (unowned State state in this.states) { + GLib.Type type = state.source.get_type(); if (type.is_a(typeof(Account))) { - this.account = (Account) source; + this.account = (Account) state.source; } else if (type.is_a(typeof(ClientService))) { - this.service = (ClientService) source; + this.service = (ClientService) state.source; } else if (type.is_a(typeof(Folder))) { - this.folder = (Folder) source; + this.folder = (Folder) state.source; } } this.filled = true; @@ -277,33 +261,11 @@ public class Record { str.append(": "); } - // Use a compact format for well known ojects - if (this.account != null) { - str.append(this.account.information.id); - str.append_c('['); - str.append(this.account.information.service_provider.to_value()); - if (this.service != null) { - str.append_c(':'); - str.append(this.service.configuration.protocol.to_value()); - } - str.append_c(']'); - if (this.folder == null) { - str.append(": "); - } - } else if (this.service != null) { - str.append(this.service.configuration.protocol.to_value()); - str.append(": "); - } - if (this.folder != null) { - str.append(this.folder.path.to_string()); - str.append(": "); - } - - // Append in reverse so leaf sources appears last - var sources = get_other_sources(); - for (int i = sources.length - 1; i >= 0; i--) { - str.append(sources[i].to_string()); - str.append_c(' '); + // Append in reverse so inner sources appear first + for (int i = this.states.length - 1; i >= 0; i--) { + str.append("["); + str.append(this.states[i].format_message()); + str.append("] "); } str.append(message); diff --git a/src/engine/imap-engine/imap-engine-account-operation.vala b/src/engine/imap-engine/imap-engine-account-operation.vala index 38c4b047..3dc32d37 100644 --- a/src/engine/imap-engine/imap-engine-account-operation.vala +++ b/src/engine/imap-engine/imap-engine-account-operation.vala @@ -112,8 +112,8 @@ public abstract class Geary.ImapEngine.AccountOperation : BaseObject, Logging.So } /** {@inheritDoc} */ - public virtual string to_string() { - return get_type().name(); + public virtual Logging.State to_logging_state() { + return new Logging.State(this, this.account.information.id); } } @@ -160,14 +160,14 @@ public abstract class Geary.ImapEngine.FolderOperation : AccountOperation { ); } - /** - * Provides a representation of this operation for debugging. - * - * The return value will include its folder's path and the name of - * the class. - */ - public override string to_string() { - return "%s(%s)".printf(base.to_string(), folder.path.to_string()); + /** {@inheritDoc} */ + public override Logging.State to_logging_state() { + return new Logging.State( + this, + "%s:%s", + this.account.information.id, + this.folder.path.to_string() + ); } } diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala b/src/engine/imap-engine/imap-engine-minimal-folder.vala index 06ae06f5..37694c47 100644 --- a/src/engine/imap-engine/imap-engine-minimal-folder.vala +++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala @@ -1411,9 +1411,14 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport yield op.wait_for_ready_async(cancellable); } - public override string to_string() { - return "%s (open_count=%d remote_opened=%s)".printf( - base.to_string(), open_count, (this.remote_session != null).to_string() + /** {@inheritDoc} */ + public override Logging.State to_logging_state() { + return new Logging.State( + this, + "%s, open_count=%d, remote_opened=%s", + this.path.to_string(), + this.open_count, + (this.remote_session != null).to_string() ); } diff --git a/src/engine/util/util-logging.vala b/src/engine/util/util-logging.vala index 3fac688d..30ff8ffe 100644 --- a/src/engine/util/util-logging.vala +++ b/src/engine/util/util-logging.vala @@ -5,6 +5,7 @@ * (version 2.1 or later). See the COPYING file in this distribution. */ + /** * Mixin interface for objects that support structured logging. * @@ -18,6 +19,22 @@ public interface Geary.Logging.Source : GLib.Object { + /** + * Returns a string representation of a source based on its state. + * + * The string returned will include the source's type name, the + * its current logging state, and the value of extra_values, if + * any. + */ + protected static string default_to_string(Source source, + string extra_values) { + return "%s(%s%s)".printf( + source.get_type().name(), + source.to_logging_state().format_message(), + extra_values + ); + } + // Based on function from with the same name from GLib's // gmessages.c. Return value must be 1 byte long (plus nul byte). // Reference: @@ -114,10 +131,26 @@ public interface Geary.Logging.Source : GLib.Object { public abstract Source? logging_parent { get; } /** - * Returns a string representation of the service, for debugging. + * Returns a loggable representation of this source's current state. + * + * Since this source's internal state may change between being + * logged and being used from a log record, this records relevant + * state at the time when it was logged so it may be displayed or + * recorded as it is right now. */ - public abstract string to_string(); + public abstract State to_logging_state(); + /** + * Returns a string representation of this source based on its state. + * + * This simply calls {@link default_to_string} with this source + * and the empty string, returning the result. Implementations of + * this interface can call that method if they need to override + * the default behaviour of this method. + */ + public string to_string() { + return Source.default_to_string(this, ""); + } /** * Logs a debug-level log message with this object as context. @@ -185,3 +218,45 @@ public interface Geary.Logging.Source : GLib.Object { } } + +/** + * A record of the state of a logging source to be recorded. + * + * @see Source.to_logging_state + */ +// This a class rather than a struct so we get pass-by-reference +// semantics for it, and make its members private +public class Geary.Logging.State { + + + public Source source { get; private set; } + + + private string message; + // Would like to use the following but can't because of + // https://gitlab.gnome.org/GNOME/vala/issues/884 + // private va_list args; + + + /* + * Constructs a new logging state. + * + * The given source should be the source object that constructed + * the state during a call to {@link Source.to_logging_state}. + */ + [PrintfFormat] + public State(Source source, string message, ...) { + this.source = source; + this.message = message; + + // this.args = va_list(); + this.message = message.vprintf(va_list()); + } + + public string format_message() { + // vprint mangles its passed-in args, so copy them + // return this.message.vprintf(va_list.copy(this.args)); + return message; + } + +} From 778a06af7744845c87f2b4c0ad7558302dc0b5d0 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Mon, 2 Dec 2019 11:42:15 +1100 Subject: [PATCH 11/16] Add Geary.Logging.Source::log method Generic method allows custom level and flags to be specified. --- src/engine/util/util-logging.vala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/engine/util/util-logging.vala b/src/engine/util/util-logging.vala index 30ff8ffe..ef823705 100644 --- a/src/engine/util/util-logging.vala +++ b/src/engine/util/util-logging.vala @@ -203,6 +203,16 @@ public interface Geary.Logging.Source : GLib.Object { ); } + /** + * Logs a message with this object as context. + */ + [PrintfFormat] + public inline void log(Logging.Flag flags, + GLib.LogLevelFlags levels, + string fmt, ...) { + log_structured(flags, levels, fmt, va_list()); + } + private inline void log_structured(Logging.Flag flags, GLib.LogLevelFlags levels, string fmt, From 9884cd2e6c9cb9b0a7ee3baac67263df147df116 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Mon, 2 Dec 2019 11:43:41 +1100 Subject: [PATCH 12/16] Update Geary.IMAP classes to implement Geary.Logging.Source --- .../imap-engine-generic-account.vala | 15 +- src/engine/imap/api/imap-account-session.vala | 22 +- src/engine/imap/api/imap-client-service.vala | 16 +- src/engine/imap/api/imap-folder-session.vala | 92 ++++---- src/engine/imap/api/imap-folder.vala | 4 + src/engine/imap/api/imap-session-object.vala | 36 ++-- .../transport/imap-client-connection.vala | 139 ++++++------ .../imap/transport/imap-client-session.vala | 200 ++++++++++-------- 8 files changed, 288 insertions(+), 236 deletions(-) diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala b/src/engine/imap-engine/imap-engine-generic-account.vala index ae9f0cb2..6f54c6a3 100644 --- a/src/engine/imap-engine/imap-engine-generic-account.vala +++ b/src/engine/imap-engine/imap-engine-generic-account.vala @@ -297,11 +297,10 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account { check_open(); debug("Acquiring account session"); yield this.remote_ready_lock.wait_async(cancellable); - Imap.ClientSession client = - yield this.imap.claim_authorized_session_async(cancellable); - return new Imap.AccountSession( - this.information.id, this.local.imap_folder_root, client - ); + var client = yield this.imap.claim_authorized_session_async(cancellable); + var session = new Imap.AccountSession(this.local.imap_folder_root, client); + session.set_logging_parent(this.imap); + return session; } /** @@ -351,8 +350,9 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account { Imap.ClientSession? client = yield this.imap.claim_authorized_session_async(cancellable); Imap.AccountSession account = new Imap.AccountSession( - this.information.id, this.local.imap_folder_root, client + this.local.imap_folder_root, client ); + account.set_logging_parent(this.imap); Imap.Folder? folder = null; GLib.Error? folder_err = null; @@ -368,8 +368,9 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account { if (folder_err == null) { try { folder_session = yield new Imap.FolderSession( - this.information.id, client, folder, cancellable + client, folder, cancellable ); + folder_session.set_logging_parent(this.imap); } catch (Error err) { folder_err = err; } diff --git a/src/engine/imap/api/imap-account-session.vala b/src/engine/imap/api/imap-account-session.vala index 63838b2f..ed713733 100644 --- a/src/engine/imap/api/imap-account-session.vala +++ b/src/engine/imap/api/imap-account-session.vala @@ -32,10 +32,8 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject { private Gee.List? status_collector = null; - internal AccountSession(string account_id, - FolderRoot root, - ClientSession session) { - base("%s:account".printf(account_id), session); + internal AccountSession(FolderRoot root, ClientSession session) { + base(session); this.root = root; session.list.connect(on_list_data); @@ -228,8 +226,8 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject { MailboxInformation mailbox_info = info_map.get(mailbox); if (response.status != Status.OK) { - message("Unable to get STATUS of %s: %s", mailbox.to_string(), response.to_string()); - message("STATUS command: %s", cmd.to_string()); + warning("Unable to get STATUS of %s: %s", mailbox.to_string(), response.to_string()); + warning("STATUS command: %s", cmd.to_string()); continue; } @@ -243,7 +241,7 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject { } } if (status == null) { - message("Unable to get STATUS of %s: not returned from server", mailbox.to_string()); + warning("Unable to get STATUS of %s: not returned from server", mailbox.to_string()); continue; } status_results.remove(status); @@ -294,6 +292,16 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject { return old_session; } + /** {@inheritDoc} */ + public override Logging.State to_logging_state() { + return new Logging.State( + this, + "%s, folder root: %s", + base.to_logging_state().format_message(), // XXX this is cruddy + this.root.to_string() + ); + } + // Performs a LIST against the server, returning the results private async Gee.List send_list_async(ClientSession session, FolderPath folder, diff --git a/src/engine/imap/api/imap-client-service.vala b/src/engine/imap/api/imap-client-service.vala index 724942f2..26d4aa6d 100644 --- a/src/engine/imap/api/imap-client-service.vala +++ b/src/engine/imap/api/imap-client-service.vala @@ -143,8 +143,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { } if (this.all_sessions.size > 0) { - debug("[%s] Cancelling remaining client sessions...", - this.account.id); + debug("Cancelling remaining client sessions..."); this.close_cancellable.cancel(); } } @@ -447,6 +446,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { } ClientSession new_session = new ClientSession(remote); + new_session.set_logging_parent(this); yield new_session.connect_async(cancellable); try { @@ -504,8 +504,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { } private async void disconnect_session(ClientSession session) { - debug("[%s] Logging out session %s", - this.account.id, session.to_string()); + debug("Logging out session: %s", session.to_string()); // Log out before removing the session since close() only // hangs around until all sessions have been removed before @@ -514,15 +513,14 @@ internal class Geary.Imap.ClientService : Geary.ClientService { yield session.logout_async(this.close_cancellable); yield remove_session_async(session); } catch (GLib.Error err) { - debug("[%s] Error logging out of session: %s", - this.account.id, err.message); + debug("Error logging out of session: %s", err.message); yield force_disconnect_session(session); } } private async void force_disconnect_session(ClientSession session) { - debug("[%s] Dropping session %s", this.account.id, session.to_string()); + debug("Dropping session: %s", session.to_string()); try { yield remove_session_async(session); @@ -552,6 +550,10 @@ internal class Geary.Imap.ClientService : Geary.ClientService { } private void on_disconnected(ClientSession session, ClientSession.DisconnectReason reason) { + debug( + "Session unexpected disconnect: %s: %s", + session.to_string(), reason.to_string() + ); this.remove_session_async.begin( session, (obj, res) => { diff --git a/src/engine/imap/api/imap-folder-session.vala b/src/engine/imap/api/imap-folder-session.vala index d07199b2..c94da4bc 100644 --- a/src/engine/imap/api/imap-folder-session.vala +++ b/src/engine/imap/api/imap-folder-session.vala @@ -32,11 +32,11 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { /** Determines if this folder immutable. */ public Trillian readonly { get; private set; default = Trillian.UNKNOWN; } - /** Determines if this folder accepts custom IMAP flags. */ - public Trillian accepts_user_flags { get; private set; default = Trillian.UNKNOWN; } + /** This folder's set of permanent IMAP flags. */ + public MessageFlags? permanent_flags { get; private set; default = null; } /** Determines if this folder accepts custom IMAP flags. */ - public MessageFlags? permanent_flags { get; private set; default = null; } + public Trillian accepts_user_flags { get; private set; default = Trillian.UNKNOWN; } /** * Set to true when it's detected that the server doesn't allow a @@ -87,12 +87,11 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { public signal void removed(SequenceNumber pos); - public async FolderSession(string account_id, - ClientSession session, + public async FolderSession(ClientSession session, Imap.Folder folder, Cancellable cancellable) throws Error { - base("%s:%s".printf(account_id, folder.path.to_string()), session); + base(session); this.folder = folder; if (folder.properties.attrs.is_no_select) { @@ -173,7 +172,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { } private void on_exists(int total) { - debug("%s EXISTS %d", to_string(), total); + debug("EXISTS %d", total); int old_total = this.folder.properties.select_examine_messages; this.folder.properties.set_select_examine_message_count(total); @@ -185,7 +184,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { } private void on_expunge(SequenceNumber pos) { - debug("%s EXPUNGE %s", to_string(), pos.to_string()); + debug("EXPUNGE %s", pos.to_string()); int old_total = this.folder.properties.select_examine_messages; if (old_total > 0) { @@ -206,15 +205,13 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { data.seq_num, (existing != null) ? data.combine(existing) : data ); } else { - debug("%s: FETCH (unsolicited): %s:", - to_string(), - data.to_string()); + debug("FETCH (unsolicited): %s:", data.to_string()); updated(data.seq_num, data); } } private void on_recent(int total) { - debug("%s RECENT %d", to_string(), total); + debug("RECENT %d", total); this.folder.properties.recent = total; recent(total); } @@ -227,11 +224,12 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { try { this.search_accumulator.add(new UID.checked(uid)); } catch (ImapError imaperr) { - debug("%s Unable to process SEARCH UID result: %s", to_string(), imaperr.message); + warning("Unable to process SEARCH UID result: %s", + imaperr.message); } } } else { - debug("%s Not handling unsolicited SEARCH response", to_string()); + debug("Not handling unsolicited SEARCH response"); } } @@ -280,8 +278,8 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { break; } } catch (ImapError ierr) { - debug("Unable to parse ResponseCode %s: %s", response_code.to_string(), - ierr.message); + warning("Unable to parse ResponseCode %s: %s", response_code.to_string(), + ierr.message); } } @@ -512,8 +510,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { if (retry_bad_header_fields_response(cmds)) { // The command failed, but it wasn't using the // header field hack, so retry it. - debug("Retryable server failure detected for %s: %s", - to_string(), err.message); + debug("Retryable server failure detected: %s", err.message); } else { throw err; } @@ -534,15 +531,14 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { // 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()); + message("Unable to list message #%s: No UID returned from server", + seq_num.to_string()); continue; } try { Geary.Email email = fetched_data_to_email( - to_string(), uid, fetched_data, fields, @@ -552,9 +548,8 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { preview_charset_specifier ); if (!email.fields.fulfills(fields)) { - message( - "%s: %s missing=%s fetched=%s", - to_string(), + warning( + "%s missing=%s fetched=%s", email.id.to_string(), fields.clear(email.fields).to_string(), fetched_data.to_string() @@ -564,8 +559,10 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { email_list.add(email); } catch (Error err) { - debug("%s: Unable to convert email for %s %s: %s", to_string(), uid.to_string(), - fetched_data.to_string(), err.message); + warning("Unable to convert email for %s %s: %s", + uid.to_string(), + fetched_data.to_string(), + err.message); } } }, cancellable); @@ -685,7 +682,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { try { response.response_code.get_copyuid(null, out src_uids, out dst_uids); } catch (ImapError ierr) { - debug("Unable to retrieve COPYUID UIDs: %s", ierr.message); + warning("Unable to retrieve COPYUID UIDs: %s", ierr.message); } if (src_uids != null && !src_uids.is_empty && @@ -795,8 +792,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { } } - private static Geary.Email fetched_data_to_email( - string folder_name, + private Geary.Email fetched_data_to_email( UID uid, FetchedData fetched_data, Geary.Email.Field required_fields, @@ -890,12 +886,11 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { } } else { warning( - "[%s] No header specifier \"%s\" found in response:", - folder_name, + "No header specifier \"%s\" found in response:", header_specifier.to_string() ); foreach (FetchBodyDataSpecifier specifier in fetched_data.body_data_map.keys) { - message("[%s] - has %s", folder_name, specifier.to_string()); + warning(" - has %s", specifier.to_string()); } } } @@ -908,7 +903,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { try { date = new RFC822.Date(value); } catch (GLib.Error err) { - debug( + warning( "Error parsing date from FETCH response: %s", err.message ); @@ -1006,10 +1001,10 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { fetched_data.body_data_map.get(preview_charset_specifier), fetched_data.body_data_map.get(preview_specifier))); } else { - message("[%s] No preview specifiers \"%s\" and \"%s\" found", folder_name, + warning("No preview specifiers \"%s\" and \"%s\" found", preview_specifier.to_string(), preview_charset_specifier.to_string()); foreach (FetchBodyDataSpecifier specifier in fetched_data.body_data_map.keys) - message("[%s] has %s", folder_name, specifier.to_string()); + warning(" - has %s", specifier.to_string()); } } @@ -1042,10 +1037,10 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { ); } } else { - message("[%s] No body specifier \"%s\" found", folder_name, - body_specifier.to_string()); + warning("No body specifier \"%s\" found", + body_specifier.to_string()); foreach (FetchBodyDataSpecifier specifier in fetched_data.body_data_map.keys) - message("[%s] has %s", folder_name, specifier.to_string()); + warning(" - has %s", specifier.to_string()); } } @@ -1093,6 +1088,20 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { return null; } + /** {@inheritDoc} */ + public override Logging.State to_logging_state() { + return new Logging.State( + this, + "%s, %s, ro: %s, permanent_flags: %s, accepts_user_flags: %s", + base.to_logging_state().format_message(), // XXX this is cruddy + this.folder.to_string(), + this.readonly.to_string(), + this.permanent_flags != null + ? this.permanent_flags.to_string() : "(none)", + this.accepts_user_flags.to_string() + ); + } + // HACK: See https://bugzilla.gnome.org/show_bug.cgi?id=714902 // // Detect when a server has returned a BAD response to FETCH @@ -1130,14 +1139,13 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject { case Status.NO: throw new ImapError.NOT_SUPPORTED( - "Request %s failed on %s: %s", cmd.to_string(), - to_string(), response.to_string() + "Request %s failed: %s", cmd.to_string(), response.to_string() ); default: throw new ImapError.SERVER_ERROR( - "Unknown response status to %s on %s: %s", - cmd.to_string(), to_string(), response.to_string() + "Unknown response status to %s: %s", + cmd.to_string(), response.to_string() ); } } diff --git a/src/engine/imap/api/imap-folder.vala b/src/engine/imap/api/imap-folder.vala index 2144bab8..5e8028ce 100644 --- a/src/engine/imap/api/imap-folder.vala +++ b/src/engine/imap/api/imap-folder.vala @@ -31,4 +31,8 @@ internal class Geary.Imap.Folder : Geary.BaseObject { this.properties = properties; } + public string to_string() { + return "Imap.Folder(%s)".printf(path.to_string()); + } + } diff --git a/src/engine/imap/api/imap-session-object.vala b/src/engine/imap/api/imap-session-object.vala index 8391fde1..248f9b70 100644 --- a/src/engine/imap/api/imap-session-object.vala +++ b/src/engine/imap/api/imap-session-object.vala @@ -17,13 +17,21 @@ * * This class is ''not'' thread safe. */ -public abstract class Geary.Imap.SessionObject : BaseObject { +public abstract class Geary.Imap.SessionObject : BaseObject, Logging.Source { /** Determines if this object has a valid session or not. */ public bool is_valid { get { return this.session != null; } } - private string id; + /** {@inheritDoc} */ + public Logging.Flag logging_flags { + get; protected set; default = Logging.Flag.ALL; + } + + /** {@inheritDoc} */ + public Logging.Source? logging_parent { get { return _logging_parent; } } + private weak Logging.Source? _logging_parent = null; + private ClientSession? session; @@ -34,15 +42,14 @@ public abstract class Geary.Imap.SessionObject : BaseObject { /** * Constructs a new IMAP object with the given session. */ - protected SessionObject(string id, ClientSession session) { - this.id = id; + protected SessionObject(ClientSession session) { this.session = session; this.session.disconnected.connect(on_disconnected); } ~SessionObject() { if (close() != null) { - debug("%s: destroyed without releasing its session".printf(this.id)); + debug("Destroyed without releasing its session"); } } @@ -67,16 +74,19 @@ public abstract class Geary.Imap.SessionObject : BaseObject { return old_session; } - /** - * Returns a string representation of this object for debugging. - */ - public virtual string to_string() { - return "%s:%s".printf( - this.id, - this.session != null ? this.session.to_string() : "(session dropped)" + /** {@inheritDoc} */ + public virtual Logging.State to_logging_state() { + return new Logging.State( + this, + this.session != null ? this.session.to_string() : "no session" ); } + /** Sets the session's logging parent. */ + internal void set_logging_parent(Logging.Source parent) { + this._logging_parent = parent; + } + /** * Obtains IMAP session the server for use by this object. * @@ -93,7 +103,7 @@ public abstract class Geary.Imap.SessionObject : BaseObject { } private void on_disconnected(ClientSession.DisconnectReason reason) { - debug("%s: DISCONNECTED %s", to_string(), reason.to_string()); + debug("DISCONNECTED %s", reason.to_string()); close(); disconnected(reason); diff --git a/src/engine/imap/transport/imap-client-connection.vala b/src/engine/imap/transport/imap-client-connection.vala index fd9e2f1b..7513adb7 100644 --- a/src/engine/imap/transport/imap-client-connection.vala +++ b/src/engine/imap/transport/imap-client-connection.vala @@ -6,7 +6,7 @@ * (version 2.1 or later). See the COPYING file in this distribution. */ -public class Geary.Imap.ClientConnection : BaseObject { +public class Geary.Imap.ClientConnection : BaseObject, Logging.Source { /** * Default socket timeout duration. @@ -58,6 +58,16 @@ public class Geary.Imap.ClientConnection : BaseObject { */ public bool idle_when_quiet { get; private set; default = false; } + + /** {@inheritDoc} */ + public Logging.Flag logging_flags { + get; protected set; default = Logging.Flag.NETWORK; + } + + /** {@inheritDoc} */ + public Logging.Source? logging_parent { get { return _logging_parent; } } + private weak Logging.Source? _logging_parent = null; + private Geary.Endpoint endpoint; private SocketConnection? cx = null; private IOStream? ios = null; @@ -80,59 +90,56 @@ public class Geary.Imap.ClientConnection : BaseObject { public virtual signal void connected() { - Logging.debug(Logging.Flag.NETWORK, "[%s] connected to %s", to_string(), - endpoint.to_string()); + debug("Connected to %s", endpoint.to_string()); } public virtual signal void disconnected() { - Logging.debug(Logging.Flag.NETWORK, "[%s] disconnected from %s", to_string(), - endpoint.to_string()); + debug("Disconnected from %s", endpoint.to_string()); } public virtual signal void sent_command(Command cmd) { - Logging.debug(Logging.Flag.NETWORK, "[%s S] %s", to_string(), cmd.to_string()); + debug("SEND: %s", cmd.to_string()); } public virtual signal void received_status_response(StatusResponse status_response) { - Logging.debug(Logging.Flag.NETWORK, "[%s R] %s", to_string(), status_response.to_string()); + debug("RECV: %s", status_response.to_string()); } public virtual signal void received_server_data(ServerData server_data) { - Logging.debug(Logging.Flag.NETWORK, "[%s R] %s", to_string(), server_data.to_string()); + debug("RECV: %s", 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()); + debug("RECV: %s", 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 received_bad_response(RootParameters root, + ImapError err) { + warning("Received bad response: %s", err.message); } public virtual signal void received_eos() { - Logging.debug(Logging.Flag.NETWORK, "[%s] recv eos", to_string()); + debug("Received eos"); } public virtual signal void send_failure(Error err) { - Logging.debug(Logging.Flag.NETWORK, "[%s] send failure: %s", to_string(), err.message); + warning("Send failure: %s", err.message); } public virtual signal void receive_failure(Error err) { - Logging.debug(Logging.Flag.NETWORK, "[%s] recv failure: %s", to_string(), err.message); + warning("Receive failure: %s", err.message); } public virtual signal void deserialize_failure(Error err) { - Logging.debug(Logging.Flag.NETWORK, "[%s] deserialize failure: %s", to_string(), - err.message); + warning("Deserialize failure: %s", err.message); } public virtual signal void close_error(Error err) { - Logging.debug(Logging.Flag.NETWORK, "[%s] close error: %s", to_string(), err.message); + warning("Close error: %s", err.message); } @@ -148,30 +155,22 @@ 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); + /** Returns the remote address of this connection, if any. */ + public GLib.SocketAddress? get_remote_address() throws GLib.Error { + GLib.SocketAddress? addr = null; + if (cx != null) { + addr = cx.get_remote_address(); } - - return null; + return addr; } - 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); + /** Returns the local address of this connection, if any. */ + public SocketAddress? get_local_address() throws GLib.Error { + GLib.SocketAddress? addr = null; + if (cx != null) { + addr = cx.get_local_address(); } - - return null; + return addr; } /** @@ -203,12 +202,12 @@ public class Geary.Imap.ClientConnection : BaseObject { } /** - * Returns silently if a connection is already established. + * Establishes a connection to the connection's endpoint. */ - public async void connect_async(Cancellable? cancellable = null) throws Error { + public async void connect_async(Cancellable? cancellable = null) + throws GLib.Error { if (this.cx != null) { - debug("Already connected/connecting to %s", to_string()); - return; + throw new ImapError.ALREADY_CONNECTED("Client already connected"); } this.cx = yield endpoint.connect_async(cancellable); @@ -258,10 +257,7 @@ public class Geary.Imap.ClientConnection : BaseObject { // Cancel any pending commands foreach (Command pending in this.pending_queue.get_all()) { - debug( - "[%s] Cancelling pending command: %s", - to_string(), pending.to_brief_string() - ); + debug("Cancelling pending command: %s", pending.to_brief_string()); pending.disconnected("Disconnected"); } this.pending_queue.clear(); @@ -269,36 +265,38 @@ public class Geary.Imap.ClientConnection : BaseObject { // close the actual streams and the connection itself Error? close_err = null; try { - debug("[%s] Disconnecting...", to_string()); yield ios.close_async(Priority.DEFAULT, cancellable); yield close_cx.close_async(Priority.DEFAULT, cancellable); - debug("[%s] Disconnected", to_string()); } catch (Error err) { - debug("[%s] Error disconnecting: %s", to_string(), err.message); close_err = err; } finally { ios = null; - if (close_err != null) + if (close_err != null) { close_error(close_err); + } disconnected(); } } - 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()); + public async void starttls_async(Cancellable? cancellable = null) + throws GLib.Error { + if (cx == null) { + throw new ImapError.NOT_CONNECTED( + "Cannot start TLS when not connected" + ); + } // (mostly) silent fail in this case if (cx is TlsClientConnection) { - debug("[%s] Already TLS connection", to_string()); - - return; + throw new ImapError.NOT_SUPPORTED( + "Cannot start TLS when already established" + ); } // Close the Serializer/Deserializer, as need to use the TLS streams - debug("[%s] Closing serializer to switch to TLS", to_string()); + debug("Closing serializer to switch to TLS"); yield close_channels_async(cancellable); // wrap connection with TLS connection @@ -319,11 +317,14 @@ public class Geary.Imap.ClientConnection : BaseObject { cancel_idle(); } - public string to_string() { - return "%04X/%s/%s".printf( + /** {@inheritDoc} */ + public Logging.State to_logging_state() { + return new Logging.State( + this, + "%04X/%s/%s", cx_id, endpoint.to_string(), - this.cx != null ? "Connected" : "Disconnected" + this.cx != null ? "up" : "down" ); } @@ -346,6 +347,11 @@ public class Geary.Imap.ClientConnection : BaseObject { return sent; } + /** Sets the connection's logging parent. */ + internal void set_logging_parent(Logging.Source parent) { + this._logging_parent = parent; + } + private async void open_channels_async() throws Error { assert(ios != null); assert(ser == null); @@ -381,10 +387,7 @@ public class Geary.Imap.ClientConnection : BaseObject { // underlying streams are going away. this.open_cancellable.cancel(); foreach (Command sent in this.sent_queue) { - debug( - "[%s] Cancelling sent command: %s", - to_string(), sent.to_brief_string() - ); + debug("Cancelling sent command: %s", sent.to_brief_string()); sent.disconnected("Connection channels closed"); } this.sent_queue.clear(); @@ -517,8 +520,8 @@ public class Geary.Imap.ClientConnection : BaseObject { on_continuation_response((ContinuationResponse) response); } else { warning( - "[%s] Unknown ServerResponse of type %s received: %s:", - to_string(), response.get_type().name(), + "Unknown ServerResponse of type %s received: %s:", + response.get_type().name(), response.to_string() ); } @@ -619,11 +622,11 @@ public class Geary.Imap.ClientConnection : BaseObject { } private void on_idle_timeout() { - Logging.debug(Logging.Flag.NETWORK, "[%s] Initiating IDLE", to_string()); + debug("Initiating IDLE"); try { this.send_command(new IdleCommand()); } catch (ImapError err) { - debug("[%s] Error sending IDLE: %s", to_string(), err.message); + warning("Error sending IDLE: %s", err.message); } } diff --git a/src/engine/imap/transport/imap-client-session.vala b/src/engine/imap/transport/imap-client-session.vala index c368ab32..a40c5986 100644 --- a/src/engine/imap/transport/imap-client-session.vala +++ b/src/engine/imap/transport/imap-client-session.vala @@ -24,7 +24,7 @@ * sending a {@link LoginCommand}. Other commands can be sent via * {@link send_command_async} and {@link send_multiple_commands_async}. */ -public class Geary.Imap.ClientSession : BaseObject { +public class Geary.Imap.ClientSession : BaseObject, Logging.Source { /** * Maximum keep-alive interval required to maintain a session. @@ -187,7 +187,7 @@ public class Geary.Imap.ClientSession : BaseObject { } private static string state_to_string(uint state) { - return ((State) state).to_string(); + return ObjectUtils.to_enum_nick(typeof(State), state); } private enum Event { @@ -245,6 +245,14 @@ public class Geary.Imap.ClientSession : BaseObject { */ public int64 last_seen = 0; + /** {@inheritDoc} */ + public Logging.Flag logging_flags { + get; protected set; default = Logging.Flag.ALL; + } + + /** {@inheritDoc} */ + public Logging.Source? logging_parent { get { return _logging_parent; } } + private weak Logging.Source? _logging_parent = null; // While the following inbox and namespace data should be server // specific, there is a small chance they will differ between @@ -483,10 +491,8 @@ public class Geary.Imap.ClientSession : BaseObject { break; default: - warning("[%s] ClientSession ref dropped while still active", to_string()); + warning("ClientSession ref dropped while still active"); } - - debug("DTOR: ClientSession %s", to_string()); } public MailboxSpecifier? get_current_mailbox() { @@ -709,8 +715,10 @@ public class Geary.Imap.ClientSession : BaseObject { try { yield disconnect_async(cancellable); } catch (Error err) { - debug("[%s] Error disconnecting after a failed connect attempt: %s", to_string(), - err.message); + warning( + "Error disconnecting after a failed connect attempt: %s", + err.message + ); } throw connect_err; @@ -730,6 +738,7 @@ public class Geary.Imap.ClientSession : BaseObject { assert(cx == null); cx = new ClientConnection(imap_endpoint); + cx.set_logging_parent(this); cx.connected.connect(on_network_connected); cx.disconnected.connect(on_network_disconnected); cx.sent_command.connect(on_network_sent_command); @@ -775,9 +784,7 @@ public class Geary.Imap.ClientSession : BaseObject { } private uint on_connected(uint state, uint event) { - debug("[%s] Connected to %s", - to_string(), - imap_endpoint.to_string()); + debug("Connected to %s", imap_endpoint.to_string()); // stay in current state -- wait for initial status response // to move into NOAUTH or LOGOUT @@ -789,9 +796,7 @@ public class Geary.Imap.ClientSession : BaseObject { void *user = null, GLib.Object? obj = null, GLib.Error? err = null) { - debug("[%s] Disconnected from %s", - to_string(), - this.imap_endpoint.to_string()); + debug("Disconnected from %s", this.imap_endpoint.to_string()); return State.CLOSED; } @@ -802,8 +807,10 @@ public class Geary.Imap.ClientSession : BaseObject { try { connect_waiter.notify(); } catch (Error err) { - message("[%s] Unable to notify connect_waiter of connection: %s", to_string(), - err.message); + warning( + "Unable to notify connect_waiter of connection: %s", + err.message + ); } if (status_response.status == Status.OK) { @@ -812,8 +819,6 @@ public class Geary.Imap.ClientSession : BaseObject { return State.NOAUTH; } - debug("[%s] Connect denied: %s", to_string(), status_response.to_string()); - fsm.do_post_transition(() => { session_denied(status_response.get_text()); }); // Don't need to manually disconnect here, by setting @@ -830,12 +835,11 @@ public class Geary.Imap.ClientSession : BaseObject { try { connect_waiter.notify(); } catch (Error err) { - message("[%s] Unable to notify connect_waiter of timeout: %s", to_string(), - err.message); + warning( + "Unable to notify connect_waiter of timeout: %s", err.message + ); } - debug("[%s] Connect timed-out", to_string()); - // Don't need to manually disconnect here, by setting // connect_err here that will be done in connect_async this.connect_err = new IOError.TIMED_OUT( @@ -957,28 +961,15 @@ public class Geary.Imap.ClientSession : BaseObject { "STARTTLS unavailable for %s", to_string()); } - debug("[%s] Attempting STARTTLS...", to_string()); - StatusResponse resp; - try { - resp = yield send_command_async( - new StarttlsCommand(), cancellable - ); - } catch (Error err) { - debug( - "Error attempting STARTTLS command on %s: %s", - to_string(), err.message - ); - throw err; - } + debug("Attempting STARTTLS..."); + StatusResponse resp = yield send_command_async( + new StarttlsCommand(), cancellable + ); if (resp.status == Status.OK) { yield cx.starttls_async(cancellable); - debug("[%s] STARTTLS completed", to_string()); + debug("STARTTLS completed"); } else { - debug( - "[%s} STARTTLS refused: %s", - to_string(), resp.status.to_string() - ); // Throw an exception and fail rather than send // credentials under suspect conditions throw new ImapError.NOT_SUPPORTED( @@ -1009,7 +1000,7 @@ public class Geary.Imap.ClientSession : BaseObject { ); if (response.status == Status.OK && !server_data.is_empty) { this.inbox = server_data[0].get_list(); - debug("[%s] Using as INBOX: %s", to_string(), this.inbox.to_string()); + debug("Using INBOX: %s", this.inbox.to_string()); } else { throw new ImapError.INVALID("Unable to find INBOX"); } @@ -1027,14 +1018,16 @@ public class Geary.Imap.ClientSession : BaseObject { update_namespaces(ns.user, this.user_namespaces); update_namespaces(ns.shared, this.shared_namespaces); } else { - debug("[%s] NAMESPACE command failed", to_string()); + warning("NAMESPACE command failed"); } } server_data.clear(); if (!this.personal_namespaces.is_empty) { - debug("[%s] Default personal namespace: %s", to_string(), this.personal_namespaces[0].to_string()); + debug( + "Default personal namespace: %s", + this.personal_namespaces[0].to_string() + ); } else { - debug("[%s] Personal namespace not found, guessing it", to_string()); string? prefix = ""; string? delim = this.inbox.delim; if (!this.inbox.attrs.contains(MailboxAttribute.NO_INFERIORS) && @@ -1061,8 +1054,8 @@ public class Geary.Imap.ClientSession : BaseObject { } this.personal_namespaces.add(new Namespace(prefix, delim)); - debug("[%s] Personal namespace guessed as: %s", - to_string(), this.personal_namespaces[0].to_string()); + debug("Personal namespace not specified, guessed as: %s", + this.personal_namespaces[0].to_string()); } } finally { disconnect(data_id); @@ -1114,7 +1107,7 @@ public class Geary.Imap.ClientSession : BaseObject { return State.AUTHORIZED; default: - debug("[%s] Unable to LOGIN: %s", to_string(), completion_response.to_string()); + debug("LOGIN failed: %s", completion_response.to_string()); fsm.do_post_transition((resp) => { login_failed((StatusResponse)resp); }, completion_response); return State.NOAUTH; @@ -1226,7 +1219,7 @@ public class Geary.Imap.ClientSession : BaseObject { keepalive_id = 0; send_command_async.begin(new NoopCommand(), null, on_keepalive_completed); - Logging.debug(Logging.Flag.PERIODIC, "[%s] Sending keepalive...", to_string()); + log(PERIODIC, LEVEL_DEBUG, "Sending keepalive..."); // No need to reschedule keepalive, as the notification that the command was sent should // do that automatically @@ -1236,11 +1229,9 @@ public class Geary.Imap.ClientSession : BaseObject { private void on_keepalive_completed(Object? source, AsyncResult result) { try { - StatusResponse response = send_command_async.end(result); - Logging.debug(Logging.Flag.PERIODIC, "[%s] Keepalive result: %s", to_string(), - response.to_string()); - } catch (Error err) { - debug("[%s] Keepalive error: %s", to_string(), err.message); + send_command_async.end(result); + } catch (GLib.Error err) { + log(PERIODIC, LEVEL_WARNING, "Keepalive error: %s", err.message); } } @@ -1346,8 +1337,8 @@ public class Geary.Imap.ClientSession : BaseObject { break; case Status.BYE: - debug("[%s] Received unilateral BYE from server: %s", - to_string(), status_response.to_string()); + debug("Received unilateral BYE from server: %s", + 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); @@ -1356,7 +1347,8 @@ public class Geary.Imap.ClientSession : BaseObject { break; default: - debug("[%s] Received error from server: %s", to_string(), status_response.to_string()); + debug("Received error from server: %s", + status_response.to_string()); break; } @@ -1454,7 +1446,8 @@ public class Geary.Imap.ClientSession : BaseObject { return State.SELECTED; default: - debug("[%s]: Unable to SELECT/EXAMINE: %s", to_string(), completion_response.to_string()); + warning("SELECT/EXAMINE failed: %s", + completion_response.to_string()); return State.AUTHORIZED; } } @@ -1510,7 +1503,7 @@ public class Geary.Imap.ClientSession : BaseObject { return State.AUTHORIZED; default: - debug("[%s] Unable to CLOSE: %s", to_string(), completion_response.to_string()); + warning("CLOSE failed: %s", completion_response.to_string()); return State.SELECTED; } @@ -1572,13 +1565,13 @@ public class Geary.Imap.ClientSession : BaseObject { case Status.BYE: // We're expecting this bye, but don't disconnect yet // since we'll do that when the command is complete - debug("[%s] Received bye from server on logout: %s", - to_string(), status_response.to_string()); + debug("Received bye from server on logout: %s", + status_response.to_string()); break; default: - debug("[%s] Received error from server on logout: %s", - to_string(), status_response.to_string()); + warning("Received error from server on logout: %s", + status_response.to_string()); break; } @@ -1623,6 +1616,35 @@ public class Geary.Imap.ClientSession : BaseObject { throw disconnect_err; } + /** {@inheritDoc} */ + public string to_string() { + string cx = ", " + ( + (this.cx != null) ? this.cx.to_string() : "not connected" + ); + return Logging.Source.default_to_string(this, cx); + } + + /** {@inheritDoc} */ + public Logging.State to_logging_state() { + return (this.current_mailbox == null) + ? new Logging.State( + this, + this.fsm.get_state_string(fsm.get_state()) + ) + : new Logging.State( + this, + "%s:%s %s", + this.fsm.get_state_string(fsm.get_state()), + this.current_mailbox.to_string(), + this.current_mailbox_readonly ? "RO" : "RW" + ); + } + + /** Sets the connection's logging parent. */ + internal void set_logging_parent(Logging.Source parent) { + this._logging_parent = parent; + } + private uint on_disconnect(uint state, uint event, void *user, Object? object) { MachineParams params = (MachineParams) object; @@ -1645,8 +1667,7 @@ public class Geary.Imap.ClientSession : BaseObject { void *user, GLib.Object? object, GLib.Error? err) { - debug("[%s] Connecting send/recv error, dropping client connection: %s", - to_string(), + debug("Connecting send/recv error, dropping client connection: %s", err != null ? err.message : "EOS"); fsm.do_post_transition(() => { drop_connection(); }); return State.CLOSED; @@ -1658,7 +1679,7 @@ public class Geary.Imap.ClientSession : BaseObject { if (err is IOError.CANCELLED) return state; - debug("[%s] Send error, disconnecting: %s", to_string(), err.message); + debug("Send error, disconnecting: %s", err.message); cx.disconnect_async.begin(null, on_fire_send_error_signal); @@ -1674,8 +1695,7 @@ public class Geary.Imap.ClientSession : BaseObject { void *user, GLib.Object? object, GLib.Error? err) { - debug("[%s] Receive error, disconnecting: %s", - to_string(), + debug("Receive error, disconnecting: %s", (err != null) ? err.message : "EOS" ); cx.disconnect_async.begin(null, on_fire_recv_error_signal); @@ -1687,16 +1707,13 @@ public class Geary.Imap.ClientSession : BaseObject { } 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); + debug("Send/recv disconnect failed: %s", err.message); } drop_connection(); - disconnected(reason); } @@ -1745,8 +1762,9 @@ public class Geary.Imap.ClientSession : BaseObject { 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()); + debug("Dropped server response at %s: %s", + fsm.get_event_issued_string(state, event), + server_response.to_string()); return state; } @@ -1762,7 +1780,8 @@ public class Geary.Imap.ClientSession : BaseObject { } private uint on_ignored_transition(uint state, uint event) { - debug("[%s] Ignored transition: %s", to_string(), fsm.get_event_issued_string(state, event)); + debug("Ignored transition: %s", + fsm.get_event_issued_string(state, event)); return state; } @@ -1826,14 +1845,17 @@ public class Geary.Imap.ClientSession : BaseObject { try { if (response_code.get_response_code_type().is_value(ResponseCodeType.CAPABILITY)) { capabilities = response_code.get_capabilities(ref next_capabilities_revision); - debug("[%s] %s %s", to_string(), status_response.status.to_string(), + debug("%s %s", + status_response.status.to_string(), capabilities.to_string()); capability(capabilities); } - } catch (Error err) { - debug("[%s] Unable to convert response code to capabilities: %s", to_string(), - err.message); + } catch (GLib.Error err) { + warning( + "Unable to convert response code to capabilities: %s", + err.message + ); } } @@ -1855,8 +1877,9 @@ public class Geary.Imap.ClientSession : BaseObject { // update ClientSession capabilities before firing signal, so external signal // handlers that refer back to property aren't surprised 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()); + debug("%s %s", + server_data.server_data_type.to_string(), + capabilities.to_string()); capability(capabilities); break; @@ -1902,8 +1925,8 @@ public class Geary.Imap.ClientSession : BaseObject { case ServerDataType.LSUB: default: // do nothing - debug("[%s] Not notifying of unhandled server data: %s", to_string(), - server_data.to_string()); + debug("Not notifying of unhandled server data: %s", + server_data.to_string()); break; } @@ -1920,8 +1943,9 @@ public class Geary.Imap.ClientSession : BaseObject { try { notify_received_data(server_data); } catch (ImapError ierr) { - debug("[%s] Failure notifying of server data: %s %s", to_string(), server_data.to_string(), - ierr.message); + debug("Failure notifying of server data: %s %s", + server_data.to_string(), + ierr.message); } } @@ -1940,7 +1964,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); + debug("Received bad response %s: %s", root.to_string(), err.message); fsm.issue(Event.RECV_ERROR, null, null, err); } @@ -1952,12 +1976,4 @@ public class Geary.Imap.ClientSession : BaseObject { 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())); - } else { - return "%04X/%s %s".printf(cx.cx_id, imap_endpoint.to_string(), - fsm.get_state_string(fsm.get_state())); - } - } } From dc665d20dd8bc97f2f501f88072fb527ec08de71 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Mon, 2 Dec 2019 16:31:42 +1100 Subject: [PATCH 13/16] Update Geary.ImapEngine classes to implement Geary.Logging.Source --- .../imap-engine-account-processor.vala | 31 ++++++++-- .../imap-engine-account-synchronizer.vala | 58 ++++++++++--------- .../imap-engine-generic-account.vala | 3 +- .../imap-engine/account-processor-test.vala | 2 +- 4 files changed, 60 insertions(+), 34 deletions(-) diff --git a/src/engine/imap-engine/imap-engine-account-processor.vala b/src/engine/imap-engine/imap-engine-account-processor.vala index 49752361..561c9c62 100644 --- a/src/engine/imap-engine/imap-engine-account-processor.vala +++ b/src/engine/imap-engine/imap-engine-account-processor.vala @@ -17,7 +17,8 @@ * occurs the error will be suppressed and it will be re-attempted * once, to allow for the network dropping out mid-execution. */ -internal class Geary.ImapEngine.AccountProcessor : Geary.BaseObject { +internal class Geary.ImapEngine.AccountProcessor : + Geary.BaseObject, Logging.Source { // Retry ops after network failures at least once before giving up @@ -38,8 +39,15 @@ internal class Geary.ImapEngine.AccountProcessor : Geary.BaseObject { /** Fired when an error occurs processing an operation. */ public signal void operation_error(AccountOperation op, Error error); + /** {@inheritDoc} */ + public Logging.Flag logging_flags { + get; protected set; default = Logging.Flag.ALL; + } + + /** {@inheritDoc} */ + public Logging.Source? logging_parent { get { return _logging_parent; } } + private weak Logging.Source? _logging_parent = null; - private string id; private bool is_running; @@ -50,8 +58,7 @@ internal class Geary.ImapEngine.AccountProcessor : Geary.BaseObject { private GLib.Cancellable? op_cancellable = null; - public AccountProcessor(string id) { - this.id = id; + public AccountProcessor() { this.queue.allow_duplicates = false; this.is_running = true; this.run.begin(); @@ -72,6 +79,20 @@ internal class Geary.ImapEngine.AccountProcessor : Geary.BaseObject { this.queue.clear(); } + /** {@inheritDoc} */ + public virtual Logging.State to_logging_state() { + return new Logging.State( + this, + "queued: %d", + this.queue.size + ); + } + + /** Sets the processor's logging parent. */ + internal void set_logging_parent(Logging.Source parent) { + this._logging_parent = parent; + } + private async void run() { while (this.is_running) { this.op_cancellable = new GLib.Cancellable(); @@ -85,7 +106,7 @@ internal class Geary.ImapEngine.AccountProcessor : Geary.BaseObject { } if (op != null) { - debug("%s: Executing operation: %s", id, op.to_string()); + debug("Executing operation: %s", op.to_string()); this.current_op = op; Error? op_error = null; diff --git a/src/engine/imap-engine/imap-engine-account-synchronizer.vala b/src/engine/imap-engine/imap-engine-account-synchronizer.vala index 0bb7432b..5d399f24 100644 --- a/src/engine/imap-engine/imap-engine-account-synchronizer.vala +++ b/src/engine/imap-engine/imap-engine-account-synchronizer.vala @@ -6,7 +6,8 @@ * (version 2.1 or later). See the COPYING file in this distribution. */ -private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject { +private class Geary.ImapEngine.AccountSynchronizer : + Geary.BaseObject, Logging.Source { private weak GenericAccount account { get; private set; } @@ -28,6 +29,26 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject { this.account.folders_contents_altered.connect(on_folders_contents_altered); } + /** {@inheritDoc} */ + public Logging.Flag logging_flags { + get; protected set; default = Logging.Flag.ALL; + } + + /** {@inheritDoc} */ + public Logging.Source? logging_parent { + get { return this.account; } + } + + /** {@inheritDoc} */ + public virtual Logging.State to_logging_state() { + return new Logging.State( + this, + "%s, %s", + this.account.information.id, + this.max_epoch.to_string() + ); + } + private void send_all(Gee.Collection folders, bool became_available) { foreach (Folder folder in folders) { // Only sync folders that: @@ -53,7 +74,7 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject { try { this.account.queue_operation(op); } catch (Error err) { - debug("Failed to queue sync operation: %s", err.message); + warning("Failed to queue sync operation: %s", err.message); } } } @@ -122,7 +143,7 @@ private class Geary.ImapEngine.RefreshFolderSync : FolderOperation { try { yield minimal.open_async(Folder.OpenFlags.NO_DELAY, cancellable); was_opened = true; - debug("Synchronising %s", minimal.to_string()); + debug("Synchronising"); yield sync_folder(cancellable); } catch (GLib.IOError.CANCELLED err) { // All good @@ -255,11 +276,7 @@ private class Geary.ImapEngine.CheckFolderSync : RefreshFolderSync { next_epoch = prefetch_max_epoch; } - debug( - "Synchronising %s to: %s", - folder.to_string(), - next_epoch.to_string() - ); + debug("Fetching to: %s", next_epoch.to_string()); if (local_count < this.folder.properties.email_total && next_epoch.compare(prefetch_max_epoch) >= 0) { @@ -295,14 +312,7 @@ private class Geary.ImapEngine.CheckFolderSync : RefreshFolderSync { Geary.Email? current_oldest, Cancellable cancellable) throws Error { - // Expand the vector up until the given epoch - Logging.debug( - Logging.Flag.PERIODIC, - "Synchronizing %s:%s to %s", - this.account.to_string(), - this.folder.to_string(), - next_epoch.to_string() - ); + debug("Expanding vector to %s", next_epoch.to_string()); return yield ((MinimalFolder) this.folder).find_earliest_email_async( next_epoch, (current_oldest != null) ? current_oldest.id : null, @@ -323,11 +333,8 @@ private class Geary.ImapEngine.CheckFolderSync : RefreshFolderSync { // marker of age Geary.EmailIdentifier? id = (current_oldest != null) ? current_oldest.id : null; - Logging.debug( - Logging.Flag.PERIODIC, - "Unable to locate epoch messages on remote folder %s:%s%s, fetching one past oldest...", - this.account.to_string(), - this.folder.to_string(), + debug( + "Unable to locate epoch messages on remote folder%s, fetching one past oldest...", (id != null) ? " earlier than oldest local" : "" ); yield this.folder.list_email_by_id_async( @@ -342,12 +349,9 @@ private class Geary.ImapEngine.CheckFolderSync : RefreshFolderSync { private async void expand_complete_vector(Cancellable cancellable) throws Error { // past max_epoch, so just pull in everything and be done with it - Logging.debug( - Logging.Flag.PERIODIC, - "Synchronization reached max epoch of %s, fetching all mail from %s:%s", - this.sync_max_epoch.to_string(), - this.account.to_string(), - this.folder.to_string() + debug( + "Reached max epoch of %s, fetching all mail", + this.sync_max_epoch.to_string() ); // Per the contract for list_email_by_id_async, we need to diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala b/src/engine/imap-engine/imap-engine-generic-account.vala index 6f54c6a3..c6c0b54b 100644 --- a/src/engine/imap-engine/imap-engine-generic-account.vala +++ b/src/engine/imap-engine/imap-engine-generic-account.vala @@ -131,8 +131,9 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account { this.open_cancellable = new Cancellable(); this.remote_ready_lock = new Nonblocking.Semaphore(this.open_cancellable); - this.processor = new AccountProcessor(this.to_string()); + this.processor = new AccountProcessor(); this.processor.operation_error.connect(on_operation_error); + this.processor.set_logging_parent(this); try { yield this.local.open_async(cancellable); diff --git a/test/engine/imap-engine/account-processor-test.vala b/test/engine/imap-engine/account-processor-test.vala index 4f2ed50a..37456ff3 100644 --- a/test/engine/imap-engine/account-processor-test.vala +++ b/test/engine/imap-engine/account-processor-test.vala @@ -71,7 +71,7 @@ public class Geary.ImapEngine.AccountProcessorTest : TestCase { new RFC822.MailboxAddress(null, "test1@example.com") ); this.account = new Geary.MockAccount(this.info); - this.processor = new AccountProcessor("processor"); + this.processor = new AccountProcessor(); this.succeeded = 0; this.failed = 0; From 24a0ad70ae70bdc861653dd989cd8cf78a3f32fa Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Mon, 2 Dec 2019 18:19:52 +0800 Subject: [PATCH 14/16] Ensure some kind of logging source location is being printed If printing source and line number is not being printed (it isn't at the moment) then print the class name of the inner-most logging source to give an idea of where the message came from. --- src/engine/api/geary-logging.vala | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/engine/api/geary-logging.vala b/src/engine/api/geary-logging.vala index f5b1e097..07346156 100644 --- a/src/engine/api/geary-logging.vala +++ b/src/engine/api/geary-logging.vala @@ -256,21 +256,18 @@ public class Record { ); if (flags != NONE) { - str.append_printf("[%s]: ", flags.to_string()); + str.append_printf("[%s]:", flags.to_string()); } else { - str.append(": "); + str.append(":"); } // Append in reverse so inner sources appear first for (int i = this.states.length - 1; i >= 0; i--) { - str.append("["); + str.append(" ["); str.append(this.states[i].format_message()); - str.append("] "); + str.append("]"); } - str.append(message); - - // XXX Don't append source details for the moment because of // https://gitlab.gnome.org/GNOME/vala/issues/815 bool disabled = true; @@ -286,8 +283,17 @@ public class Record { str.append(this.source_function.to_string()); } str.append("]"); + } else if (this.states.length > 0) { + // Print the class name of the leaf logging source to at + // least give a general idea of where the message came + // from. + str.append(" "); + str.append(this.states[0].source.get_type().name()); + str.append(": "); } + str.append(message); + return str.str; } From d949a8e500f4edf1a92aadea86fa60c380a7b9f3 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Mon, 2 Dec 2019 18:21:21 +0800 Subject: [PATCH 15/16] Don't log non-enabled levels from Geary.Logging.Source We want to do this in the future, but only when we have better means of selecting which log messages to display or not. --- src/engine/util/util-logging.vala | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/engine/util/util-logging.vala b/src/engine/util/util-logging.vala index ef823705..4161aad7 100644 --- a/src/engine/util/util-logging.vala +++ b/src/engine/util/util-logging.vala @@ -217,14 +217,16 @@ public interface Geary.Logging.Source : GLib.Object { GLib.LogLevelFlags levels, string fmt, va_list args) { - Context context = Context(Logging.DOMAIN, flags, levels, fmt, args); - Source? decorated = this; - while (decorated != null) { - context.append_source(decorated); - decorated = decorated.logging_parent; - } + if (flags == ALL || Logging.get_flags().is_any_set(flags)) { + Context context = Context(Logging.DOMAIN, flags, levels, fmt, args); + Source? decorated = this; + while (decorated != null) { + context.append_source(decorated); + decorated = decorated.logging_parent; + } - GLib.log_structured_array(levels, context.to_array()); + GLib.log_structured_array(levels, context.to_array()); + } } } From 875909e4f43746aa77ee856a3a615a1c80b8592d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emin=20Tufan=20=C3=87etin?= Date: Mon, 2 Dec 2019 16:25:44 +0000 Subject: [PATCH 16/16] Update Turkish translation --- po/tr.po | 1551 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 873 insertions(+), 678 deletions(-) diff --git a/po/tr.po b/po/tr.po index 26ae4cdc..2837036e 100644 --- a/po/tr.po +++ b/po/tr.po @@ -16,8 +16,8 @@ msgid "" msgstr "" "Project-Id-Version: geary.mainline\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n" -"POT-Creation-Date: 2019-11-07 01:41+0000\n" -"PO-Revision-Date: 2019-11-10 09:05+0300\n" +"POT-Creation-Date: 2019-11-28 14:38+0000\n" +"PO-Revision-Date: 2019-12-02 19:23+0300\n" "Last-Translator: Emin Tufan Çetin \n" "Language-Team: Turkish \n" "Language: tr\n" @@ -40,6 +40,7 @@ msgstr "Dosyaları Geary kullanarak gönderin" #: desktop/org.gnome.Geary.appdata.xml.in.in:12 #: desktop/org.gnome.Geary.desktop.in.in:3 #: src/client/accounts/accounts-editor-servers-pane.vala:555 +#: src/client/application/application-main-window.vala:547 msgid "Geary" msgstr "Geary" @@ -52,7 +53,7 @@ msgstr "E-posta" #: desktop/geary-autostart.desktop.in.in:5 #: desktop/org.gnome.Geary.appdata.xml.in.in:16 #: desktop/org.gnome.Geary.desktop.in.in:5 -#: src/client/application/geary-application.vala:31 +#: src/client/application/application-client.vala:32 msgid "Send and receive email" msgstr "E-posta gönder ve al" @@ -131,6 +132,10 @@ msgstr "Posta;E-posta;Mail;E-mail;IMAP;GMail;Yahoo;Hotmail;Outlook;" msgid "Compose Message" msgstr "İleti Oluştur" +#: desktop/org.gnome.Geary.desktop.in.in:26 +msgid "New Window" +msgstr "Yeni Pencere" + #: desktop/org.gnome.Geary.gschema.xml:8 msgid "Maximize window" msgstr "Pencereyi büyüt" @@ -213,10 +218,24 @@ msgid "True if we should display a short preview of each message." msgstr "Her iletinin kısa bir ön izlemesini göstermemiz gerekiyorsa doğru." #: desktop/org.gnome.Geary.gschema.xml:68 +#| msgctxt "shortcut window" +#| msgid "Single-key shortcuts" +msgid "Use single key shortcuts" +msgstr "Tek tuşlu kısayolları kullan" + +#: desktop/org.gnome.Geary.gschema.xml:69 +msgid "" +"Enables shortcuts for email actions that do not require pressing to " +"emulate those used by Gmail." +msgstr "" +"Gmailʼin kullandığına benzemek için eposta eylemlerinde ʼye basmayı " +"gerektirmeyen kısayolları etkinleştirir." + +#: desktop/org.gnome.Geary.gschema.xml:76 msgid "Languages that shall be used in the spell checker" msgstr "Yazım denetleyicide kullanılacak diller" -#: desktop/org.gnome.Geary.gschema.xml:69 +#: desktop/org.gnome.Geary.gschema.xml:77 msgid "" "A list of POSIX locales, with the empty list disabling spell checking and " "the null list using desktop languages by default." @@ -224,11 +243,11 @@ msgstr "" "POSIX yerellerinin listesi, boş listeyle imla denetimi devre dışı bırakılır " "ve butlan (null) listeyle öntanımlı olarak masaüstü dillerini kullanılır." -#: desktop/org.gnome.Geary.gschema.xml:76 +#: desktop/org.gnome.Geary.gschema.xml:84 msgid "Languages that are displayed in the spell checker popover" msgstr "Yazım denetleyici açılır penceresinde gösterilecek diller" -#: desktop/org.gnome.Geary.gschema.xml:77 +#: desktop/org.gnome.Geary.gschema.xml:85 msgid "" "List of languages that are always displayed in the popover of the spell " "checker." @@ -236,62 +255,75 @@ msgstr "" "Yazım denetleyicinin açılır penceresinde her zaman gösterilecek dillerin " "listesi." -#: desktop/org.gnome.Geary.gschema.xml:82 +#: desktop/org.gnome.Geary.gschema.xml:90 msgid "Notify of new mail at startup" msgstr "Başlangıçta yeni postanın bildirilmesi" -#: desktop/org.gnome.Geary.gschema.xml:83 +#: desktop/org.gnome.Geary.gschema.xml:91 msgid "True to notify of new mail at startup." msgstr "Başlangıçta yeni postaların bildirilmesi için doğru." -#: desktop/org.gnome.Geary.gschema.xml:88 +#: desktop/org.gnome.Geary.gschema.xml:96 msgid "Ask when opening an attachment" msgstr "Ek açarken sor" -#: desktop/org.gnome.Geary.gschema.xml:89 +#: desktop/org.gnome.Geary.gschema.xml:97 msgid "True to ask when opening an attachment." msgstr "Eki açarken sormak için doğru." -#: desktop/org.gnome.Geary.gschema.xml:94 +#: desktop/org.gnome.Geary.gschema.xml:102 msgid "Whether to compose emails in HTML" msgstr "E-postaların HTML’de oluşturulup oluşturulmayacağı" -#: desktop/org.gnome.Geary.gschema.xml:95 +#: desktop/org.gnome.Geary.gschema.xml:103 msgid "True to compose emails in HTML; false for plain text." msgstr "E-postaları HTML’de oluşturmak için doğru, düz metin için yanlış." -#: desktop/org.gnome.Geary.gschema.xml:100 +#: desktop/org.gnome.Geary.gschema.xml:108 msgid "Advisory strategy for full-text searching" msgstr "Tam metin arama için tavsiye niteliğinde izlem" -#: desktop/org.gnome.Geary.gschema.xml:101 +#: desktop/org.gnome.Geary.gschema.xml:109 msgid "" "Acceptable values are “exact”, “conservative”, “aggressive”, and “horizon”." msgstr "" "Kabul edilebilir değerler şunlardır: “exact” (birebir), " "“conservative” (ılımlı), “aggressive” (sert) ve “horizon”." -#: desktop/org.gnome.Geary.gschema.xml:106 +#: desktop/org.gnome.Geary.gschema.xml:114 msgid "Zoom of conversation viewer" msgstr "Konuşma göstericisinin yakınlaşması" -#: desktop/org.gnome.Geary.gschema.xml:107 +#: desktop/org.gnome.Geary.gschema.xml:115 msgid "The zoom to apply on the conservation view." msgstr "Konuşma görünümünde uygulanacak yakınlaşma." -#: desktop/org.gnome.Geary.gschema.xml:112 +#: desktop/org.gnome.Geary.gschema.xml:120 msgid "Size of detached composer window" msgstr "Ayrılan oluşturucu penceresinin boyutu" -#: desktop/org.gnome.Geary.gschema.xml:113 +#: desktop/org.gnome.Geary.gschema.xml:121 msgid "The last recorded size of the detached composer window." msgstr "Ayrılmış oluşturucu penceresinin kaydedilen son boyutu." -#: desktop/org.gnome.Geary.gschema.xml:118 +#: desktop/org.gnome.Geary.gschema.xml:126 +#| msgid "Error sending email" +msgid "Undo sending email delay" +msgstr "Eposta göndermeyi geri alma gecikmesi" + +#: desktop/org.gnome.Geary.gschema.xml:127 +msgid "" +"The number of seconds to wait before sending an email. Set to zero or less " +"to disable." +msgstr "" +"Eposta gönderilmeden önce beklenecek saniye. Devre dışı bırakmak için sıfır " +"veya daha azına belirleyin." + +#: desktop/org.gnome.Geary.gschema.xml:133 msgid "Whether we migrated the old settings" msgstr "Eski ayarları taşıyıp taşımayacağımız" -#: desktop/org.gnome.Geary.gschema.xml:119 +#: desktop/org.gnome.Geary.gschema.xml:134 msgid "" "False to check for the old “org.yorba.geary”-schema and copy its values." msgstr "" @@ -301,24 +333,24 @@ msgstr "" #. Translators: In-app notification label, when #. the app had a problem pinning an otherwise #. untrusted TLS certificate -#: src/client/accounts/accounts-editor.vala:204 +#: src/client/accounts/accounts-editor.vala:210 msgid "Failed to store certificate" msgstr "Sertifika kaydetme başarısız oldu" #. Translators: Label for adding an email account #. account for a generic IMAP service provider. -#: src/client/accounts/accounts-editor-add-pane.vala:109 +#: src/client/accounts/accounts-editor-add-pane.vala:108 msgid "All others" msgstr "Tüm diğerleri" #. Translators: In-app notification label -#: src/client/accounts/accounts-editor-add-pane.vala:196 +#: src/client/accounts/accounts-editor-add-pane.vala:195 #: src/client/accounts/accounts-editor-servers-pane.vala:316 msgid "Check your receiving login and password" msgstr "Alıcı giriş ve parolanızı gözden geçirin" #. Translators: In-app notification label -#: src/client/accounts/accounts-editor-add-pane.vala:211 +#: src/client/accounts/accounts-editor-add-pane.vala:210 #: src/client/accounts/accounts-editor-servers-pane.vala:329 msgid "Check your receiving server details" msgstr "Alıcı sunucu ayrıntılarınızı gözden geçirin" @@ -328,51 +360,51 @@ msgstr "Alıcı sunucu ayrıntılarınızı gözden geçirin" #. succeeded, so the user probably needs to #. specify custom creds here #. Translators: In-app notification label -#: src/client/accounts/accounts-editor-add-pane.vala:233 +#: src/client/accounts/accounts-editor-add-pane.vala:232 #: src/client/accounts/accounts-editor-servers-pane.vala:350 msgid "Check your sending login and password" msgstr "Gönderici giriş ve parolanızı gözden geçirin" #. Translators: In-app notification label -#: src/client/accounts/accounts-editor-add-pane.vala:247 +#: src/client/accounts/accounts-editor-add-pane.vala:246 #: src/client/accounts/accounts-editor-servers-pane.vala:363 msgid "Check your sending server details" msgstr "Gönderici sunucu ayrıntılarınızı gözden geçirin" #. Translators: In-app notification label -#: src/client/accounts/accounts-editor-add-pane.vala:262 +#: src/client/accounts/accounts-editor-add-pane.vala:261 msgid "Check your email address and password" msgstr "E-posta adres ve parolanızı gözden geçirin" #. Translators: In-app notification label -#: src/client/accounts/accounts-editor-add-pane.vala:273 +#: src/client/accounts/accounts-editor-add-pane.vala:272 msgid "Could not connect, check your network" msgstr "Bağlanamadı, ağınızı gözden geçirin" #. Translators: In-app notification label for a #. generic error creating an account -#: src/client/accounts/accounts-editor-add-pane.vala:286 +#: src/client/accounts/accounts-editor-add-pane.vala:285 msgid "An unexpected problem occurred" msgstr "Beklenmedik sorun oluştu" #. Translators: In-app notification label, the #. string substitution is a more detailed reason. -#: src/client/accounts/accounts-editor-add-pane.vala:304 +#: src/client/accounts/accounts-editor-add-pane.vala:303 #, c-format msgid "Account not created: %s" msgstr "Hesap oluşturulmadı: %s" #. Translators: Label for the person's actual name when adding #. an account -#: src/client/accounts/accounts-editor-add-pane.vala:551 +#: src/client/accounts/accounts-editor-add-pane.vala:558 msgid "Your name" msgstr "Adınız" #. Translators: Label used for the address part of an #. email address when editing a user's sender address #. preferences for an account. -#: src/client/accounts/accounts-editor-add-pane.vala:568 -#: src/client/accounts/accounts-editor-edit-pane.vala:501 +#: src/client/accounts/accounts-editor-add-pane.vala:575 +#: src/client/accounts/accounts-editor-edit-pane.vala:513 msgid "Email address" msgstr "E-posta adresi" @@ -381,8 +413,8 @@ msgstr "E-posta adresi" #. Translators: This is used as a placeholder for the #. address part of an email address when editing a user's #. sender address preferences for an account. -#: src/client/accounts/accounts-editor-add-pane.vala:571 -#: src/client/accounts/accounts-editor-edit-pane.vala:469 +#: src/client/accounts/accounts-editor-add-pane.vala:579 +#: src/client/accounts/accounts-editor-edit-pane.vala:479 msgid "person@example.com" msgstr "kisi@ornek.com" @@ -390,15 +422,15 @@ msgstr "kisi@ornek.com" #. when adding an account #. Translators: Label for the user's login name for an #. IMAP, SMTP, etc service -#: src/client/accounts/accounts-editor-add-pane.vala:585 -#: src/client/accounts/accounts-editor-servers-pane.vala:880 +#: src/client/accounts/accounts-editor-add-pane.vala:593 +#: src/client/accounts/accounts-editor-servers-pane.vala:884 msgid "Login name" msgstr "Giriş adınız" #. Translators: Label for the user's password for an IMAP, #. SMTP, etc service -#: src/client/accounts/accounts-editor-add-pane.vala:599 -#: src/client/accounts/accounts-editor-servers-pane.vala:999 +#: src/client/accounts/accounts-editor-add-pane.vala:607 +#: src/client/accounts/accounts-editor-servers-pane.vala:1006 #: ui/password-dialog.glade:108 msgid "Password" msgstr "Parola" @@ -407,14 +439,14 @@ msgstr "Parola" #. adding an account. #. Translators: This label describes the host name or IP #. address and port used by an account's IMAP service. -#: src/client/accounts/accounts-editor-add-pane.vala:621 -#: src/client/accounts/accounts-editor-servers-pane.vala:727 +#: src/client/accounts/accounts-editor-add-pane.vala:629 +#: src/client/accounts/accounts-editor-servers-pane.vala:728 msgid "IMAP server" msgstr "IMAP sunucusu" #. Translators: Placeholder for the IMAP server hostname #. when adding an account. -#: src/client/accounts/accounts-editor-add-pane.vala:624 +#: src/client/accounts/accounts-editor-add-pane.vala:632 msgid "imap.example.com" msgstr "imap.ornek.com" @@ -422,20 +454,20 @@ msgstr "imap.ornek.com" #. adding an account. #. Translators: This label describes the host name or IP #. address and port used by an account's SMTP service. -#: src/client/accounts/accounts-editor-add-pane.vala:630 -#: src/client/accounts/accounts-editor-servers-pane.vala:733 +#: src/client/accounts/accounts-editor-add-pane.vala:638 +#: src/client/accounts/accounts-editor-servers-pane.vala:734 msgid "SMTP server" msgstr "SMTP sunucusu" #. Translators: Placeholder for the SMTP server hostname #. when adding an account. -#: src/client/accounts/accounts-editor-add-pane.vala:633 +#: src/client/accounts/accounts-editor-add-pane.vala:641 msgid "smtp.example.com" msgstr "smtp.ornek.com" #. Translators: Label in the account editor for the user's #. custom name for an account. -#: src/client/accounts/accounts-editor-edit-pane.vala:278 +#: src/client/accounts/accounts-editor-edit-pane.vala:277 #: ui/accounts_editor_remove_pane.ui:123 msgid "Account name" msgstr "Hesap adı" @@ -444,46 +476,46 @@ msgstr "Hesap adı" #. the name of an account. The string #. substitution is the old name of the #. account. -#: src/client/accounts/accounts-editor-edit-pane.vala:312 +#: src/client/accounts/accounts-editor-edit-pane.vala:318 #, c-format msgid "Change account name back to “%s”" msgstr "Hesap adınızı “%s” olarak değiştirin" #. Translators: Tooltip for adding a new email sender/from #. address's address to an account -#: src/client/accounts/accounts-editor-edit-pane.vala:336 +#: src/client/accounts/accounts-editor-edit-pane.vala:342 msgid "Add a new sender email address" msgstr "Yeni gönderen e-posta adresi ekle" #. Translators: Label used to indicate the user has #. provided no display name for one of their sender #. email addresses in their account settings. -#: src/client/accounts/accounts-editor-edit-pane.vala:417 +#: src/client/accounts/accounts-editor-edit-pane.vala:423 msgid "Name not set" msgstr "Ad belirlenmedi" #. Translators: This is used as a placeholder for the #. display name for an email address when editing a user's #. sender address preferences for an account. -#: src/client/accounts/accounts-editor-edit-pane.vala:456 +#: src/client/accounts/accounts-editor-edit-pane.vala:464 msgid "Sender Name" msgstr "Gönderen Adı" -#: src/client/accounts/accounts-editor-edit-pane.vala:479 +#: src/client/accounts/accounts-editor-edit-pane.vala:491 msgid "Remove" msgstr "Kaldır" #. Translators: Label used for the display name part of an #. email address when editing a user's sender address #. preferences for an account. -#: src/client/accounts/accounts-editor-edit-pane.vala:494 +#: src/client/accounts/accounts-editor-edit-pane.vala:506 msgid "Sender name" msgstr "Gönderen adı" #. Translators: Label used as the undo tooltip after adding an #. new sender email address to an account. The string #. substitution is the email address added. -#: src/client/accounts/accounts-editor-edit-pane.vala:561 +#: src/client/accounts/accounts-editor-edit-pane.vala:573 #, c-format msgid "Remove “%s”" msgstr "“%s” adresini kaldır" @@ -491,7 +523,7 @@ msgstr "“%s” adresini kaldır" #. Translators: Label used as the undo tooltip after editing a #. sender address for an account. The string substitution is #. the email address edited. -#: src/client/accounts/accounts-editor-edit-pane.vala:601 +#: src/client/accounts/accounts-editor-edit-pane.vala:613 #, c-format msgid "Undo changes to “%s”" msgstr "“%s” adresine yapılan değişikliği geri al" @@ -499,7 +531,7 @@ msgstr "“%s” adresine yapılan değişikliği geri al" #. Translators: Label used as the undo tooltip after removing #. a sender address from an account. The string substitution #. is the email address edited. -#: src/client/accounts/accounts-editor-edit-pane.vala:688 +#: src/client/accounts/accounts-editor-edit-pane.vala:700 #, c-format msgid "Add “%s” back" msgstr "“%s” adresini geri ekle" @@ -507,14 +539,14 @@ msgstr "“%s” adresini geri ekle" #. Translators: Label used as the undo tooltip after removing #. a sender address from an account. The string substitution #. is the email address edited. -#: src/client/accounts/accounts-editor-edit-pane.vala:730 +#: src/client/accounts/accounts-editor-edit-pane.vala:742 msgid "Undo signature changes" msgstr "İmza değişikliklerini geri al" #. Translators: This label describes the account #. preference for the length of time (weeks, months or #. years) that past email should be downloaded. -#: src/client/accounts/accounts-editor-edit-pane.vala:778 +#: src/client/accounts/accounts-editor-edit-pane.vala:790 msgid "Download mail" msgstr "Posta indir" @@ -523,56 +555,56 @@ msgstr "Posta indir" #. should be downloaded for an account. The #. string substitution is the duration, #. e.g. "1 month back". -#: src/client/accounts/accounts-editor-edit-pane.vala:810 +#: src/client/accounts/accounts-editor-edit-pane.vala:822 #, c-format msgid "Change download period back to: %s" msgstr "İndirme sıklığını şuna geri al: %s" -#: src/client/accounts/accounts-editor-edit-pane.vala:831 +#: src/client/accounts/accounts-editor-edit-pane.vala:843 msgid "Everything" msgstr "Her şey" -#: src/client/accounts/accounts-editor-edit-pane.vala:835 +#: src/client/accounts/accounts-editor-edit-pane.vala:847 msgid "2 weeks back" msgstr "2 hafta öncesinden" -#: src/client/accounts/accounts-editor-edit-pane.vala:839 +#: src/client/accounts/accounts-editor-edit-pane.vala:851 msgid "1 month back" msgstr "1 ay öncesinden" -#: src/client/accounts/accounts-editor-edit-pane.vala:843 +#: src/client/accounts/accounts-editor-edit-pane.vala:855 msgid "3 months back" msgstr "3 ay öncesinden" -#: src/client/accounts/accounts-editor-edit-pane.vala:847 +#: src/client/accounts/accounts-editor-edit-pane.vala:859 msgid "6 months back" msgstr "6 ay öncesinden" -#: src/client/accounts/accounts-editor-edit-pane.vala:851 +#: src/client/accounts/accounts-editor-edit-pane.vala:863 msgid "1 year back" msgstr "1 yıl öncesinden" -#: src/client/accounts/accounts-editor-edit-pane.vala:855 +#: src/client/accounts/accounts-editor-edit-pane.vala:867 msgid "2 years back" msgstr "2 yıl öncesinden" -#: src/client/accounts/accounts-editor-edit-pane.vala:859 +#: src/client/accounts/accounts-editor-edit-pane.vala:871 msgid "4 years back" msgstr "4 yıl öncesinden" -#: src/client/accounts/accounts-editor-edit-pane.vala:865 +#: src/client/accounts/accounts-editor-edit-pane.vala:877 #, c-format msgid "%d day back" msgid_plural "%d days back" msgstr[0] "%d gün öncesinden" #: src/client/accounts/accounts-editor-list-pane.vala:248 -#: src/client/components/main-window.vala:1623 +#: src/client/application/application-main-window.vala:2021 msgid "Undo" msgstr "Geri Al" #: src/client/accounts/accounts-editor-list-pane.vala:257 -#: src/client/components/main-window.vala:1613 +#: src/client/application/application-main-window.vala:2011 msgid "Redo" msgstr "Yinele" @@ -650,8 +682,8 @@ msgstr "Bağlantı güvenliği" #. Translators: Label used when no auth scheme is used #. by an account's IMAP or SMTP service. #: src/client/accounts/accounts-editor-row.vala:479 -#: src/client/accounts/accounts-editor-servers-pane.vala:752 -#: src/client/accounts/accounts-editor-servers-pane.vala:964 +#: src/client/accounts/accounts-editor-servers-pane.vala:755 +#: src/client/accounts/accounts-editor-servers-pane.vala:970 #: src/engine/api/geary-special-folder-type.vala:58 msgid "None" msgstr "Hiç" @@ -668,7 +700,8 @@ msgstr "TLS" #. credentials (none, use IMAP, custom) when adding a new #. account #. Button label for retrying when a login error has occurred -#: src/client/accounts/accounts-editor-row.vala:534 ui/main-window.ui:347 +#: src/client/accounts/accounts-editor-row.vala:534 +#: ui/application-main-window.ui:346 msgid "Login" msgstr "Giriş" @@ -728,12 +761,12 @@ msgstr "Gönderilmiş postayı sunucuya kaydet" #. Translators: Label used when an account's IMAP or #. SMTP service uses OAuth2. The string replacement is #. the service's login name. -#: src/client/accounts/accounts-editor-servers-pane.vala:950 +#: src/client/accounts/accounts-editor-servers-pane.vala:956 #, c-format msgid "%s using OAuth2" msgstr "%s OAuth2 kullanıyor" -#: src/client/accounts/accounts-editor-servers-pane.vala:960 +#: src/client/accounts/accounts-editor-servers-pane.vala:966 msgid "Use receiving server login" msgstr "Alıcı sunucu girişini kullan" @@ -766,27 +799,198 @@ msgstr "" msgid "_Replace" msgstr "_Değiştir" +#: src/client/application/application-client.vala:33 +msgid "Copyright 2016 Software Freedom Conservancy Inc." +msgstr "Telif Hakkı 2016 Software Freedom Conservancy Inc." + +#: src/client/application/application-client.vala:34 +msgid "Copyright 2016-2019 Geary Development Team." +msgstr "Telif Hakkı 2016-2019 Geary Geliştirme Takımı." + +#: src/client/application/application-client.vala:36 +msgid "Visit the Geary web site" +msgstr "Geary web sitesini ziyaret et" + +#. / Command line option +#: src/client/application/application-client.vala:94 +msgid "Print debug logging" +msgstr "Hata ayıklama günlüğünü yazdır" + +#. / Command line option +#: src/client/application/application-client.vala:97 +msgid "Start with the main window hidden (deprecated)" +msgstr "Ana pencere gizlenmiş olarak başlat (terk edildi)" + +#. / Command line option +#: src/client/application/application-client.vala:100 +msgid "Enable WebKitGTK Inspector in web views" +msgstr "Web görünümünde WebKitGTK İnceleyicisiʼni etkinleştir" + +#. / Command line option +#: src/client/application/application-client.vala:103 +msgid "Log conversation monitoring" +msgstr "Konuşma gözetimini kayda al" + +#. / Command line option +#: src/client/application/application-client.vala:106 +msgid "Log IMAP network deserialization" +msgstr "IMAP ağ serisizleştirmeyi kayda al" + +#. / Command line option. "Normalization" can also be called +#. / "synchronization". +#: src/client/application/application-client.vala:110 +msgid "Log folder normalization" +msgstr "Klasör düzgelemeyi kayda al" + +#. / Command line option +#: src/client/application/application-client.vala:113 +msgid "Log network activity" +msgstr "Ağ etkinliğini kayda al" + +#. / Command line option +#: src/client/application/application-client.vala:116 +msgid "Log periodic activity" +msgstr "Dönemsel etkinliği kayda al" + +#. / Command line option. The IMAP replay queue is how changes +#. / on the server are replicated on the client. It could +#. / also be called the IMAP events queue. +#: src/client/application/application-client.vala:121 +msgid "Log IMAP replay queue" +msgstr "IMAP tekrar sırasını kayda al" + +#. / Command line option. Serialization is how commands and +#. / responses are converted into a stream of bytes for +#. / network transmission +#: src/client/application/application-client.vala:126 +msgid "Log IMAP network serialization" +msgstr "IMAP ağ serileştirmeyi kayda al" + +#. / Command line option +#: src/client/application/application-client.vala:129 +msgid "Log database queries (generates lots of messages)" +msgstr "Veri tabanı sorgularını kayda al (birçok ileti oluşturur)" + +#. / Command line option +#: src/client/application/application-client.vala:132 +msgid "Perform a graceful quit" +msgstr "Hoş bir çıkış gerçekleştir" + +#: src/client/application/application-client.vala:134 +#| msgid "Use %s to open a new composer window" +msgid "Open a new window" +msgstr "Yeni pencere aç" + +#. / Command line option +#: src/client/application/application-client.vala:137 +msgid "Revoke all pinned TLS server certificates" +msgstr "Tüm imlenmiş TLS sunucu sertifikalarını geçersizleştir" + +#. / Command line option +#: src/client/application/application-client.vala:140 +msgid "Display program version" +msgstr "Uygulama sürümünü göster" + +#. / Application runtime information label +#: src/client/application/application-client.vala:264 +msgid "Geary version" +msgstr "Geary sürümü" + +#. / Application runtime information label +#: src/client/application/application-client.vala:266 +msgid "Geary revision" +msgstr "Geary düzeltisi" + +#. / Application runtime information label +#: src/client/application/application-client.vala:268 +msgid "GTK version" +msgstr "GTK sürümü" + +#. / Applciation runtime information label +#: src/client/application/application-client.vala:275 +msgid "GLib version" +msgstr "GLib sürümü" + +#. / Application runtime information label +#: src/client/application/application-client.vala:282 +msgid "WebKitGTK version" +msgstr "WebKitGTK sürümü" + +#. / Application runtime information label +#: src/client/application/application-client.vala:289 +msgid "Desktop environment" +msgstr "Masaüstü ortamı" + +#. Translators: This is the file type displayed for +#. attachments with unknown file types. +#: src/client/application/application-client.vala:291 +#: src/client/components/components-attachment-pane.vala:91 +msgid "Unknown" +msgstr "Bilinmiyor" + +#. / Application runtime information label +#: src/client/application/application-client.vala:321 +msgid "Distribution name" +msgstr "Dağıtım adı" + +#. / Application runtime information label +#: src/client/application/application-client.vala:326 +msgid "Distribution release" +msgstr "Dağıtım sürümü" + +#. / Application runtime information label +#: src/client/application/application-client.vala:334 +msgid "Installation prefix" +msgstr "Kurulum ön eki" + +#: src/client/application/application-client.vala:566 +#, c-format +msgid "About %s" +msgstr "%s hakkında" + +#. Translators: add your name and email address to receive +#. credit in the About dialog For example: Yamada Taro +#. +#: src/client/application/application-client.vala:570 +msgid "translator-credits" +msgstr "" +"Ferhat TUNÇTAN \n" +"Yunus Burak TUNÇTAN \n" +"Muhammet Kara \n" +"Emin Tufan Çetin " + +#. / Warning printed to the console when a deprecated +#. / command line option is used. +#: src/client/application/application-client.vala:913 +msgid "The `--hidden` option is deprecated and will be removed in the future." +msgstr "`--hidden` seçeneği terk edilmiştir ve gelecekte kaldırılacaktır." + +#. / Command line warning, string substitution +#. / is the given argument +#: src/client/application/application-client.vala:946 +#, c-format +msgid "Unrecognised program argument: “%s”" +msgstr "Tanınmayan program argümanı: “%s”" + #. / Notification title. -#: src/client/application/application-controller.vala:454 +#: src/client/application/application-controller.vala:477 #, c-format msgid "A problem occurred sending email for %s" msgstr "%s için e-posta gönderilirken sorun oluştu" #. / Notification body -#: src/client/application/application-controller.vala:458 +#: src/client/application/application-controller.vala:481 msgid "Email will not be sent until re-connected" msgstr "Yeniden bağlanana dek e-posta gönderilmeyecek" #. / Translators: Label for in-app notification -#: src/client/application/application-controller.vala:564 -#| msgid "Conversation Shortcuts" +#: src/client/application/application-controller.vala:587 msgid "Conversation marked" msgid_plural "Conversations marked" msgstr[0] "Konuşma(lar) imlendi" #. / Translators: Label for in-app notification -#: src/client/application/application-controller.vala:570 -#| msgid "No conversations found" +#: src/client/application/application-controller.vala:593 msgid "Conversation un-marked" msgid_plural "Conversations un-marked" msgstr[0] "Konuşmaların imi kaldırıldı" @@ -794,10 +998,9 @@ msgstr[0] "Konuşmaların imi kaldırıldı" #. / Translators: Label for in-app #. / notification. String substitution is the name #. / of the destination folder. -#: src/client/application/application-controller.vala:596 -#: src/client/application/application-controller.vala:680 +#: src/client/application/application-controller.vala:619 +#: src/client/application/application-controller.vala:703 #, c-format -#| msgid "Conversation Shortcuts" msgid "Conversation moved to %s" msgid_plural "Conversations moved to %s" msgstr[0] "Konuşma(lar) şuraya taşındı: %s" @@ -807,33 +1010,29 @@ msgstr[0] "Konuşma(lar) şuraya taşındı: %s" #. / of the source folder. #. / Translators: Label for in-app notification. String #. / substitution is the name of the destination folder. -#: src/client/application/application-controller.vala:604 -#: src/client/application/application-controller.vala:626 +#: src/client/application/application-controller.vala:627 +#: src/client/application/application-controller.vala:649 #, c-format -#| msgid "Conversation Shortcuts" msgid "Conversation restored to %s" msgid_plural "Conversations restored to %s" msgstr[0] "Konuşma(lar) şuraya geri yüklendi: %s" #. / Translators: Label for in-app notification. -#: src/client/application/application-controller.vala:647 -#| msgid "Conversation Shortcuts" +#: src/client/application/application-controller.vala:670 msgid "Conversation archived" msgid_plural "Conversations archived" msgstr[0] "Konuşma(lar) arşivlendi" #. / Translators: Label for in-app notification. String #. / substitution is the name of the destination folder. -#: src/client/application/application-controller.vala:703 +#: src/client/application/application-controller.vala:726 #, c-format -#| msgid "Message not saved" msgid "Message restored to %s" msgid_plural "Messages restored to %s" msgstr[0] "İleti(ler) şuraya geri yüklendi: %s" #. / Translators: Label for in-app notification. -#: src/client/application/application-controller.vala:724 -#| msgid "Message not saved" +#: src/client/application/application-controller.vala:747 msgid "Message archived" msgid_plural "Messages archived" msgstr[0] "İleti(ler) arşivlendi" @@ -841,9 +1040,8 @@ msgstr[0] "İleti(ler) arşivlendi" #. / Translators: Label for in-app #. / notification. String substitution is the name #. / of the destination folder. -#: src/client/application/application-controller.vala:759 +#: src/client/application/application-controller.vala:782 #, c-format -#| msgid "Message not saved" msgid "Message moved to %s" msgid_plural "Messages moved to %s" msgstr[0] "İleti(ler) şuraya taşındı: %s" @@ -851,9 +1049,8 @@ msgstr[0] "İleti(ler) şuraya taşındı: %s" #. / Translators: Label for in-app #. / notification. String substitution is the name #. / of the destination folder. -#: src/client/application/application-controller.vala:787 +#: src/client/application/application-controller.vala:810 #, c-format -#| msgid "No conversations selected" msgid "Conversation labelled as %s" msgid_plural "Conversations labelled as %s" msgstr[0] "Konuşma(lar) %s olarak etiketlendi" @@ -861,25 +1058,18 @@ msgstr[0] "Konuşma(lar) %s olarak etiketlendi" #. / Translators: Label for in-app #. / notification. String substitution is the name #. / of the destination folder. -#: src/client/application/application-controller.vala:795 +#: src/client/application/application-controller.vala:818 #, c-format msgid "Conversation un-labelled as %s" msgid_plural "Conversations un-labelled as %s" msgstr[0] "Konuşma(lar) %s olarak etiketlenmedi" -#: src/client/application/application-controller.vala:1193 -msgid "Labels" -msgstr "Etiketler" - -#. 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. -#: src/client/application/application-controller.vala:1206 +#: src/client/application/application-controller.vala:1238 #, c-format msgid "Unable to open the database for %s" msgstr "%s için veri tabanı açılamadı" -#: src/client/application/application-controller.vala:1207 +#: src/client/application/application-controller.vala:1239 #, c-format msgid "" "There was an error opening the local mail database for this account. This is " @@ -903,20 +1093,20 @@ msgstr "" "Veri tabanının yeniden inşa edilmesi tüm yerel e-postaları ve eklerini yok " "edecektir.Sizin sunucunuzdaki postalar etkilenmeyecektir." -#: src/client/application/application-controller.vala:1209 +#: src/client/application/application-controller.vala:1241 msgid "_Rebuild" msgstr "_Yeniden oluştur" -#: src/client/application/application-controller.vala:1209 +#: src/client/application/application-controller.vala:1241 msgid "E_xit" msgstr "Ç_ık" -#: src/client/application/application-controller.vala:1218 +#: src/client/application/application-controller.vala:1251 #, c-format msgid "Unable to rebuild database for “%s”" msgstr "“%s” için veri tabanı yeniden oluşturulamadı" -#: src/client/application/application-controller.vala:1219 +#: src/client/application/application-controller.vala:1252 #, c-format msgid "" "Error during rebuild:\n" @@ -927,180 +1117,91 @@ msgstr "" "\n" "%s" -#. Translators: The label for an in-app notification. The -#. string substitution is a list of recipients of the email. -#: src/client/application/application-controller.vala:1642 +#. / Translators: The label for an in-app notification. The +#. / string substitution is a list of recipients of the email. +#: src/client/application/application-controller.vala:1575 #, c-format -msgid "Successfully sent mail to %s." -msgstr "Posta, %s adresine başarıyla gönderildi." +msgid "Email sent to %s" +msgstr "Şun(lar)a eposta gönderildi: %s" -#: src/client/application/geary-application.vala:32 -msgid "Copyright 2016 Software Freedom Conservancy Inc." -msgstr "Telif Hakkı 2016 Software Freedom Conservancy Inc." - -#: src/client/application/geary-application.vala:33 -msgid "Copyright 2016-2019 Geary Development Team." -msgstr "Telif Hakkı 2016-2019 Geary Geliştirme Takımı." - -#: src/client/application/geary-application.vala:35 -msgid "Visit the Geary web site" -msgstr "Geary web sitesini ziyaret et" - -#. / Command line option -#: src/client/application/geary-application.vala:110 -msgid "Print debug logging" -msgstr "Hata ayıklama günlüğünü yazdır" - -#. / Command line option -#: src/client/application/geary-application.vala:113 -msgid "Start with the main window hidden (deprecated)" -msgstr "Ana pencere gizlenmiş olarak başlat (terk edildi)" - -#. / Command line option -#: src/client/application/geary-application.vala:116 -msgid "Enable WebKitGTK Inspector in web views" -msgstr "Web görünümünde WebKitGTK İnceleyicisiʼni etkinleştir" - -#. / Command line option -#: src/client/application/geary-application.vala:119 -msgid "Log conversation monitoring" -msgstr "Konuşma gözetimini kayda al" - -#. / Command line option -#: src/client/application/geary-application.vala:122 -msgid "Log IMAP network deserialization" -msgstr "IMAP ağ serisizleştirmeyi kayda al" - -#. / Command line option. "Normalization" can also be called -#. / "synchronization". -#: src/client/application/geary-application.vala:126 -msgid "Log folder normalization" -msgstr "Klasör düzgelemeyi kayda al" - -#. / Command line option -#: src/client/application/geary-application.vala:129 -msgid "Log network activity" -msgstr "Ağ etkinliğini kayda al" - -#. / Command line option -#: src/client/application/geary-application.vala:132 -msgid "Log periodic activity" -msgstr "Dönemsel etkinliği kayda al" - -#. / Command line option. The IMAP replay queue is how changes -#. / on the server are replicated on the client. It could -#. / also be called the IMAP events queue. -#: src/client/application/geary-application.vala:137 -msgid "Log IMAP replay queue" -msgstr "IMAP tekrar sırasını kayda al" - -#. / Command line option. Serialization is how commands and -#. / responses are converted into a stream of bytes for -#. / network transmission -#: src/client/application/geary-application.vala:142 -msgid "Log IMAP network serialization" -msgstr "IMAP ağ serileştirmeyi kayda al" - -#. / Command line option -#: src/client/application/geary-application.vala:145 -msgid "Log database queries (generates lots of messages)" -msgstr "Veri tabanı sorgularını kayda al (birçok ileti oluşturur)" - -#. / Command line option -#: src/client/application/geary-application.vala:148 -msgid "Perform a graceful quit" -msgstr "Hoş bir çıkış gerçekleştir" - -#. / Command line option -#: src/client/application/geary-application.vala:151 -msgid "Revoke all pinned TLS server certificates" -msgstr "Tüm imlenmiş TLS sunucu sertifikalarını geçersizleştir" - -#. / Command line option -#: src/client/application/geary-application.vala:154 -msgid "Display program version" -msgstr "Uygulama sürümünü göster" - -#. / Application runtime information label -#: src/client/application/geary-application.vala:286 -msgid "Geary version" -msgstr "Geary sürümü" - -#. / Application runtime information label -#: src/client/application/geary-application.vala:288 -msgid "Geary revision" -msgstr "Geary düzeltisi" - -#. / Application runtime information label -#: src/client/application/geary-application.vala:290 -msgid "GTK version" -msgstr "GTK sürümü" - -#. / Applciation runtime information label -#: src/client/application/geary-application.vala:297 -msgid "GLib version" -msgstr "GLib sürümü" - -#. / Application runtime information label -#: src/client/application/geary-application.vala:304 -msgid "WebKitGTK version" -msgstr "WebKitGTK sürümü" - -#. / Application runtime information label -#: src/client/application/geary-application.vala:311 -msgid "Desktop environment" -msgstr "Masaüstü ortamı" - -#. Translators: This is the file type displayed for -#. attachments with unknown file types. -#: src/client/application/geary-application.vala:313 -#: src/client/components/components-attachment-pane.vala:91 -msgid "Unknown" -msgstr "Bilinmiyor" - -#. / Application runtime information label -#: src/client/application/geary-application.vala:343 -msgid "Distribution name" -msgstr "Dağıtım adı" - -#. / Application runtime information label -#: src/client/application/geary-application.vala:348 -msgid "Distribution release" -msgstr "Dağıtım sürümü" - -#. / Application runtime information label -#: src/client/application/geary-application.vala:356 -msgid "Installation prefix" -msgstr "Kurulum ön eki" - -#: src/client/application/geary-application.vala:518 +#. / Translators: The label for an in-app notification. The +#. / string substitution is a list of recipients of the email. +#: src/client/application/application-controller.vala:2648 #, c-format -msgid "About %s" -msgstr "%s hakkında" +msgid "Email to %s queued for delivery" +msgstr "Şun(lar)a gidecek eposta iletim sırasında: %s" -#. Translators: add your name and email address to receive -#. credit in the About dialog For example: Yamada Taro -#. -#: src/client/application/geary-application.vala:522 -msgid "translator-credits" -msgstr "" -"Ferhat TUNÇTAN \n" -"Yunus Burak TUNÇTAN \n" -"Muhammet Kara \n" -"Emin Tufan Çetin " - -#. / Warning printed to the console when a deprecated -#. / command line option is used. -#: src/client/application/geary-application.vala:859 -msgid "The `--hidden` option is deprecated and will be removed in the future." -msgstr "`--hidden` seçeneği terk edilmiştir ve gelecekte kaldırılacaktır." - -#. / Command line warning, string substitution -#. / is the given argument -#: src/client/application/geary-application.vala:891 +#. / Translators: The label for an in-app notification. The +#. / string substitution is a list of recipients of the email. +#: src/client/application/application-controller.vala:2713 #, c-format -msgid "Unrecognised program argument: “%s”" -msgstr "Tanınmayan program argümanı: “%s”" +msgid "Email to %s saved" +msgstr "Şun(lar)a gidecek eposta kaydedildi: %s" + +#. / Translators: A label for an in-app notification. +#: src/client/application/application-controller.vala:2728 +#: src/client/application/application-controller.vala:2786 +msgid "Composer could not be restored" +msgstr "Oluşturucu kurtarılamadı" + +#. / Translators: The label for an in-app notification. The +#. / string substitution is a list of recipients of the email. +#: src/client/application/application-controller.vala:2771 +#, c-format +msgid "Email to %s discarded" +msgstr "Şun(lar)a gidecek eposta gözden çıkarıldı: %s" + +#. / Translators: Main window title, first string +#. / substitution being the currently selected folder name, +#. / the second being the selected account name. +#: src/client/application/application-main-window.vala:552 +#, c-format +msgid "%s — %s" +msgstr "%s — %s" + +#: src/client/application/application-main-window.vala:949 +msgid "Labels" +msgstr "Etiketler" + +#. / Translators: Primary text for a confirmation dialog +#: src/client/application/application-main-window.vala:1281 +msgid "Do you want to permanently delete this conversation?" +msgid_plural "Do you want to permanently delete these conversations?" +msgstr[0] "Bu konuşmaları kalıcı olarak silmek istiyor musunuz?" + +#: src/client/application/application-main-window.vala:1286 +#: src/client/application/application-main-window.vala:1301 +msgid "Delete" +msgstr "Sil" + +#. / Translators: Primary text for a confirmation dialog +#: src/client/application/application-main-window.vala:1296 +msgid "Do you want to permanently delete this message?" +msgid_plural "Do you want to permanently delete these messages?" +msgstr[0] "Bu ileti(ler)i kalıcı olarak silmek istiyor musunuz?" + +#: src/client/application/application-main-window.vala:1309 +#, c-format +msgid "Empty all email from your %s folder?" +msgstr "%s klasörünüzdeki tüm e-postaları boşalt?" + +#: src/client/application/application-main-window.vala:1312 +msgid "This removes the email from Geary and your email server." +msgstr "Bu işlem e-postayı Geary’den ve e-posta sunucunuzdan kaldırır." + +#: src/client/application/application-main-window.vala:1313 +msgid "This cannot be undone." +msgstr "Bu geri alınamaz." + +#: src/client/application/application-main-window.vala:1314 +#, c-format +msgid "Empty %s" +msgstr "%s boşalt" + +#: src/client/application/application-main-window.vala:1644 +#, c-format +msgid "%s (%d)" +msgstr "%s (%d)" #. Translators: The first argument will be a #. description of the document type, the second will @@ -1108,7 +1209,7 @@ msgstr "Tanınmayan program argümanı: “%s”" #. Document (100.9MB) #. / In the composer, the filename followed by its filesize, i.e. "notes.txt (1.12KB)" #: src/client/components/components-attachment-pane.vala:107 -#: src/client/composer/composer-widget.vala:1666 +#: src/client/composer/composer-widget.vala:1796 #, c-format msgid "%s (%s)" msgstr "%s (%s)" @@ -1129,138 +1230,136 @@ msgstr "" msgid "Don’t _ask me again" msgstr "Yeniden _sorma" -#: src/client/components/components-inspector.vala:68 +#: src/client/components/components-inspector.vala:72 msgid "Inspector" msgstr "İnceleyici" #. / Translators: Title for Inspector logs pane #. / Translators: Title for problem report dialog logs pane -#: src/client/components/components-inspector.vala:77 -#: src/client/dialogs/dialogs-problem-details-dialog.vala:91 +#: src/client/components/components-inspector.vala:87 +#: src/client/dialogs/dialogs-problem-details-dialog.vala:101 msgid "Logs" msgstr "Günlükler" #. / Translators: Title for Inspector system system information pane #. / Translators: Title for problem report system information #. / pane -#: src/client/components/components-inspector.vala:81 -#: src/client/dialogs/dialogs-problem-details-dialog.vala:94 +#: src/client/components/components-inspector.vala:91 +#: src/client/dialogs/dialogs-problem-details-dialog.vala:104 msgid "System" msgstr "Sistem" #. Button label for saving problem report information -#: src/client/components/components-inspector.vala:198 -#: src/client/components/components-inspector.vala:201 -#: src/client/dialogs/dialogs-problem-details-dialog.vala:210 -#: src/client/dialogs/dialogs-problem-details-dialog.vala:213 +#: src/client/components/components-inspector.vala:208 +#: src/client/components/components-inspector.vala:211 +#: src/client/dialogs/dialogs-problem-details-dialog.vala:220 +#: src/client/dialogs/dialogs-problem-details-dialog.vala:223 #: ui/problem-details-dialog.ui:42 msgid "Save As" msgstr "Farklı Kaydet" -#: src/client/components/components-inspector.vala:202 -#: src/client/dialogs/dialogs-problem-details-dialog.vala:214 +#: src/client/components/components-inspector.vala:212 +#: src/client/dialogs/dialogs-problem-details-dialog.vala:224 #: ui/accounts_editor_servers_pane.ui:17 msgid "Cancel" msgstr "İptal Et" +#. / Translators: Preferences label +#: src/client/components/components-preferences-window.vala:43 +msgid "_Automatically select next message" +msgstr "Sonraki iletiyi _kendiliğinden seç" + +#. / Translators: Preferences label +#: src/client/components/components-preferences-window.vala:53 +msgid "_Display conversation preview" +msgstr "Konuşma ön izlemesini _göster" + +#. / Translators: Preferences label +#: src/client/components/components-preferences-window.vala:63 +msgid "Use _three pane view" +msgstr "_Üç bölmeli görünümü kullan" + +#. / Translators: Preferences label +#: src/client/components/components-preferences-window.vala:73 +#| msgctxt "shortcut window" +#| msgid "Single-key shortcuts" +msgid "Use _single key email shortcuts" +msgstr "_Tek tuşlu eposta kısayolları kullan" + +#: src/client/components/components-preferences-window.vala:75 +msgid "" +"Enable keyboard shortcuts for email actions that do not require pressing " +"" +msgstr "" +"ʼye basmayı gerektirmeyen eposta eylemleri için klavye kısayollarını " +"etkinleştir" + +#. / Translators: Preferences label +#: src/client/components/components-preferences-window.vala:86 +msgid "_Watch for new mail when closed" +msgstr "Kapatıldığında yeni postayı _gözetle" + +#. / Translators: Preferences tooltip +#: src/client/components/components-preferences-window.vala:90 +msgid "Geary will keep running after all windows are closed" +msgstr "Geary, tüm pencereler kapatıldıktan sonra çalışmayı sürdürecek" + #. Translators: Tooltip used when an entry requires a valid #. email address to be entered, but one is not provided. -#: src/client/components/components-validator.vala:378 +#: src/client/components/components-validator.vala:390 msgid "An email address is required" msgstr "E-posta adresi gerekli" #. Translators: Tooltip used when an entry requires a valid #. email address to be entered, but the address is invalid. -#: src/client/components/components-validator.vala:382 +#: src/client/components/components-validator.vala:394 msgid "Not a valid email address" msgstr "Geçersiz e-posta adresi" #. Translators: Tooltip used when an entry requires a valid, #. resolvable server name to be entered, but one is not #. provided. -#: src/client/components/components-validator.vala:428 +#: src/client/components/components-validator.vala:440 msgid "A server name is required" msgstr "Sunucu adı gerekli" #. Translators: Tooltip used when an entry requires a valid #. server name to be entered, but it was unable to be #. looked-up in the DNS. -#: src/client/components/components-validator.vala:433 +#: src/client/components/components-validator.vala:445 msgid "Could not look up server name" msgstr "Sunucu adı yoklanamıyor" -#: src/client/components/main-toolbar.vala:139 +#: src/client/components/main-toolbar.vala:142 msgid "Mark conversation" msgid_plural "Mark conversations" msgstr[0] "Konuşmayı imle" -#: src/client/components/main-toolbar.vala:144 +#: src/client/components/main-toolbar.vala:147 msgid "Add label to conversation" msgid_plural "Add label to conversations" msgstr[0] "Konuşmayı etiketle" -#: src/client/components/main-toolbar.vala:149 +#: src/client/components/main-toolbar.vala:152 msgid "Move conversation" msgid_plural "Move conversations" msgstr[0] "Konuşmayı taşı" -#: src/client/components/main-toolbar.vala:154 +#: src/client/components/main-toolbar.vala:157 msgid "Archive conversation" msgid_plural "Archive conversations" msgstr[0] "Konuşmayı arşivle" -#: src/client/components/main-toolbar.vala:163 +#: src/client/components/main-toolbar.vala:168 msgid "Move conversation to Trash" msgid_plural "Move conversations to Trash" msgstr[0] "Konuşmayı Çöpʼe taşı" -#: src/client/components/main-toolbar.vala:171 +#: src/client/components/main-toolbar.vala:178 msgid "Delete conversation" msgid_plural "Delete conversations" msgstr[0] "Konuşmayı sil" -#. / Translators: Primary text for a confirmation dialog -#: src/client/components/main-window.vala:985 -#| msgid "Do you want to permanently delete this message?" -#| msgid_plural "Do you want to permanently delete these messages?" -msgid "Do you want to permanently delete this conversation?" -msgid_plural "Do you want to permanently delete these conversations?" -msgstr[0] "Bu konuşmaları kalıcı olarak silmek istiyor musunuz?" - -#: src/client/components/main-window.vala:990 -#: src/client/components/main-window.vala:1005 -msgid "Delete" -msgstr "Sil" - -#. / Translators: Primary text for a confirmation dialog -#: src/client/components/main-window.vala:1000 -msgid "Do you want to permanently delete this message?" -msgid_plural "Do you want to permanently delete these messages?" -msgstr[0] "Bu ileti(ler)i kalıcı olarak silmek istiyor musunuz?" - -#: src/client/components/main-window.vala:1013 -#, c-format -msgid "Empty all email from your %s folder?" -msgstr "%s klasörünüzdeki tüm e-postaları boşalt?" - -#: src/client/components/main-window.vala:1016 -msgid "This removes the email from Geary and your email server." -msgstr "Bu işlem e-postayı Geary’den ve e-posta sunucunuzdan kaldırır." - -#: src/client/components/main-window.vala:1017 -msgid "This cannot be undone." -msgstr "Bu geri alınamaz." - -#: src/client/components/main-window.vala:1018 -#, c-format -msgid "Empty %s" -msgstr "%s boşalt" - -#: src/client/components/main-window.vala:1361 -#, c-format -msgid "%s (%d)" -msgstr "%s (%d)" - #. Translators: Info bar title for a generic account #. problem. #: src/client/components/main-window-info-bar.vala:44 @@ -1331,23 +1430,23 @@ msgid "_Retry" msgstr "_Yeniden dene" #: src/client/components/search-bar.vala:8 -#: src/client/folder-list/folder-list-search-branch.vala:38 +#: src/client/folder-list/folder-list-search-branch.vala:53 #: src/engine/api/geary-special-folder-type.vala:51 msgid "Search" msgstr "Ara" #. Search entry. -#: src/client/components/search-bar.vala:23 +#: src/client/components/search-bar.vala:24 msgid "Search all mail in account for keywords (Ctrl+S)" msgstr "Anahtar sözcükler için hesaptaki tüm postaları ara (Ctrl+S)" -#: src/client/components/search-bar.vala:83 +#: src/client/components/search-bar.vala:88 #, c-format msgid "Indexing %s account" msgstr "%s hesabı dizinleniyor" -#: src/client/components/search-bar.vala:110 -#: src/client/folder-list/folder-list-search-branch.vala:39 +#: src/client/components/search-bar.vala:119 +#: src/client/folder-list/folder-list-search-branch.vala:54 #, c-format msgid "Search %s account" msgstr "%s hesabını ara" @@ -1438,19 +1537,24 @@ msgstr "Geçersiz bağlantı URL’si" msgid "Invalid email address" msgstr "Geçersiz e-posta adresi" -#: src/client/composer/composer-widget.vala:159 +#. / Translators: Title for an empty composer window +#: src/client/composer/composer-widget.vala:28 +msgid "New Message" +msgstr "Yeni İleti" + +#: src/client/composer/composer-widget.vala:211 msgid "Saved" msgstr "Kaydedildi" -#: src/client/composer/composer-widget.vala:160 +#: src/client/composer/composer-widget.vala:212 msgid "Saving" msgstr "Kaydediliyor" -#: src/client/composer/composer-widget.vala:161 +#: src/client/composer/composer-widget.vala:213 msgid "Error saving" msgstr "Kaydedilirken hata" -#: src/client/composer/composer-widget.vala:162 +#: src/client/composer/composer-widget.vala:214 msgid "Press Backspace to delete quote" msgstr "Alıntıyı silmek için Geri tuşuna basın" @@ -1459,7 +1563,7 @@ msgstr "Alıntıyı silmek için Geri tuşuna basın" #. checking, include all variants of each word. No spaces are #. allowed. The words will be converted to lower case based on #. locale and English versions included automatically. -#: src/client/composer/composer-widget.vala:178 +#: src/client/composer/composer-widget.vala:230 msgid "" "attach|attaching|attaches|attachment|attachments|attached|enclose|enclosed|" "enclosing|encloses|enclosure|enclosures" @@ -1470,89 +1574,90 @@ msgstr "" #. Translators: This dialog text is displayed to the #. user when closing a composer where the options are #. Keep, Discard or Cancel. -#: src/client/composer/composer-widget.vala:1182 +#: src/client/composer/composer-widget.vala:798 msgid "Do you want to keep or discard this draft message?" msgstr "Bu iletiyi saklamak mı yoksa gözden çıkarmak mı istersiniz?" #. Translators: This dialog text is displayed to the #. user when closing a composer where the options are #. only Discard or Cancel. -#: src/client/composer/composer-widget.vala:1212 +#: src/client/composer/composer-widget.vala:824 msgid "Do you want to discard this draft message?" msgstr "Bu taslak iletiyi gözden çıkarmak istiyor musunuz?" -#: src/client/composer/composer-widget.vala:1331 +#: src/client/composer/composer-widget.vala:1465 msgid "Send message with an empty subject and body?" msgstr "İleti konusu ve gövdesi olmadan gönderilsin mi?" -#: src/client/composer/composer-widget.vala:1333 +#: src/client/composer/composer-widget.vala:1467 msgid "Send message with an empty subject?" msgstr "İleti konusu olmadan gönderilsin mi?" -#: src/client/composer/composer-widget.vala:1335 +#: src/client/composer/composer-widget.vala:1469 msgid "Send message with an empty body?" msgstr "İleti, ileti gövdesi olmadan gönderilsin mi?" -#: src/client/composer/composer-widget.vala:1344 +#: src/client/composer/composer-widget.vala:1478 msgid "Send message without an attachment?" msgstr "İleti eki olmadan gönderilsin mi?" -#: src/client/composer/composer-widget.vala:1658 +#: src/client/composer/composer-widget.vala:1788 #, c-format msgid "“%s” already attached for delivery." msgstr "“%s” gönderim için zaten eklendi." -#: src/client/composer/composer-widget.vala:1703 -#, c-format -msgid "“%s” could not be found." -msgstr "“%s” bulunamadı." - -#: src/client/composer/composer-widget.vala:1709 -#, c-format -msgid "“%s” is a folder." -msgstr "“%s” bir klasör." - -#: src/client/composer/composer-widget.vala:1715 +#: src/client/composer/composer-widget.vala:1818 +#: src/client/composer/composer-widget.vala:1868 #, c-format msgid "“%s” is an empty file." msgstr "“%s” boş bir dosya." -#: src/client/composer/composer-widget.vala:1728 +#: src/client/composer/composer-widget.vala:1856 +#, c-format +msgid "“%s” could not be found." +msgstr "“%s” bulunamadı." + +#: src/client/composer/composer-widget.vala:1862 +#, c-format +msgid "“%s” is a folder." +msgstr "“%s” bir klasör." + +#: src/client/composer/composer-widget.vala:1881 #, c-format msgid "“%s” could not be opened for reading." msgstr "“%s” okuma için açılamadı." -#: src/client/composer/composer-widget.vala:1736 +#: src/client/composer/composer-widget.vala:1889 msgid "Cannot add attachment" msgstr "Eklenti eklenemiyor" #. Translators: Human-readable version of the RFC 822 To header -#: src/client/composer/composer-widget.vala:1793 +#: src/client/composer/composer-widget.vala:1946 #: src/client/conversation-viewer/conversation-email.vala:559 -#: src/client/util/util-email.vala:216 ui/conversation-message.ui:312 +#: src/client/util/util-email.vala:235 ui/conversation-message.ui:312 msgid "To:" msgstr "Kime:" #. Translators: Human-readable version of the RFC 822 CC header -#: src/client/composer/composer-widget.vala:1799 +#: src/client/composer/composer-widget.vala:1952 #: src/client/conversation-viewer/conversation-email.vala:564 -#: src/client/util/util-email.vala:221 ui/conversation-message.ui:357 +#: src/client/util/util-email.vala:240 ui/conversation-message.ui:357 msgid "Cc:" msgstr "Cc:" #. Translators: Human-readable version of the RFC 822 BCC header -#: src/client/composer/composer-widget.vala:1805 +#: src/client/composer/composer-widget.vala:1958 #: src/client/conversation-viewer/conversation-email.vala:569 #: ui/conversation-message.ui:402 msgid "Bcc:" msgstr "Bcc:" #. Translators: Human-readable version of the RFC 822 Reply-To header -#: src/client/composer/composer-widget.vala:1811 +#: src/client/composer/composer-widget.vala:1964 msgid "Reply-To: " msgstr "Yanıtla: " -#: src/client/composer/composer-widget.vala:1951 +#: src/client/composer/composer-widget.vala:2144 msgid "Select Color" msgstr "Renk Seç" @@ -1561,27 +1666,23 @@ msgstr "Renk Seç" #. printf argument will be the alternate email address, #. and the second will be the account's primary email #. address. -#: src/client/composer/composer-widget.vala:2143 +#: src/client/composer/composer-widget.vala:2336 #, c-format msgid "%1$s via %2$s" msgstr "%2$s aracılığıyla %1$s" #. Composer label (with mnemonic underscore) for the account selector #. when choosing what address to send a message from. -#: src/client/composer/composer-widget.vala:2198 +#: src/client/composer/composer-widget.vala:2392 msgid "_From:" msgstr "_Gönderen:" #. Translators: This is the name of the file chooser filter #. when inserting an image in the composer. -#: src/client/composer/composer-widget.vala:2480 +#: src/client/composer/composer-widget.vala:2695 msgid "Images" msgstr "Resimler" -#: src/client/composer/composer-window.vala:14 -msgid "New Message" -msgstr "Yeni İleti" - #: src/client/composer/spell-check-popover.vala:108 msgid "Remove this language from the preferred list" msgstr "Bu dili tercih edilenler listesinden kaldır" @@ -1595,49 +1696,49 @@ msgid "Search for more languages" msgstr "Daha çok dil için ara" #. / Translators: Context menu item -#: src/client/conversation-list/conversation-list-view.vala:312 +#: src/client/conversation-list/conversation-list-view.vala:335 msgid "Move conversation to _Trash" msgid_plural "Move conversations to _Trash" msgstr[0] "Konuşmayı _Çöpʼe taşı" #. / Translators: Context menu item -#: src/client/conversation-list/conversation-list-view.vala:322 +#: src/client/conversation-list/conversation-list-view.vala:347 msgid "_Delete conversation" msgid_plural "_Delete conversations" msgstr[0] "Konuşmayı _sil" -#: src/client/conversation-list/conversation-list-view.vala:331 +#: src/client/conversation-list/conversation-list-view.vala:360 #: ui/main-toolbar-menus.ui:5 msgid "Mark as _Read" msgstr "_Okundu olarak imle" -#: src/client/conversation-list/conversation-list-view.vala:334 +#: src/client/conversation-list/conversation-list-view.vala:368 #: ui/main-toolbar-menus.ui:9 msgid "Mark as _Unread" msgstr "Ok_unmamış olarak imle" -#: src/client/conversation-list/conversation-list-view.vala:337 +#: src/client/conversation-list/conversation-list-view.vala:376 #: ui/main-toolbar-menus.ui:17 msgid "U_nstar" msgstr "Y_ıldızı kaldır" -#: src/client/conversation-list/conversation-list-view.vala:339 +#: src/client/conversation-list/conversation-list-view.vala:383 #: ui/main-toolbar-menus.ui:13 msgid "_Star" msgstr "_Yıldızla" #. Translators: Menu item to reply to a specific message. -#: src/client/conversation-list/conversation-list-view.vala:342 +#: src/client/conversation-list/conversation-list-view.vala:392 #: ui/conversation-email-menus.ui:9 msgid "_Reply" msgstr "_Yanıtla" -#: src/client/conversation-list/conversation-list-view.vala:343 +#: src/client/conversation-list/conversation-list-view.vala:398 msgid "R_eply All" msgstr "Tümüne _Yanıtla" #. Translators: Menu item to forward a specific message. -#: src/client/conversation-list/conversation-list-view.vala:344 +#: src/client/conversation-list/conversation-list-view.vala:404 #: ui/conversation-email-menus.ui:21 msgid "_Forward" msgstr "_Yönlendir" @@ -1648,19 +1749,19 @@ msgstr "Ben" #. Translators: Human-readable version of the RFC 822 From header #: src/client/conversation-viewer/conversation-email.vala:554 -#: src/client/util/util-email.vala:207 +#: src/client/util/util-email.vala:226 msgid "From:" msgstr "Gönderen:" #. Translators: Human-readable version of the RFC 822 Date header #: src/client/conversation-viewer/conversation-email.vala:574 -#: src/client/util/util-email.vala:212 +#: src/client/util/util-email.vala:231 msgid "Date:" msgstr "Tarih:" #. Translators: Human-readable version of the RFC 822 Subject header #: src/client/conversation-viewer/conversation-email.vala:584 -#: src/client/util/util-email.vala:210 +#: src/client/util/util-email.vala:229 msgid "Subject:" msgstr "Konu:" @@ -1672,44 +1773,44 @@ msgstr "Bu e-posta adresi taklit edilmiş olabilir" #. in load_contacts. #. Translators: This is displayed in place of the from address #. when the message has no from address. -#: src/client/conversation-viewer/conversation-message.vala:449 +#: src/client/conversation-viewer/conversation-message.vala:469 msgid "No sender" msgstr "Gönderen yok" #. Translators: This separates multiple 'from' #. addresses in the compact header for a message. -#: src/client/conversation-viewer/conversation-message.vala:841 +#: src/client/conversation-viewer/conversation-message.vala:963 msgid ", " msgstr ", " #. Translators: This string is used as the HTML IMG ALT #. attribute value when displaying an inline image in an email #. that did not specify a file name. E.g.