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/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. Image 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, @@ -474,11 +474,8 @@ public abstract class Geary.Account : BaseObject, Loggable { 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 6f6da006..b5bee4fc 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, @@ -293,16 +293,13 @@ public abstract class Geary.ClientService : BaseObject, Loggable { } /** {@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. */ - 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..f2762f09 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; } } @@ -696,12 +696,8 @@ public abstract class Geary.Folder : BaseObject, Loggable { 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 bdbe8744..07346156 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 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.loggables = new Loggable[fields.length]; - int loggable_count = 0; + this.states = new State[fields.length]; + int state_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.states[state_count++] = + ((Source) field.value).to_logging_state(); break; case "GEARY_FLAGS": @@ -204,43 +205,26 @@ public class Record { } } - this.loggables.length = loggable_count; - } - - /** Returns the record's loggables that aren't well-known. */ - public Loggable[] get_other_loggables() { - fill_well_known_loggables(); - - Loggable[] copy = new Loggable[this.loggables.length]; - int count = 0; - foreach (Loggable loggable in this.loggables) { - if (loggable != this.account && - loggable != this.service && - loggable != this.folder) { - copy[count++] = loggable; - } - } - copy.length = count; - return copy; + this.states.length = state_count; } /** - * 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 (unowned State state in this.states) { + GLib.Type type = state.source.get_type(); if (type.is_a(typeof(Account))) { - this.account = (Account) loggable; + this.account = (Account) state.source; } else if (type.is_a(typeof(ClientService))) { - this.service = (ClientService) loggable; + this.service = (ClientService) state.source; } else if (type.is_a(typeof(Folder))) { - this.folder = (Folder) loggable; + this.folder = (Folder) state.source; } } this.filled = true; @@ -249,7 +233,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,41 +255,18 @@ public class Record { domain ); - if (flags != NONE && flags != ALL) { - str.append_printf("[%s]: ", flags.to_string()); + if (flags != NONE) { + str.append_printf("[%s]:", flags.to_string()); } else { - str.append(": "); + 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(": "); + // 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("]"); } - if (this.folder != null) { - str.append(this.folder.path.to_string()); - str.append(": "); - } - - foreach (Loggable loggable in get_other_loggables()) { - str.append(loggable.to_string()); - str.append_c(' '); - } - - str.append(message); - // XXX Don't append source details for the moment because of // https://gitlab.gnome.org/GNOME/vala/issues/815 @@ -322,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; } diff --git a/src/engine/imap-engine/imap-engine-account-operation.vala b/src/engine/imap-engine/imap-engine-account-operation.vala index 99a7fdb3..3dc32d37 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; } @@ -112,8 +112,8 @@ public abstract class Geary.ImapEngine.AccountOperation : BaseObject, Loggable { } /** {@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-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 b790d647..c6c0b54b 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); @@ -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); @@ -297,11 +298,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 +351,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 +369,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-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/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 ea891425..26d4aa6d 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,11 @@ internal class Geary.Imap.ClientService : Geary.ClientService { if (++attempts > 12) break; } + + if (this.all_sessions.size > 0) { + debug("Cancelling remaining client sessions..."); + this.close_cancellable.cancel(); + } } /** @@ -216,7 +223,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 +234,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 +275,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 +359,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 +373,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 +390,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 +406,7 @@ internal class Geary.Imap.ClientService : Geary.ClientService { break; default: - yield force_disconnect(target); + yield disconnect_session(target); break; } @@ -408,7 +427,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); @@ -427,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 { @@ -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,32 @@ 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("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 + // exiting. + try { + yield session.logout_async(this.close_cancellable); + yield remove_session_async(session); + } catch (GLib.Error err) { + debug("Error logging out of session: %s", err.message); + yield force_disconnect_session(session); + } + + } + + private async void force_disconnect_session(ClientSession session) { + debug("Dropping session: %s", session.to_string()); try { yield remove_session_async(session); @@ -510,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 d26aef7a..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. @@ -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, @@ -188,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 { @@ -246,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 @@ -445,29 +452,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_dropped_response), - 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), @@ -495,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() { @@ -595,8 +589,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; @@ -716,14 +709,16 @@ 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 { 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; @@ -743,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); @@ -788,12 +784,10 @@ 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 LOGGED OUT + // to move into NOAUTH or LOGOUT return state; } @@ -802,10 +796,8 @@ 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()); - return state; + debug("Disconnected from %s", this.imap_endpoint.to_string()); + return State.CLOSED; } private uint on_connecting_recv_status(uint state, uint event, void *user, Object? object) { @@ -815,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) { @@ -825,14 +819,15 @@ 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()); }); - 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) { @@ -840,16 +835,19 @@ 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( + "Session greeting not seen in %u seconds", + GREETING_TIMEOUT_SEC + ); - connect_err = new IOError.TIMED_OUT("Session greeting not seen in %u seconds", - GREETING_TIMEOUT_SEC); - - return State.LOGGED_OUT; + return State.LOGOUT; } /** @@ -963,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( @@ -1015,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"); } @@ -1033,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) && @@ -1067,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); @@ -1120,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; @@ -1232,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 @@ -1242,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); } } @@ -1352,7 +1337,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("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); @@ -1361,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; } @@ -1459,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; } } @@ -1515,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; } @@ -1539,8 +1527,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) { @@ -1550,10 +1547,35 @@ 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.LOGOUT; + } - 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("Received bye from server on logout: %s", + status_response.to_string()); + break; + + default: + warning("Received error from server on logout: %s", + status_response.to_string()); + break; + } + + return state; } private uint on_logging_out_recv_completion(uint state, uint event, void *user, Object? object) { @@ -1562,9 +1584,7 @@ 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; + return State.CLOSED; } // @@ -1596,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; @@ -1618,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; @@ -1631,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); @@ -1647,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); @@ -1660,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); } @@ -1718,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; } @@ -1735,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; } @@ -1799,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 + ); } } @@ -1828,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; @@ -1875,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; } @@ -1893,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); } } @@ -1913,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); } @@ -1925,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())); - } - } } 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 50% rename from src/engine/util/util-loggable.vala rename to src/engine/util/util-logging.vala index d559f41b..4161aad7 100644 --- a/src/engine/util/util-loggable.vala +++ b/src/engine/util/util-logging.vala @@ -5,19 +5,36 @@ * (version 2.1 or later). See the COPYING file in this distribution. */ + /** * 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 { + /** + * 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: @@ -87,8 +104,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,23 +118,39 @@ 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. + * 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. @@ -125,7 +158,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 +168,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 +178,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 +189,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,22 +199,76 @@ 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() ); } + /** + * 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, va_list args) { - Context context = Context(Logging.DOMAIN, flags, levels, fmt, args); - Loggable? decorated = this; - while (decorated != null) { - context.append_loggable(decorated); - decorated = decorated.loggable_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()); + } + } + +} + +/** + * 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; } } diff --git a/test/engine/imap-engine/account-processor-test.vala b/test/engine/imap-engine/account-processor-test.vala index 35577c26..37456ff3 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(); + + 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;