Merge branch 'mainline' into remove-old-msgs-beyond-storage-pref

This commit is contained in:
Chris Heywood 2020-05-17 12:03:30 +10:00
commit 71262f0e79
62 changed files with 3163 additions and 1355 deletions

View file

@ -46,7 +46,7 @@
</key>
<key name="formatting-toolbar-visible" type="b">
<default>true</default>
<default>false</default>
<summary>Show/hide formatting toolbar</summary>
<description>True if the formatting toolbar in the composer is shown.</description>
</key>

View file

@ -11,3 +11,4 @@ pl
pt_BR
sv
tr
uk

1540
help/uk/uk.po Normal file

File diff suppressed because it is too large Load diff

View file

@ -50,7 +50,7 @@ valac = meson.get_compiler('vala')
# Required libraries and other dependencies
#
target_vala = '0.42'
target_vala = '0.46.2'
target_glib = '2.60.4'
target_gtk = '3.24.7'
target_webkit = '2.26'

View file

@ -91,7 +91,7 @@
"sources": [
{
"type": "archive",
"url": "https://github.com/libical/libical/releases/download/v3.0.8/libical-3.0.7.tar.gz",
"url": "https://github.com/libical/libical/releases/download/v3.0.8/libical-3.0.8.tar.gz",
"sha256": "09fecacaf75ba5a242159e3a9758a5446b5ce4d0ab684f98a7040864e1d1286f"
}
],

155
po/es.po
View file

@ -18,8 +18,8 @@ msgid ""
msgstr ""
"Project-Id-Version: geary-0.4.1\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
"POT-Creation-Date: 2020-04-25 12:36+0000\n"
"PO-Revision-Date: 2020-04-30 11:48+0200\n"
"POT-Creation-Date: 2020-05-08 12:54+0000\n"
"PO-Revision-Date: 2020-05-14 16:25+0200\n"
"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
"Language-Team: Spanish - Spain <gnome-es-list@gnome.org>\n"
"Language: es_ES\n"
@ -876,7 +876,6 @@ msgstr "Registrar la normalización de carpetas"
#. / Command line option
#: src/client/application/application-client.vala:115
#| msgid "Log network activity"
msgid "Log IMAP network activity"
msgstr "Registrar actividad IMAP de la red"
@ -889,7 +888,6 @@ msgstr "Registrar la cola de eventos IMAP"
#. / Command line option
#: src/client/application/application-client.vala:123
#| msgid "Log network activity"
msgid "Log SMTP network activity"
msgstr "Registrar actividad SMTP de la red"
@ -1240,7 +1238,11 @@ msgstr "Compruebe los detalles de seguridad de la conexión"
msgid "%s — %s"
msgstr "%s — %s"
#. Translators: The name of the folder group containing
#. folders created by people (as opposed to special-use
#. folders)
#: src/client/application/application-main-window.vala:972
#: src/client/folder-list/folder-list-account-branch.vala:43
msgid "Labels"
msgstr "Etiquetas"
@ -1293,7 +1295,7 @@ msgstr "%s (%d)"
#. 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:1918
#: src/client/composer/composer-widget.vala:1920
#, c-format
msgid "%s (%s)"
msgstr "%s (%s)"
@ -1392,7 +1394,7 @@ msgid "Preferences"
msgstr "Preferencias"
#. / Translators: Preferences page title
#: src/client/components/components-preferences-window.vala:179
#: src/client/components/components-preferences-window.vala:180
msgid "Plugins"
msgstr "Complementos"
@ -1697,63 +1699,63 @@ msgstr "¿Quiere enviar el mensaje sin cuerpo?"
msgid "Send message without an attachment?"
msgstr "¿Quiere enviar el mensaje sin el archivo adjunto?"
#: src/client/composer/composer-widget.vala:1904
#: src/client/composer/composer-widget.vala:1906
#, c-format
msgid "“%s” already attached for delivery."
msgstr "Ya se ha adjuntado «%s» para enviarlo."
#: src/client/composer/composer-widget.vala:1940
#: src/client/composer/composer-widget.vala:1990
#: src/client/composer/composer-widget.vala:1942
#: src/client/composer/composer-widget.vala:1992
#, c-format
msgid "“%s” is an empty file."
msgstr "«%s» es un archivo vacío."
#: src/client/composer/composer-widget.vala:1978
#: src/client/composer/composer-widget.vala:1980
#, c-format
msgid "“%s” could not be found."
msgstr "No se pudo encontrar «%s»."
#: src/client/composer/composer-widget.vala:1984
#: src/client/composer/composer-widget.vala:1986
#, c-format
msgid "“%s” is a folder."
msgstr "«%s» es una carpeta."
#: src/client/composer/composer-widget.vala:2003
#: src/client/composer/composer-widget.vala:2005
#, c-format
msgid "“%s” could not be opened for reading."
msgstr "No se pudo abrir «%s» para lectura."
#: src/client/composer/composer-widget.vala:2011
#: src/client/composer/composer-widget.vala:2013
msgid "Cannot add attachment"
msgstr "No se puede adjuntar el archivo"
#. Translators: Human-readable version of the RFC 822 To header
#: src/client/composer/composer-widget.vala:2071
#: src/client/composer/composer-widget.vala:2073
#: src/client/conversation-viewer/conversation-email.vala:542
#: src/client/util/util-email.vala:236 ui/conversation-message.ui:312
msgid "To:"
msgstr "Para:"
#. Translators: Human-readable version of the RFC 822 CC header
#: src/client/composer/composer-widget.vala:2077
#: src/client/composer/composer-widget.vala:2079
#: src/client/conversation-viewer/conversation-email.vala:547
#: src/client/util/util-email.vala:241 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:2083
#: src/client/composer/composer-widget.vala:2085
#: src/client/conversation-viewer/conversation-email.vala:552
#: ui/conversation-message.ui:402
msgid "Bcc:"
msgstr "Cco:"
#. Translators: Human-readable version of the RFC 822 Reply-To header
#: src/client/composer/composer-widget.vala:2089
#: src/client/composer/composer-widget.vala:2091
msgid "Reply-To: "
msgstr "Responder a:"
#: src/client/composer/composer-widget.vala:2341
#: src/client/composer/composer-widget.vala:2343
msgid "Select Color"
msgstr "Seleccionar color"
@ -1762,14 +1764,14 @@ msgstr "Seleccionar color"
#. 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:2530
#: src/client/composer/composer-widget.vala:2532
#, c-format
msgid "%1$s via %2$s"
msgstr "%1$s mediante %2$s"
#. Translators: This is the name of the file chooser filter
#. when inserting an image in the composer.
#: src/client/composer/composer-widget.vala:2887
#: src/client/composer/composer-widget.vala:2889
msgid "Images"
msgstr "Imágenes"
@ -2168,7 +2170,6 @@ msgstr[0] "Mensaje nuevo"
msgstr[1] "Mensajes nuevos"
#: src/client/plugin/email-templates/email-templates.plugin.desktop.in:4
#| msgid "Email address"
msgid "Email Templates"
msgstr "Plantillas de de corre-e"
@ -2181,7 +2182,6 @@ msgstr "Crear plantillas reutilizables para enviar correo"
#. the front for the default. English names do not need to be
#. included.
#: src/client/plugin/email-templates/email-templates.vala:29
#| msgid "Sent | Sent Mail | Sent Email | Sent E-Mail"
msgid "Templates | Template Mail | Template Email | Template E-Mail"
msgstr ""
"Plantillas | Plantilla de correo | Plantillas correo-e | Plantillas mensajes"
@ -2195,21 +2195,17 @@ msgstr "Plantillas"
#. Translators: Info bar button label for creating a
#. new email template
#: src/client/plugin/email-templates/email-templates.vala:276
#| msgid "Now"
msgid "New"
msgstr "Nueva"
#. Translators: Infobar status label for an email template
#: src/client/plugin/email-templates/email-templates.vala:287
#| msgid "Message not saved"
msgid "Message template"
msgstr "Plantilla de mensaje"
#. Translators: Info bar button label for sending an
#. email template
#: src/client/plugin/email-templates/email-templates.vala:291
#| msgctxt "shortcut window"
#| msgid "Send"
msgid "Send"
msgstr "Enviar"
@ -2723,18 +2719,18 @@ msgstr "Elementos eliminados"
msgid "Archive | Archives"
msgstr "Archivo | Archivos"
#: src/engine/rfc822/rfc822-message.vala:461
#: src/engine/rfc822/rfc822-message.vala:531
#, c-format
msgid "Could not determine mime type for “%s”."
msgstr "No se pudo determinar el tipo MIME de «%s»"
#: src/engine/rfc822/rfc822-message.vala:472
#: src/engine/rfc822/rfc822-message.vala:542
#, c-format
msgid "Could not determine content type for mime type “%s” on “%s”."
msgstr ""
"No se pudo determinar el tipo de contenido para el tipo MIME «%s» en «%s»."
#: src/engine/rfc822/rfc822-message.vala:1003
#: src/engine/rfc822/rfc822-message.vala:1001
msgid "(no subject)"
msgstr "(sin asunto)"
@ -2743,14 +2739,15 @@ msgid "Add an account"
msgstr "Añadir una cuenta"
#: ui/accounts_editor_add_pane.ui:53
msgid "Create"
msgstr "Crear"
#| msgid "Create"
msgid "_Create"
msgstr "_Crear"
#: ui/accounts_editor_add_pane.ui:130 ui/accounts_editor_servers_pane.ui:125
#: ui/accounts_editor_add_pane.ui:131 ui/accounts_editor_servers_pane.ui:125
msgid "Receiving"
msgstr "Recibiendo"
#: ui/accounts_editor_add_pane.ui:178 ui/accounts_editor_servers_pane.ui:165
#: ui/accounts_editor_add_pane.ui:179 ui/accounts_editor_servers_pane.ui:165
msgid "Sending"
msgstr "Enviando"
@ -3142,7 +3139,6 @@ msgstr "Copiar al portapapeles"
#. Tooltip for inspector button
#: ui/components-inspector.ui:121
#| msgid "Search for matching log entries"
msgid "Clears all log entries"
msgstr "Limpiar todas las entradas del registro"
@ -3346,7 +3342,7 @@ msgstr "etiqueta"
msgid "Conversation Shortcuts"
msgstr "Atajos de conversaciones"
#: ui/gtk/help-overlay.ui:13 ui/gtk/help-overlay.ui:355
#: ui/gtk/help-overlay.ui:13 ui/gtk/help-overlay.ui:377
msgctxt "shortcut window"
msgid "Actions"
msgstr "Acciones"
@ -3361,37 +3357,37 @@ msgctxt "shortcut window"
msgid "Reply to sender"
msgstr "Responder al remitente "
#: ui/gtk/help-overlay.ui:31 ui/gtk/help-overlay.ui:269
#: ui/gtk/help-overlay.ui:31 ui/gtk/help-overlay.ui:281
msgctxt "shortcut window"
msgid "Reply to all"
msgstr "Responder a todos"
#: ui/gtk/help-overlay.ui:38 ui/gtk/help-overlay.ui:276
#: ui/gtk/help-overlay.ui:38 ui/gtk/help-overlay.ui:288
msgctxt "shortcut window"
msgid "Forward"
msgstr "Reenviar"
#: ui/gtk/help-overlay.ui:45 ui/gtk/help-overlay.ui:283
#: ui/gtk/help-overlay.ui:45 ui/gtk/help-overlay.ui:295
msgctxt "shortcut window"
msgid "Un-mark/mark read"
msgstr "Desmarcar/marcar como leído"
#: ui/gtk/help-overlay.ui:52 ui/gtk/help-overlay.ui:290
#: ui/gtk/help-overlay.ui:52 ui/gtk/help-overlay.ui:302
msgctxt "shortcut window"
msgid "Mark/un-mark starred"
msgstr "Marcar/desmarcar como favorito"
#: ui/gtk/help-overlay.ui:59 ui/gtk/help-overlay.ui:297
#: ui/gtk/help-overlay.ui:59 ui/gtk/help-overlay.ui:309
msgctxt "shortcut window"
msgid "Archive conversations"
msgstr "Archivar conversaciones"
#: ui/gtk/help-overlay.ui:66 ui/gtk/help-overlay.ui:304
#: ui/gtk/help-overlay.ui:66 ui/gtk/help-overlay.ui:326
msgctxt "shortcut window"
msgid "Move conversations"
msgstr "Mover conversaciones"
#: ui/gtk/help-overlay.ui:73 ui/gtk/help-overlay.ui:311
#: ui/gtk/help-overlay.ui:73 ui/gtk/help-overlay.ui:333
msgctxt "shortcut window"
msgid "Label conversations"
msgstr "Etiquetar conversaciones"
@ -3401,12 +3397,12 @@ msgctxt "shortcut window"
msgid "Trash conversations"
msgstr "Eliminar conversaciones"
#: ui/gtk/help-overlay.ui:87 ui/gtk/help-overlay.ui:318
#: ui/gtk/help-overlay.ui:87 ui/gtk/help-overlay.ui:340
msgctxt "shortcut window"
msgid "Junk conversations"
msgstr "Marcar conversaciones"
#: ui/gtk/help-overlay.ui:95 ui/gtk/help-overlay.ui:325
#: ui/gtk/help-overlay.ui:95 ui/gtk/help-overlay.ui:347
msgctxt "shortcut window"
msgid "Delete conversations"
msgstr "Eliminar conversaciones"
@ -3421,7 +3417,7 @@ msgctxt "shortcut window"
msgid "Search for conversations"
msgstr "Buscar conversaciones"
#: ui/gtk/help-overlay.ui:115
#: ui/gtk/help-overlay.ui:115 ui/gtk/help-overlay.ui:354
msgctxt "shortcut window"
msgid "Find in current conversation"
msgstr "Buscar en la conversación actual"
@ -3461,7 +3457,7 @@ msgctxt "shortcut window"
msgid "Reset zoom"
msgstr "Restablecer ampliación"
#: ui/gtk/help-overlay.ui:188 ui/gtk/help-overlay.ui:375
#: ui/gtk/help-overlay.ui:188 ui/gtk/help-overlay.ui:397
msgctxt "shortcut window"
msgid "General"
msgstr "General"
@ -3511,121 +3507,124 @@ msgctxt "shortcut window"
msgid "Focus next/previous message"
msgstr "Mover el foco al mensaje siguiente/anterior"
#: ui/gtk/help-overlay.ui:258
msgctxt "shortcut window"
msgid "Single-key shortcuts"
#: ui/gtk/help-overlay.ui:260
#| msgctxt "shortcut window"
#| msgid "Single-key shortcuts"
msgid "Single-key Shortcuts"
msgstr "Atajos de una sola tecla"
#: ui/gtk/help-overlay.ui:262
#: ui/gtk/help-overlay.ui:265
#| msgctxt "shortcut window"
#| msgid "Single-key shortcuts"
msgctxt "shortcut window"
msgid "Single-key shortcuts (if enabled)"
msgstr "Atajos de una sola tecla (si están activados)"
#: ui/gtk/help-overlay.ui:274
msgctxt "shortcut window"
msgid "Reply to sender "
msgstr "Responder al remitente "
#: ui/gtk/help-overlay.ui:332
msgctxt "shortcut window"
msgid "Find in current conversations"
msgstr "Buscar en las conversaciones actual"
#: ui/gtk/help-overlay.ui:339
#: ui/gtk/help-overlay.ui:361
msgctxt "shortcut window"
msgid "Select next/previous conversations"
msgstr "Seleccionar conversaciones siguientes/anteriores"
#: ui/gtk/help-overlay.ui:351
#: ui/gtk/help-overlay.ui:373
msgid "Composer Shortcuts"
msgstr "Atajos del editor"
#: ui/gtk/help-overlay.ui:359
#: ui/gtk/help-overlay.ui:381
msgctxt "shortcut window"
msgid "Send"
msgstr "Enviar"
#: ui/gtk/help-overlay.ui:366
#: ui/gtk/help-overlay.ui:388
msgctxt "shortcut window"
msgid "Add attachment"
msgstr "Añadir un adjunto"
#: ui/gtk/help-overlay.ui:379
#: ui/gtk/help-overlay.ui:401
msgctxt "shortcut window"
msgid "Close composer window"
msgstr "Cerrar la ventana del editor"
#: ui/gtk/help-overlay.ui:386
#: ui/gtk/help-overlay.ui:408
msgctxt "shortcut window"
msgid "Detach composer window"
msgstr "Desacoplar la ventana del editor"
#: ui/gtk/help-overlay.ui:393
#: ui/gtk/help-overlay.ui:415
msgctxt "shortcut window"
msgid "Editing"
msgstr "Edición"
#: ui/gtk/help-overlay.ui:398
#: ui/gtk/help-overlay.ui:420
msgctxt "shortcut window"
msgid "Move selection to the clipboard"
msgstr "Mover la selección al portapapeles"
#: ui/gtk/help-overlay.ui:405
#: ui/gtk/help-overlay.ui:427
msgctxt "shortcut window"
msgid "Copy selection to clipboard"
msgstr "Copiar selección a portapapeles"
#: ui/gtk/help-overlay.ui:412
#: ui/gtk/help-overlay.ui:434
msgctxt "shortcut window"
msgid "Paste from the clipboard"
msgstr "Pegar desde el portapapeles"
#: ui/gtk/help-overlay.ui:419
#: ui/gtk/help-overlay.ui:441
msgctxt "shortcut window"
msgid "Quote text"
msgstr "Citar el texto"
#: ui/gtk/help-overlay.ui:426
#: ui/gtk/help-overlay.ui:448
msgctxt "shortcut window"
msgid "Unquote text"
msgstr "Eliminar cita del texto"
#: ui/gtk/help-overlay.ui:435
#: ui/gtk/help-overlay.ui:457
msgctxt "shortcut window"
msgid "Rich text editing"
msgstr "Edición de texto enriquecido"
#: ui/gtk/help-overlay.ui:439
#: ui/gtk/help-overlay.ui:461
msgctxt "shortcut window"
msgid "Paste without formatting"
msgstr "Pegar sin formato"
#: ui/gtk/help-overlay.ui:446
#: ui/gtk/help-overlay.ui:468
msgctxt "shortcut window"
msgid "Bold text"
msgstr "Texto en negrita"
#: ui/gtk/help-overlay.ui:453
#: ui/gtk/help-overlay.ui:475
msgctxt "shortcut window"
msgid "Italicize text"
msgstr "Texto en cursiva"
#: ui/gtk/help-overlay.ui:460
#: ui/gtk/help-overlay.ui:482
msgctxt "shortcut window"
msgid "Underline text"
msgstr "Subrayar el texto"
#: ui/gtk/help-overlay.ui:467
#: ui/gtk/help-overlay.ui:489
msgctxt "shortcut window"
msgid "Strike text"
msgstr "Tachar el texto"
#: ui/gtk/help-overlay.ui:474
#: ui/gtk/help-overlay.ui:496
msgctxt "shortcut window"
msgid "Remove formatting"
msgstr "Eliminar el formato"
#: ui/gtk/help-overlay.ui:481
#: ui/gtk/help-overlay.ui:503
msgctxt "shortcut window"
msgid "Insert an image"
msgstr "Insertar una imagen"
#: ui/gtk/help-overlay.ui:488
#: ui/gtk/help-overlay.ui:510
msgctxt "shortcut window"
msgid "Insert a link"
msgstr "Insertar un enlace"
@ -3695,6 +3694,10 @@ msgstr "_Autenticar"
msgid "Geary update in progress…"
msgstr "Actualización de Geary en proceso…"
#~ msgctxt "shortcut window"
#~ msgid "Find in current conversations"
#~ msgstr "Buscar en las conversaciones actual"
#~ msgid "Log periodic activity"
#~ msgstr "Registrar la actividad periódica"

507
po/ja.po

File diff suppressed because it is too large Load diff

View file

@ -10,8 +10,8 @@ msgid ""
msgstr ""
"Project-Id-Version: geary-0.4.1\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
"POT-Creation-Date: 2020-05-03 07:30+0000\n"
"PO-Revision-Date: 2020-05-04 10:21+0200\n"
"POT-Creation-Date: 2020-05-04 08:24+0000\n"
"PO-Revision-Date: 2020-05-08 14:50+0200\n"
"Last-Translator: Dušan Kazik <prescott66@gmail.com>\n"
"Language-Team: Slovak <gnome-sk-list@gnome.org>\n"
"Language: sk\n"
@ -256,7 +256,6 @@ msgid "Notify of new mail at startup"
msgstr "Oznámiť novú poštu po spustení"
#: desktop/org.gnome.Geary.gschema.xml:97
#| msgid "Desktop notification of new mail"
msgid "True to notify of new mail at startup."
msgstr "Nastavením na True, bude oznamovaná nová pošta po spustení."
@ -265,7 +264,6 @@ msgid "Ask when opening an attachment"
msgstr ""
#: desktop/org.gnome.Geary.gschema.xml:103
#| msgid "To add them as attachments"
msgid "True to ask when opening an attachment."
msgstr "Nastavením na True, bude zobrazená výzva pri otváraní prílohy."
@ -708,7 +706,7 @@ msgstr "TLS"
#: src/client/accounts/accounts-editor-row.vala:534
#: ui/application-main-window.ui:346
msgid "Login"
msgstr "Prihlasovacie meno"
msgstr "Prihlásenie"
#. Translators: ComboBox value for source of SMTP
#. authentication credentials (none) when adding a new
@ -892,7 +890,6 @@ msgstr "Otvorí nové okno"
#. / Command line option
#: src/client/application/application-client.vala:137
#| msgid "Revoke all server certificates with TLS warnings"
msgid "Revoke all pinned TLS server certificates"
msgstr "Odvolá všetky certifikáty pripnutých serverov TLS"
@ -981,7 +978,6 @@ msgstr "Nerozpoznaný parameter programu: „%s“"
#. / Notification title.
#: src/client/application/application-controller.vala:458
#, c-format
#| msgid "A problem occurred sending mail for %s"
msgid "A problem occurred sending email for %s"
msgstr "Vyskytol sa problém pri odosielaní emailu z účtu %s"
@ -1000,7 +996,6 @@ msgstr[2] "Rozhovory boli označené"
#. / Translators: Label for in-app notification
#: src/client/application/application-controller.vala:574
#| msgid "No conversations found"
msgid "Conversation un-marked"
msgid_plural "Conversations un-marked"
msgstr[0] "Rozhovorom bolo zrušené označenie"
@ -2515,11 +2510,11 @@ msgstr "Vytvoriť"
#: ui/accounts_editor_add_pane.ui:130 ui/accounts_editor_servers_pane.ui:125
msgid "Receiving"
msgstr "Prijíma sa"
msgstr "Prijímanie"
#: ui/accounts_editor_add_pane.ui:178 ui/accounts_editor_servers_pane.ui:165
msgid "Sending"
msgstr "Odosiela sa"
msgstr "Odosielanie"
#: ui/accounts_editor_edit_pane.ui:8
msgid "Edit Account"
@ -2640,7 +2635,6 @@ msgstr "Účet nahlásil nedôveryhodný server."
#. Button tooltip for retrying when a login error has occurred
#: ui/application-main-window.ui:350
#| msgid "Retry receiving email, you will be prompted for a password"
msgid "Retry login, you will be prompted for your password"
msgstr "Znovu sa pokúsi o prihlásenie, budete vyzvaný na zadanie hesla"
@ -2816,27 +2810,21 @@ msgstr "Preškrtnutý text"
# tooltip
#: ui/composer-widget.ui:707
#| msgid "Insert unordered list"
msgid "Insert bulleted list"
msgstr "Vloží zoznam s odrážkami"
# tooltip
#: ui/composer-widget.ui:731
#| msgid "Insert unordered list"
msgid "Insert numbered list"
msgstr "Vloží číslovaný zoznam"
# tooltip
#: ui/composer-widget.ui:764
#| msgctxt "shortcut window"
#| msgid "Unquote text"
msgid "Indent or quote text"
msgstr "Odsadí alebo cituje text"
# tooltip
#: ui/composer-widget.ui:788
#| msgctxt "shortcut window"
#| msgid "Unquote text"
msgid "Un-indent or unquote text"
msgstr "Zruší odsadenie alebo citáciu textu"
@ -2991,7 +2979,6 @@ msgstr "Prepne pridávanie nových položiek záznamu"
#. Tooltip for inspector button
#. Tooltip for problem report button
#: ui/components-inspector.ui:37 ui/problem-details-dialog.ui:19
#| msgid "Search for more languages"
msgid "Search for matching log entries"
msgstr "Vyhľadá zhodné položky záznamu"
@ -3041,7 +3028,6 @@ msgstr "Odstráni emailové adresy"
#. Contact popover label
#: ui/conversation-contact-popover.ui:294
#| msgid "Email address:"
msgid "This email address is:"
msgstr "Táto emailová adresa je:"
@ -3407,8 +3393,6 @@ msgid "Go to next/previous pane"
msgstr "Prejdenie na nasledovný/predošlý panel"
#: ui/gtk/help-overlay.ui:241
#| msgctxt "shortcut window"
#| msgid "Find next/previous in current conversation"
msgctxt "shortcut window"
msgid "Select next/previous conversation"
msgstr "Výber nasledovného/predošlého rozhovoru"
@ -3419,8 +3403,6 @@ msgid "Focus next/previous message"
msgstr "Zameranie na nasledovnú/predošlú správu"
#: ui/gtk/help-overlay.ui:258
#| msgctxt "shortcut window"
#| msgid "Show keyboard shortcuts"
msgctxt "shortcut window"
msgid "Single-key shortcuts"
msgstr "Klávesové skratky o jednom klávese"
@ -3575,8 +3557,6 @@ msgid "_Archive"
msgstr "_Archivovať"
#: ui/main-toolbar-menus.ui:21
#| msgctxt "shortcut window"
#| msgid "Toggle spam"
msgid "Toggle as S_pam"
msgstr "Prepnúť ako _nevyžiadanú poštu"

143
po/tr.po
View file

@ -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: 2020-04-23 04:13+0000\n"
"PO-Revision-Date: 2020-04-25 15:34+0300\n"
"POT-Creation-Date: 2020-05-14 15:06+0000\n"
"PO-Revision-Date: 2020-05-16 15:14+0300\n"
"Last-Translator: Emin Tufan Çetin <etcetin@gmail.com>\n"
"Language-Team: Turkish <gnome-turk@gnome.org>\n"
"Language: tr\n"
@ -1218,7 +1218,11 @@ msgstr "Bağlantı güvenlik ayrıntılarını gözden geçirin"
msgid "%s — %s"
msgstr "%s — %s"
#. Translators: The name of the folder group containing
#. folders created by people (as opposed to special-use
#. folders)
#: src/client/application/application-main-window.vala:972
#: src/client/folder-list/folder-list-account-branch.vala:43
msgid "Labels"
msgstr "Etiketler"
@ -1268,7 +1272,7 @@ msgstr "%s (%d)"
#. 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:1918
#: src/client/composer/composer-widget.vala:1920
#, c-format
msgid "%s (%s)"
msgstr "%s (%s)"
@ -1367,7 +1371,7 @@ msgid "Preferences"
msgstr "Tercihler"
#. / Translators: Preferences page title
#: src/client/components/components-preferences-window.vala:179
#: src/client/components/components-preferences-window.vala:180
msgid "Plugins"
msgstr "Eklentiler"
@ -1666,63 +1670,63 @@ msgstr "İleti, ileti gövdesi olmadan gönderilsin mi?"
msgid "Send message without an attachment?"
msgstr "İleti eki olmadan gönderilsin mi?"
#: src/client/composer/composer-widget.vala:1904
#: src/client/composer/composer-widget.vala:1906
#, c-format
msgid "“%s” already attached for delivery."
msgstr "“%s” gönderim için zaten eklendi."
#: src/client/composer/composer-widget.vala:1940
#: src/client/composer/composer-widget.vala:1990
#: src/client/composer/composer-widget.vala:1942
#: src/client/composer/composer-widget.vala:1992
#, c-format
msgid "“%s” is an empty file."
msgstr "“%s” boş bir dosya."
#: src/client/composer/composer-widget.vala:1978
#: src/client/composer/composer-widget.vala:1980
#, c-format
msgid "“%s” could not be found."
msgstr "“%s” bulunamadı."
#: src/client/composer/composer-widget.vala:1984
#: src/client/composer/composer-widget.vala:1986
#, c-format
msgid "“%s” is a folder."
msgstr "“%s” bir klasör."
#: src/client/composer/composer-widget.vala:2003
#: src/client/composer/composer-widget.vala:2005
#, c-format
msgid "“%s” could not be opened for reading."
msgstr "“%s” okuma için açılamadı."
#: src/client/composer/composer-widget.vala:2011
#: src/client/composer/composer-widget.vala:2013
msgid "Cannot add attachment"
msgstr "Eklenti eklenemiyor"
#. Translators: Human-readable version of the RFC 822 To header
#: src/client/composer/composer-widget.vala:2071
#: src/client/composer/composer-widget.vala:2073
#: src/client/conversation-viewer/conversation-email.vala:542
#: src/client/util/util-email.vala:236 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:2077
#: src/client/composer/composer-widget.vala:2079
#: src/client/conversation-viewer/conversation-email.vala:547
#: src/client/util/util-email.vala:241 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:2083
#: src/client/composer/composer-widget.vala:2085
#: src/client/conversation-viewer/conversation-email.vala:552
#: 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:2089
#: src/client/composer/composer-widget.vala:2091
msgid "Reply-To: "
msgstr "Şuna Yanıtla: "
#: src/client/composer/composer-widget.vala:2341
#: src/client/composer/composer-widget.vala:2343
msgid "Select Color"
msgstr "Renk Seç"
@ -1731,14 +1735,14 @@ 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:2530
#: src/client/composer/composer-widget.vala:2532
#, c-format
msgid "%1$s via %2$s"
msgstr "%2$s aracılığıyla %1$s"
#. Translators: This is the name of the file chooser filter
#. when inserting an image in the composer.
#: src/client/composer/composer-widget.vala:2887
#: src/client/composer/composer-widget.vala:2889
msgid "Images"
msgstr "Resimler"
@ -2660,17 +2664,17 @@ msgstr "Silinen Ögeler"
msgid "Archive | Archives"
msgstr "Arşiv | Arşivler"
#: src/engine/rfc822/rfc822-message.vala:461
#: src/engine/rfc822/rfc822-message.vala:531
#, c-format
msgid "Could not determine mime type for “%s”."
msgstr "“%s” için mime türü saptanamadı."
#: src/engine/rfc822/rfc822-message.vala:472
#: src/engine/rfc822/rfc822-message.vala:542
#, c-format
msgid "Could not determine content type for mime type “%s” on “%s”."
msgstr "“%2$s” üstündeki “%1$s” mime türü için içerik türü saptanamadı."
#: src/engine/rfc822/rfc822-message.vala:1003
#: src/engine/rfc822/rfc822-message.vala:1001
msgid "(no subject)"
msgstr "(konu yok)"
@ -2679,14 +2683,14 @@ msgid "Add an account"
msgstr "Hesap ekle"
#: ui/accounts_editor_add_pane.ui:53
msgid "Create"
msgstr "Oluştur"
msgid "_Create"
msgstr "_Oluştur"
#: ui/accounts_editor_add_pane.ui:130 ui/accounts_editor_servers_pane.ui:125
#: ui/accounts_editor_add_pane.ui:131 ui/accounts_editor_servers_pane.ui:125
msgid "Receiving"
msgstr "Alım"
#: ui/accounts_editor_add_pane.ui:178 ui/accounts_editor_servers_pane.ui:165
#: ui/accounts_editor_add_pane.ui:179 ui/accounts_editor_servers_pane.ui:165
msgid "Sending"
msgstr "Gönderiliyor"
@ -3281,7 +3285,7 @@ msgstr "etiket"
msgid "Conversation Shortcuts"
msgstr "Konuşma Kısayolları"
#: ui/gtk/help-overlay.ui:13 ui/gtk/help-overlay.ui:355
#: ui/gtk/help-overlay.ui:13 ui/gtk/help-overlay.ui:377
msgctxt "shortcut window"
msgid "Actions"
msgstr "Eylemler"
@ -3296,37 +3300,37 @@ msgctxt "shortcut window"
msgid "Reply to sender"
msgstr "Göndereni yanıtla"
#: ui/gtk/help-overlay.ui:31 ui/gtk/help-overlay.ui:269
#: ui/gtk/help-overlay.ui:31 ui/gtk/help-overlay.ui:281
msgctxt "shortcut window"
msgid "Reply to all"
msgstr "Tümünü yanıtla"
#: ui/gtk/help-overlay.ui:38 ui/gtk/help-overlay.ui:276
#: ui/gtk/help-overlay.ui:38 ui/gtk/help-overlay.ui:288
msgctxt "shortcut window"
msgid "Forward"
msgstr "Yönlendir"
#: ui/gtk/help-overlay.ui:45 ui/gtk/help-overlay.ui:283
#: ui/gtk/help-overlay.ui:45 ui/gtk/help-overlay.ui:295
msgctxt "shortcut window"
msgid "Un-mark/mark read"
msgstr "Okundu olarak imle/imi kaldır"
#: ui/gtk/help-overlay.ui:52 ui/gtk/help-overlay.ui:290
#: ui/gtk/help-overlay.ui:52 ui/gtk/help-overlay.ui:302
msgctxt "shortcut window"
msgid "Mark/un-mark starred"
msgstr "Yıldızla/Yıldızı kaldır"
#: ui/gtk/help-overlay.ui:59 ui/gtk/help-overlay.ui:297
#: ui/gtk/help-overlay.ui:59 ui/gtk/help-overlay.ui:309
msgctxt "shortcut window"
msgid "Archive conversations"
msgstr "Konuşmaları arşivle"
#: ui/gtk/help-overlay.ui:66 ui/gtk/help-overlay.ui:304
#: ui/gtk/help-overlay.ui:66 ui/gtk/help-overlay.ui:326
msgctxt "shortcut window"
msgid "Move conversations"
msgstr "Konuşmaları taşı"
#: ui/gtk/help-overlay.ui:73 ui/gtk/help-overlay.ui:311
#: ui/gtk/help-overlay.ui:73 ui/gtk/help-overlay.ui:333
msgctxt "shortcut window"
msgid "Label conversations"
msgstr "Konuşmaları etiketle"
@ -3336,12 +3340,12 @@ msgctxt "shortcut window"
msgid "Trash conversations"
msgstr "Konuşmaları çöpe at"
#: ui/gtk/help-overlay.ui:87 ui/gtk/help-overlay.ui:318
#: ui/gtk/help-overlay.ui:87 ui/gtk/help-overlay.ui:340
msgctxt "shortcut window"
msgid "Junk conversations"
msgstr "Konuşmaları gereksize taşı"
#: ui/gtk/help-overlay.ui:95 ui/gtk/help-overlay.ui:325
#: ui/gtk/help-overlay.ui:95 ui/gtk/help-overlay.ui:347
msgctxt "shortcut window"
msgid "Delete conversations"
msgstr "Konuşmaları sil"
@ -3356,7 +3360,7 @@ msgctxt "shortcut window"
msgid "Search for conversations"
msgstr "Konuşmaları ara"
#: ui/gtk/help-overlay.ui:115
#: ui/gtk/help-overlay.ui:115 ui/gtk/help-overlay.ui:354
msgctxt "shortcut window"
msgid "Find in current conversation"
msgstr "Şimdiki konuşmada bul"
@ -3396,7 +3400,7 @@ msgctxt "shortcut window"
msgid "Reset zoom"
msgstr "Yakınlaştırmayı sıfırla"
#: ui/gtk/help-overlay.ui:188 ui/gtk/help-overlay.ui:375
#: ui/gtk/help-overlay.ui:188 ui/gtk/help-overlay.ui:397
msgctxt "shortcut window"
msgid "General"
msgstr "Genel"
@ -3446,121 +3450,120 @@ msgctxt "shortcut window"
msgid "Focus next/previous message"
msgstr "Önceki/sonraki iletiye odaklan"
#: ui/gtk/help-overlay.ui:258
msgctxt "shortcut window"
msgid "Single-key shortcuts"
msgstr "Tek tuşlu kısayollar"
#: ui/gtk/help-overlay.ui:260
msgid "Single-key Shortcuts"
msgstr "Tek Tuşlu Kısayollar"
#: ui/gtk/help-overlay.ui:262
#: ui/gtk/help-overlay.ui:265
msgctxt "shortcut window"
msgid "Single-key shortcuts (if enabled)"
msgstr "Tek tuşlu kısayollar (etkinse)"
#: ui/gtk/help-overlay.ui:274
msgctxt "shortcut window"
msgid "Reply to sender "
msgstr "Göndereni yanıtla "
#: ui/gtk/help-overlay.ui:332
msgctxt "shortcut window"
msgid "Find in current conversations"
msgstr "Geçerli konuşmalarda bul"
#: ui/gtk/help-overlay.ui:339
#: ui/gtk/help-overlay.ui:361
msgctxt "shortcut window"
msgid "Select next/previous conversations"
msgstr "Sonraki/önceki konuşmaları seç"
#: ui/gtk/help-overlay.ui:351
#: ui/gtk/help-overlay.ui:373
msgid "Composer Shortcuts"
msgstr "Oluşturucu Kısayolları"
#: ui/gtk/help-overlay.ui:359
#: ui/gtk/help-overlay.ui:381
msgctxt "shortcut window"
msgid "Send"
msgstr "Gönder"
#: ui/gtk/help-overlay.ui:366
#: ui/gtk/help-overlay.ui:388
msgctxt "shortcut window"
msgid "Add attachment"
msgstr "Ek ekle"
#: ui/gtk/help-overlay.ui:379
#: ui/gtk/help-overlay.ui:401
msgctxt "shortcut window"
msgid "Close composer window"
msgstr "Oluşturucu penceresini kapat"
#: ui/gtk/help-overlay.ui:386
#: ui/gtk/help-overlay.ui:408
msgctxt "shortcut window"
msgid "Detach composer window"
msgstr "Oluşturucu penceresini ayır"
#: ui/gtk/help-overlay.ui:393
#: ui/gtk/help-overlay.ui:415
msgctxt "shortcut window"
msgid "Editing"
msgstr "Düzenleme"
#: ui/gtk/help-overlay.ui:398
#: ui/gtk/help-overlay.ui:420
msgctxt "shortcut window"
msgid "Move selection to the clipboard"
msgstr "Seçimi panoya taşı"
#: ui/gtk/help-overlay.ui:405
#: ui/gtk/help-overlay.ui:427
msgctxt "shortcut window"
msgid "Copy selection to clipboard"
msgstr "Seçimi panoya kopyala"
#: ui/gtk/help-overlay.ui:412
#: ui/gtk/help-overlay.ui:434
msgctxt "shortcut window"
msgid "Paste from the clipboard"
msgstr "Panodan yapıştır"
#: ui/gtk/help-overlay.ui:419
#: ui/gtk/help-overlay.ui:441
msgctxt "shortcut window"
msgid "Quote text"
msgstr "Metni alıntıla"
#: ui/gtk/help-overlay.ui:426
#: ui/gtk/help-overlay.ui:448
msgctxt "shortcut window"
msgid "Unquote text"
msgstr "Metni alıntılama"
#: ui/gtk/help-overlay.ui:435
#: ui/gtk/help-overlay.ui:457
msgctxt "shortcut window"
msgid "Rich text editing"
msgstr "Zengin metin düzenleme"
#: ui/gtk/help-overlay.ui:439
#: ui/gtk/help-overlay.ui:461
msgctxt "shortcut window"
msgid "Paste without formatting"
msgstr "Biçimlendirmeden yapıştır"
#: ui/gtk/help-overlay.ui:446
#: ui/gtk/help-overlay.ui:468
msgctxt "shortcut window"
msgid "Bold text"
msgstr "Kalın yazı"
#: ui/gtk/help-overlay.ui:453
#: ui/gtk/help-overlay.ui:475
msgctxt "shortcut window"
msgid "Italicize text"
msgstr "Eğik metin"
#: ui/gtk/help-overlay.ui:460
#: ui/gtk/help-overlay.ui:482
msgctxt "shortcut window"
msgid "Underline text"
msgstr "Metnin altını çiz"
#: ui/gtk/help-overlay.ui:467
#: ui/gtk/help-overlay.ui:489
msgctxt "shortcut window"
msgid "Strike text"
msgstr "Çizgili metin"
#: ui/gtk/help-overlay.ui:474
#: ui/gtk/help-overlay.ui:496
msgctxt "shortcut window"
msgid "Remove formatting"
msgstr "Biçimlendirmeyi kaldır"
#: ui/gtk/help-overlay.ui:481
#: ui/gtk/help-overlay.ui:503
msgctxt "shortcut window"
msgid "Insert an image"
msgstr "Resim yerleştir"
#: ui/gtk/help-overlay.ui:488
#: ui/gtk/help-overlay.ui:510
msgctxt "shortcut window"
msgid "Insert a link"
msgstr "Bağlantı yerleştir"
@ -3630,6 +3633,10 @@ msgstr "_Kimlik Doğrula"
msgid "Geary update in progress…"
msgstr "Geary güncellemesi sürüyor…"
#~ msgctxt "shortcut window"
#~ msgid "Find in current conversations"
#~ msgstr "Geçerli konuşmalarda bul"
#~ msgid "Log periodic activity"
#~ msgstr "Dönemsel etkinliği kayda al"

126
po/uk.po
View file

@ -12,8 +12,8 @@ msgid ""
msgstr ""
"Project-Id-Version: geary-0.4.1\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
"POT-Creation-Date: 2020-05-03 07:37+0000\n"
"PO-Revision-Date: 2020-05-03 14:57+0300\n"
"POT-Creation-Date: 2020-05-14 15:06+0000\n"
"PO-Revision-Date: 2020-05-16 17:48+0300\n"
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
"Language-Team: Ukrainian <kde-i18n-uk@kde.org>\n"
"Language: uk\n"
@ -793,7 +793,7 @@ msgstr "Зберігати чернетки пошти на сервері"
#. preference.
#: src/client/accounts/accounts-editor-servers-pane.vala:666
msgid "Save sent email on server"
msgstr "Зберігати надіслано пошту на сервері"
msgstr "Зберігати надіслану пошту на сервері"
#. Add a suffix for OAuth2 auth so people know they
#. shouldn't expect to be prompted for a password
@ -1330,7 +1330,7 @@ msgstr "%s (%d)"
#. 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:1918
#: src/client/composer/composer-widget.vala:1920
#, c-format
msgid "%s (%s)"
msgstr "%s (%s)"
@ -1748,63 +1748,63 @@ msgstr "Надіслати лист без тексту?"
msgid "Send message without an attachment?"
msgstr "Надіслати лист без долучень?"
#: src/client/composer/composer-widget.vala:1904
#: src/client/composer/composer-widget.vala:1906
#, c-format
msgid "“%s” already attached for delivery."
msgstr "«%s» уже долучено для доставлення."
#: src/client/composer/composer-widget.vala:1940
#: src/client/composer/composer-widget.vala:1990
#: src/client/composer/composer-widget.vala:1942
#: src/client/composer/composer-widget.vala:1992
#, c-format
msgid "“%s” is an empty file."
msgstr "«%s» — порожній файл."
#: src/client/composer/composer-widget.vala:1978
#: src/client/composer/composer-widget.vala:1980
#, c-format
msgid "“%s” could not be found."
msgstr "Не вдалося знайти «%s»."
#: src/client/composer/composer-widget.vala:1984
#: src/client/composer/composer-widget.vala:1986
#, c-format
msgid "“%s” is a folder."
msgstr "«%s» — тека."
#: src/client/composer/composer-widget.vala:2003
#: src/client/composer/composer-widget.vala:2005
#, c-format
msgid "“%s” could not be opened for reading."
msgstr "Не вдалося відкрити «%s» для читання."
#: src/client/composer/composer-widget.vala:2011
#: src/client/composer/composer-widget.vala:2013
msgid "Cannot add attachment"
msgstr "Неможливо додати долучення"
#. Translators: Human-readable version of the RFC 822 To header
#: src/client/composer/composer-widget.vala:2071
#: src/client/composer/composer-widget.vala:2073
#: src/client/conversation-viewer/conversation-email.vala:542
#: src/client/util/util-email.vala:236 ui/conversation-message.ui:312
msgid "To:"
msgstr "Кому:"
#. Translators: Human-readable version of the RFC 822 CC header
#: src/client/composer/composer-widget.vala:2077
#: src/client/composer/composer-widget.vala:2079
#: src/client/conversation-viewer/conversation-email.vala:547
#: src/client/util/util-email.vala:241 ui/conversation-message.ui:357
msgid "Cc:"
msgstr "Копія:"
#. Translators: Human-readable version of the RFC 822 BCC header
#: src/client/composer/composer-widget.vala:2083
#: src/client/composer/composer-widget.vala:2085
#: src/client/conversation-viewer/conversation-email.vala:552
#: ui/conversation-message.ui:402
msgid "Bcc:"
msgstr "Потайна копія:"
#. Translators: Human-readable version of the RFC 822 Reply-To header
#: src/client/composer/composer-widget.vala:2089
#: src/client/composer/composer-widget.vala:2091
msgid "Reply-To: "
msgstr "Відповісти:"
#: src/client/composer/composer-widget.vala:2341
#: src/client/composer/composer-widget.vala:2343
msgid "Select Color"
msgstr "Вибрати колір"
@ -1813,14 +1813,14 @@ msgstr "Вибрати колір"
#. 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:2530
#: src/client/composer/composer-widget.vala:2532
#, c-format
msgid "%1$s via %2$s"
msgstr "%1$s через %2$s"
#. Translators: This is the name of the file chooser filter
#. when inserting an image in the composer.
#: src/client/composer/composer-widget.vala:2887
#: src/client/composer/composer-widget.vala:2889
msgid "Images"
msgstr "Зображення"
@ -2792,17 +2792,17 @@ msgstr "Вилучені записи"
msgid "Archive | Archives"
msgstr "Архів | Архіви | Archive | Archives"
#: src/engine/rfc822/rfc822-message.vala:461
#: src/engine/rfc822/rfc822-message.vala:531
#, c-format
msgid "Could not determine mime type for “%s”."
msgstr "Не вдалося визначити тип MIME для «%s»."
#: src/engine/rfc822/rfc822-message.vala:472
#: src/engine/rfc822/rfc822-message.vala:542
#, c-format
msgid "Could not determine content type for mime type “%s” on “%s”."
msgstr "Не вдалося визначити тип даних для типу MIME «%s» у «%s»."
#: src/engine/rfc822/rfc822-message.vala:1003
#: src/engine/rfc822/rfc822-message.vala:1001
msgid "(no subject)"
msgstr "(без теми)"
@ -2811,7 +2811,6 @@ msgid "Add an account"
msgstr "Додати обліковий запис"
#: ui/accounts_editor_add_pane.ui:53
#| msgid "Create"
msgid "_Create"
msgstr "С_творити"
@ -3421,7 +3420,7 @@ msgstr "мітка"
msgid "Conversation Shortcuts"
msgstr "Скорочення панелі спілкування"
#: ui/gtk/help-overlay.ui:13 ui/gtk/help-overlay.ui:355
#: ui/gtk/help-overlay.ui:13 ui/gtk/help-overlay.ui:377
msgctxt "shortcut window"
msgid "Actions"
msgstr "Дії"
@ -3436,37 +3435,37 @@ msgctxt "shortcut window"
msgid "Reply to sender"
msgstr "Відповісти відправнику"
#: ui/gtk/help-overlay.ui:31 ui/gtk/help-overlay.ui:269
#: ui/gtk/help-overlay.ui:31 ui/gtk/help-overlay.ui:281
msgctxt "shortcut window"
msgid "Reply to all"
msgstr "Відповісти всім"
#: ui/gtk/help-overlay.ui:38 ui/gtk/help-overlay.ui:276
#: ui/gtk/help-overlay.ui:38 ui/gtk/help-overlay.ui:288
msgctxt "shortcut window"
msgid "Forward"
msgstr "Переслати"
#: ui/gtk/help-overlay.ui:45 ui/gtk/help-overlay.ui:283
#: ui/gtk/help-overlay.ui:45 ui/gtk/help-overlay.ui:295
msgctxt "shortcut window"
msgid "Un-mark/mark read"
msgstr "Позначити або зняти позначення як прочитаного"
#: ui/gtk/help-overlay.ui:52 ui/gtk/help-overlay.ui:290
#: ui/gtk/help-overlay.ui:52 ui/gtk/help-overlay.ui:302
msgctxt "shortcut window"
msgid "Mark/un-mark starred"
msgstr "Позначити або зняти позначення зірками"
#: ui/gtk/help-overlay.ui:59 ui/gtk/help-overlay.ui:297
#: ui/gtk/help-overlay.ui:59 ui/gtk/help-overlay.ui:309
msgctxt "shortcut window"
msgid "Archive conversations"
msgstr "Архівувати спілкування"
#: ui/gtk/help-overlay.ui:66 ui/gtk/help-overlay.ui:304
#: ui/gtk/help-overlay.ui:66 ui/gtk/help-overlay.ui:326
msgctxt "shortcut window"
msgid "Move conversations"
msgstr "Пересунути спілкування"
#: ui/gtk/help-overlay.ui:73 ui/gtk/help-overlay.ui:311
#: ui/gtk/help-overlay.ui:73 ui/gtk/help-overlay.ui:333
msgctxt "shortcut window"
msgid "Label conversations"
msgstr "Позначити спілкування"
@ -3476,12 +3475,12 @@ msgctxt "shortcut window"
msgid "Trash conversations"
msgstr "Пересунути спілкування до смітника"
#: ui/gtk/help-overlay.ui:87 ui/gtk/help-overlay.ui:318
#: ui/gtk/help-overlay.ui:87 ui/gtk/help-overlay.ui:340
msgctxt "shortcut window"
msgid "Junk conversations"
msgstr "Пересунути спілкування до спаму"
#: ui/gtk/help-overlay.ui:95 ui/gtk/help-overlay.ui:325
#: ui/gtk/help-overlay.ui:95 ui/gtk/help-overlay.ui:347
msgctxt "shortcut window"
msgid "Delete conversations"
msgstr "Вилучити спілкування"
@ -3496,7 +3495,7 @@ msgctxt "shortcut window"
msgid "Search for conversations"
msgstr "Шукати спілкування"
#: ui/gtk/help-overlay.ui:115
#: ui/gtk/help-overlay.ui:115 ui/gtk/help-overlay.ui:354
msgctxt "shortcut window"
msgid "Find in current conversation"
msgstr "Знайти у поточному спілкуванні"
@ -3536,7 +3535,7 @@ msgctxt "shortcut window"
msgid "Reset zoom"
msgstr "Відновити масштаб"
#: ui/gtk/help-overlay.ui:188 ui/gtk/help-overlay.ui:375
#: ui/gtk/help-overlay.ui:188 ui/gtk/help-overlay.ui:397
msgctxt "shortcut window"
msgid "General"
msgstr "Загальні"
@ -3586,121 +3585,120 @@ msgctxt "shortcut window"
msgid "Focus next/previous message"
msgstr "Фокусувати наступне або попереднє повідомлення"
#: ui/gtk/help-overlay.ui:258
msgctxt "shortcut window"
msgid "Single-key shortcuts"
#: ui/gtk/help-overlay.ui:260
msgid "Single-key Shortcuts"
msgstr "Одноклавішні скорочення"
#: ui/gtk/help-overlay.ui:262
#: ui/gtk/help-overlay.ui:265
msgctxt "shortcut window"
msgid "Single-key shortcuts (if enabled)"
msgstr "Одноклавішні скорочення (якщо увімкнено)"
#: ui/gtk/help-overlay.ui:274
msgctxt "shortcut window"
msgid "Reply to sender "
msgstr "Відповісти відправнику"
#: ui/gtk/help-overlay.ui:332
msgctxt "shortcut window"
msgid "Find in current conversations"
msgstr "Знайти у поточних спілкуваннях"
#: ui/gtk/help-overlay.ui:339
#: ui/gtk/help-overlay.ui:361
msgctxt "shortcut window"
msgid "Select next/previous conversations"
msgstr "Вибрати наступні або попередні спілкування"
#: ui/gtk/help-overlay.ui:351
#: ui/gtk/help-overlay.ui:373
msgid "Composer Shortcuts"
msgstr "Скорочення вікна редактора"
#: ui/gtk/help-overlay.ui:359
#: ui/gtk/help-overlay.ui:381
msgctxt "shortcut window"
msgid "Send"
msgstr "Надіслати"
#: ui/gtk/help-overlay.ui:366
#: ui/gtk/help-overlay.ui:388
msgctxt "shortcut window"
msgid "Add attachment"
msgstr "Додати долучення"
#: ui/gtk/help-overlay.ui:379
#: ui/gtk/help-overlay.ui:401
msgctxt "shortcut window"
msgid "Close composer window"
msgstr "Закрити вікно редактора"
#: ui/gtk/help-overlay.ui:386
#: ui/gtk/help-overlay.ui:408
msgctxt "shortcut window"
msgid "Detach composer window"
msgstr "Від'єднати вікно редактора"
#: ui/gtk/help-overlay.ui:393
#: ui/gtk/help-overlay.ui:415
msgctxt "shortcut window"
msgid "Editing"
msgstr "Редагування"
#: ui/gtk/help-overlay.ui:398
#: ui/gtk/help-overlay.ui:420
msgctxt "shortcut window"
msgid "Move selection to the clipboard"
msgstr "Пересунути позначене до буфера обміну даними"
#: ui/gtk/help-overlay.ui:405
#: ui/gtk/help-overlay.ui:427
msgctxt "shortcut window"
msgid "Copy selection to clipboard"
msgstr "Копіювати позначений фрагмент до буфера обміну даними"
#: ui/gtk/help-overlay.ui:412
#: ui/gtk/help-overlay.ui:434
msgctxt "shortcut window"
msgid "Paste from the clipboard"
msgstr "Вставити з буфера обміну даними"
#: ui/gtk/help-overlay.ui:419
#: ui/gtk/help-overlay.ui:441
msgctxt "shortcut window"
msgid "Quote text"
msgstr "Цитувати текст"
#: ui/gtk/help-overlay.ui:426
#: ui/gtk/help-overlay.ui:448
msgctxt "shortcut window"
msgid "Unquote text"
msgstr "Скасувати цитування тексту"
#: ui/gtk/help-overlay.ui:435
#: ui/gtk/help-overlay.ui:457
msgctxt "shortcut window"
msgid "Rich text editing"
msgstr "Редагування з форматуванням"
#: ui/gtk/help-overlay.ui:439
#: ui/gtk/help-overlay.ui:461
msgctxt "shortcut window"
msgid "Paste without formatting"
msgstr "Вставити без форматування"
#: ui/gtk/help-overlay.ui:446
#: ui/gtk/help-overlay.ui:468
msgctxt "shortcut window"
msgid "Bold text"
msgstr "Зробити текст напівжирним"
#: ui/gtk/help-overlay.ui:453
#: ui/gtk/help-overlay.ui:475
msgctxt "shortcut window"
msgid "Italicize text"
msgstr "Зробити текст курсивним"
#: ui/gtk/help-overlay.ui:460
#: ui/gtk/help-overlay.ui:482
msgctxt "shortcut window"
msgid "Underline text"
msgstr "Підкреслити текст"
#: ui/gtk/help-overlay.ui:467
#: ui/gtk/help-overlay.ui:489
msgctxt "shortcut window"
msgid "Strike text"
msgstr "Закреслити текст"
#: ui/gtk/help-overlay.ui:474
#: ui/gtk/help-overlay.ui:496
msgctxt "shortcut window"
msgid "Remove formatting"
msgstr "Вилучити форматування"
#: ui/gtk/help-overlay.ui:481
#: ui/gtk/help-overlay.ui:503
msgctxt "shortcut window"
msgid "Insert an image"
msgstr "Вставити зображення"
#: ui/gtk/help-overlay.ui:488
#: ui/gtk/help-overlay.ui:510
msgctxt "shortcut window"
msgid "Insert a link"
msgstr "Вставити посилання"

View file

@ -1051,7 +1051,7 @@ public class Accounts.AccountConfigV1 : AccountConfig, GLib.Object {
senders.add(
new Geary.RFC822.MailboxAddress.from_rfc822_string(sender)
);
} catch (Geary.RFC822Error err) {
} catch (Geary.RFC822.Error err) {
throw new ConfigError.SYNTAX(
"%s: Invalid sender address: %s", id, sender
);
@ -1274,10 +1274,15 @@ public class Accounts.AccountConfigLegacy : AccountConfig, GLib.Object {
ALTERNATE_EMAILS_KEY
);
foreach (string alt_email in alt_email_list) {
Geary.RFC822.MailboxAddresses mailboxes =
new Geary.RFC822.MailboxAddresses.from_rfc822_string(alt_email);
foreach (Geary.RFC822.MailboxAddress mailbox in mailboxes.get_all()) {
info.append_sender(mailbox);
try {
var mailboxes = new Geary.RFC822.MailboxAddresses.from_rfc822_string(alt_email);
foreach (Geary.RFC822.MailboxAddress mailbox in mailboxes.get_all()) {
info.append_sender(mailbox);
}
} catch (Geary.RFC822.Error error) {
throw new ConfigError.SYNTAX(
"Invalid alternate email: %s", error.message
);
}
}

View file

@ -63,7 +63,7 @@ public class CountBadge : Geary.BaseObject {
Pango.Rectangle? logical_rect;
layout_num.get_pixel_extents(out ink_rect, out logical_rect);
if (ctx != null) {
double bg_width = logical_rect.width + FormattedConversationData.LINE_SPACING;
double bg_width = logical_rect.width + FormattedConversationData.SPACING;
double bg_height = logical_rect.height;
double radius = bg_height / 2.0;
double degrees = Math.PI / 180.0;
@ -87,7 +87,7 @@ public class CountBadge : Geary.BaseObject {
Pango.cairo_show_layout(ctx, layout_num);
}
width = logical_rect.width + FormattedConversationData.LINE_SPACING;
width = logical_rect.width + FormattedConversationData.SPACING;
height = logical_rect.height;
}
}

View file

@ -81,9 +81,14 @@ public class Composer.EmailEntry : Gtk.Entry {
this._addresses = new Geary.RFC822.MailboxAddresses();
this.is_valid = false;
} else {
this._addresses =
new Geary.RFC822.MailboxAddresses.from_rfc822_string(text);
this.is_valid = true;
try {
this._addresses =
new Geary.RFC822.MailboxAddresses.from_rfc822_string(text);
this.is_valid = true;
} catch (Geary.RFC822.Error err) {
this._addresses = new Geary.RFC822.MailboxAddresses();
this.is_valid = false;
}
}
}

View file

@ -1131,7 +1131,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
this.reply_to_entry.addresses = referred.reply_to;
}
if (referred.in_reply_to != null)
this.in_reply_to.add_all(referred.in_reply_to.list);
this.in_reply_to.add_all(referred.in_reply_to.get_all());
if (referred.references != null)
this.references = referred.references.to_rfc822_string();
if (referred.subject != null)
@ -1360,7 +1360,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
this.context_type == REPLY_ALL) &&
!this.in_reply_to.is_empty)
email.set_in_reply_to(
new Geary.RFC822.MessageIDList.from_collection(this.in_reply_to)
new Geary.RFC822.MessageIDList(this.in_reply_to)
);
if (!Geary.String.is_empty(this.references)) {
@ -1771,7 +1771,9 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
if (this.draft_manager != null) {
Geary.ComposedEmail draft = yield get_composed_email(null, true);
yield this.draft_manager.update(
yield draft.to_rfc822_message(null, null),
yield new Geary.RFC822.Message.from_composed_email(
draft, null, null
),
null,
null
);

View file

@ -290,7 +290,6 @@ public class ConversationListStore : Gtk.ListStore {
this.config,
conversation,
preview,
this.conversations.base_folder,
this.conversations.base_folder.account.information.sender_mailboxes
);

View file

@ -13,18 +13,14 @@ public class FormattedConversationData : Geary.BaseObject {
bool was_widget_selected;
}
public const int LINE_SPACING = 6;
public const int SPACING = 6;
private const string ME = _("Me");
private const string STYLE_EXAMPLE = "Gg"; // Use both upper and lower case to get max height.
private const int TEXT_LEFT = LINE_SPACING * 2 + IconFactory.UNREAD_ICON_SIZE;
private const int TEXT_LEFT = SPACING * 2 + IconFactory.UNREAD_ICON_SIZE;
private const double DIM_TEXT_AMOUNT = 0.05;
private const double DIM_PREVIEW_TEXT_AMOUNT = 0.25;
private const int FONT_SIZE_DATE = 10;
private const int FONT_SIZE_SUBJECT = 9;
private const int FONT_SIZE_FROM = 11;
private const int FONT_SIZE_PREVIEW = 8;
private class ParticipantDisplay : Geary.BaseObject, Gee.Hashable<ParticipantDisplay> {
public Geary.RFC822.MailboxAddress address;
@ -104,6 +100,7 @@ public class FormattedConversationData : Geary.BaseObject {
public Geary.Email? preview { get; private set; default = null; }
private Application.Configuration config;
private Pango.FontDescription font;
private Geary.App.Conversation? conversation = null;
private Gee.List<Geary.RFC822.MailboxAddress>? account_owner_emails = null;
@ -116,12 +113,15 @@ public class FormattedConversationData : Geary.BaseObject {
public FormattedConversationData(Application.Configuration config,
Geary.App.Conversation conversation,
Geary.Email preview,
Geary.Folder folder,
Gee.List<Geary.RFC822.MailboxAddress> account_owner_emails) {
this.config = config;
this.conversation = conversation;
this.account_owner_emails = account_owner_emails;
use_to = (folder != null) && folder.used_as.is_outgoing();
this.use_to = conversation.base_folder.used_as.is_outgoing();
this.font = Pango.FontDescription.from_string(
this.config.gnome_interface.get_string("font-name")
);
// Load preview-related data.
update_date_string();
@ -141,6 +141,21 @@ public class FormattedConversationData : Geary.BaseObject {
this.conversation.email_flags_changed.connect(clear_participants_cache);
}
// Creates an example message (used internally for styling calculations.)
public FormattedConversationData.create_example(Application.Configuration config) {
this.config = config;
this.is_unread = false;
this.is_flagged = false;
this.date = STYLE_EXAMPLE;
this.subject_html_escaped = STYLE_EXAMPLE;
this.body = STYLE_EXAMPLE + "\n" + STYLE_EXAMPLE;
this.num_emails = 1;
this.font = Pango.FontDescription.from_string(
this.config.gnome_interface.get_string("font-name")
);
}
private void clear_participants_cache(Geary.Email email) {
participants.markup = null;
}
@ -165,17 +180,6 @@ public class FormattedConversationData : Geary.BaseObject {
return true;
}
// Creates an example message (used internally for styling calculations.)
public FormattedConversationData.create_example(Application.Configuration config) {
this.config = config;
this.is_unread = false;
this.is_flagged = false;
this.date = STYLE_EXAMPLE;
this.subject_html_escaped = STYLE_EXAMPLE;
this.body = STYLE_EXAMPLE + "\n" + STYLE_EXAMPLE;
this.num_emails = 1;
}
private uint8 gdk_to_rgb(double gdk) {
return (uint8) (gdk.clamp(0.0, 1.0) * 255.0);
}
@ -294,7 +298,7 @@ public class FormattedConversationData : Geary.BaseObject {
Cairo.Context? ctx, Gtk.CellRendererState flags, bool recalc_dims,
bool hover_select) {
bool display_preview = this.config.display_preview;
int y = LINE_SPACING + (cell_area != null ? cell_area.y : 0);
int y = SPACING + (cell_area != null ? cell_area.y : 0);
bool selected = (flags & Gtk.CellRendererState.SELECTED) != 0;
bool hover = (flags & Gtk.CellRendererState.PRELIT) != 0 || (selected && hover_select);
@ -304,7 +308,7 @@ public class FormattedConversationData : Geary.BaseObject {
// From field.
ink_rect = render_from(widget, cell_area, ctx, y, selected, ink_rect);
y += ink_rect.height + ink_rect.y + LINE_SPACING;
y += ink_rect.height + ink_rect.y + SPACING;
// If we are displaying a preview then the message counter goes on the same line as the
// preview, otherwise it is with the subject.
@ -312,28 +316,28 @@ public class FormattedConversationData : Geary.BaseObject {
// Setup counter badge.
count_badge.count = num_emails;
int counter_width = count_badge.get_width(widget) + LINE_SPACING;
int counter_width = count_badge.get_width(widget) + SPACING;
int counter_x = cell_area != null ? cell_area.width - cell_area.x - counter_width +
(LINE_SPACING / 2) : 0;
(SPACING / 2) : 0;
if (display_preview) {
// Subject field.
render_subject(widget, cell_area, ctx, y, selected);
y += ink_rect.height + ink_rect.y + LINE_SPACING;
y += ink_rect.height + ink_rect.y + (SPACING / 2);
// Number of e-mails field.
count_badge.render(widget, ctx, counter_x, y, selected);
count_badge.render(widget, ctx, counter_x, y + (SPACING / 2), selected);
// Body preview.
ink_rect = render_preview(widget, cell_area, ctx, y, selected, counter_width);
preview_height = ink_rect.height + ink_rect.y + LINE_SPACING;
preview_height = ink_rect.height + ink_rect.y + (int) (SPACING * 1.2);
} else {
// Number of e-mails field.
count_badge.render(widget, ctx, counter_x, y, selected);
// Subject field.
render_subject(widget, cell_area, ctx, y, selected, counter_width);
y += ink_rect.height + ink_rect.y + LINE_SPACING;
y += ink_rect.height + ink_rect.y + (int) (SPACING * 1.2);
}
// Draw separator line.
@ -349,25 +353,25 @@ public class FormattedConversationData : Geary.BaseObject {
FormattedConversationData.preview_height = preview_height;
FormattedConversationData.cell_height = y + preview_height;
} else {
int unread_y = display_preview ? cell_area.y + LINE_SPACING * 2 : cell_area.y +
LINE_SPACING;
int unread_y = display_preview ? cell_area.y + SPACING * 2 : cell_area.y +
SPACING;
// Unread indicator.
if (is_unread || hover) {
Gdk.Pixbuf read_icon = IconFactory.instance.load_symbolic(
is_unread ? "mail-unread-symbolic" : "mail-read-symbolic",
IconFactory.UNREAD_ICON_SIZE, widget.get_style_context());
Gdk.cairo_set_source_pixbuf(ctx, read_icon, cell_area.x + LINE_SPACING, unread_y);
Gdk.cairo_set_source_pixbuf(ctx, read_icon, cell_area.x + SPACING, unread_y);
ctx.paint();
}
// Starred indicator.
if (is_flagged || hover) {
int star_y = cell_area.y + (cell_area.height / 2) + (display_preview ? LINE_SPACING : 0);
int star_y = cell_area.y + (cell_area.height / 2) + (display_preview ? SPACING : 0);
Gdk.Pixbuf starred_icon = IconFactory.instance.load_symbolic(
is_flagged ? "starred-symbolic" : "non-starred-symbolic",
IconFactory.STAR_ICON_SIZE, widget.get_style_context());
Gdk.cairo_set_source_pixbuf(ctx, starred_icon, cell_area.x + LINE_SPACING, star_y);
Gdk.cairo_set_source_pixbuf(ctx, starred_icon, cell_area.x + SPACING, star_y);
ctx.paint();
}
}
@ -375,21 +379,19 @@ public class FormattedConversationData : Geary.BaseObject {
private Pango.Rectangle render_date(Gtk.Widget widget, Gdk.Rectangle? cell_area,
Cairo.Context? ctx, int y, bool selected) {
string date_markup = "<span foreground='%s'>%s</span>".printf(
string date_markup = "<span size='smaller' foreground='%s'>%s</span>".printf(
rgba_to_markup(dim_rgba(get_foreground_rgba(widget, selected), DIM_TEXT_AMOUNT)),
Geary.HTML.escape_markup(date));
Pango.Rectangle? ink_rect;
Pango.Rectangle? logical_rect;
Pango.FontDescription font_date = new Pango.FontDescription();
font_date.set_size(FONT_SIZE_DATE * Pango.SCALE);
Pango.Layout layout_date = widget.create_pango_layout(null);
layout_date.set_font_description(font_date);
layout_date.set_font_description(this.font);
layout_date.set_markup(date_markup, -1);
layout_date.set_alignment(Pango.Alignment.RIGHT);
layout_date.get_pixel_extents(out ink_rect, out logical_rect);
if (ctx != null && cell_area != null) {
ctx.move_to(cell_area.width - cell_area.x - ink_rect.width - ink_rect.x - LINE_SPACING, y);
ctx.move_to(cell_area.width - cell_area.x - ink_rect.width - ink_rect.x - SPACING, y);
Pango.cairo_show_layout(ctx, layout_date);
}
return ink_rect;
@ -399,14 +401,17 @@ public class FormattedConversationData : Geary.BaseObject {
Cairo.Context? ctx, int y, bool selected, Pango.Rectangle ink_rect) {
string from_markup = (conversation != null) ? get_participants_markup(widget, selected) : STYLE_EXAMPLE;
Pango.FontDescription font_from = new Pango.FontDescription();
font_from.set_size(FONT_SIZE_FROM * Pango.SCALE);
Pango.FontDescription font = this.font;
if (is_unread) {
font = font.copy();
font.set_weight(Pango.Weight.BOLD);
}
Pango.Layout layout_from = widget.create_pango_layout(null);
layout_from.set_font_description(font_from);
layout_from.set_font_description(font);
layout_from.set_markup(from_markup, -1);
layout_from.set_ellipsize(Pango.EllipsizeMode.END);
if (ctx != null && cell_area != null) {
layout_from.set_width((cell_area.width - ink_rect.width - ink_rect.x - (LINE_SPACING * 3) -
layout_from.set_width((cell_area.width - ink_rect.width - ink_rect.x - (SPACING * 3) -
TEXT_LEFT)
* Pango.SCALE);
ctx.move_to(cell_area.x + TEXT_LEFT, y);
@ -417,16 +422,17 @@ public class FormattedConversationData : Geary.BaseObject {
private void render_subject(Gtk.Widget widget, Gdk.Rectangle? cell_area, Cairo.Context? ctx,
int y, bool selected, int counter_width = 0) {
string subject_markup = "<span foreground='%s'>%s</span>".printf(
string subject_markup = "<span size='smaller' foreground='%s'>%s</span>".printf(
rgba_to_markup(dim_rgba(get_foreground_rgba(widget, selected), DIM_TEXT_AMOUNT)),
subject_html_escaped);
Pango.FontDescription font_subject = new Pango.FontDescription();
font_subject.set_size(FONT_SIZE_SUBJECT * Pango.SCALE);
if (is_unread)
font_subject.set_weight(Pango.Weight.BOLD);
Pango.FontDescription font = this.font;
if (is_unread) {
font = font.copy();
font.set_weight(Pango.Weight.BOLD);
}
Pango.Layout layout_subject = widget.create_pango_layout(null);
layout_subject.set_font_description(font_subject);
layout_subject.set_font_description(font);
layout_subject.set_markup(subject_markup, -1);
if (cell_area != null)
layout_subject.set_width((cell_area.width - TEXT_LEFT - counter_width) * Pango.SCALE);
@ -440,21 +446,17 @@ public class FormattedConversationData : Geary.BaseObject {
private Pango.Rectangle render_preview(Gtk.Widget widget, Gdk.Rectangle? cell_area,
Cairo.Context? ctx, int y, bool selected, int counter_width = 0) {
double dim = selected ? DIM_TEXT_AMOUNT : DIM_PREVIEW_TEXT_AMOUNT;
string preview_markup = "<span foreground='%s'>%s</span>".printf(
string preview_markup = "<span size='smaller' foreground='%s'>%s</span>".printf(
rgba_to_markup(dim_rgba(get_foreground_rgba(widget, selected), dim)),
Geary.String.is_empty(body) ? "" : Geary.HTML.escape_markup(body));
Pango.FontDescription font_preview = new Pango.FontDescription();
font_preview.set_size(FONT_SIZE_PREVIEW * Pango.SCALE);
Pango.Layout layout_preview = widget.create_pango_layout(null);
layout_preview.set_font_description(font_preview);
layout_preview.set_font_description(this.font);
layout_preview.set_markup(preview_markup, -1);
layout_preview.set_wrap(Pango.WrapMode.WORD);
layout_preview.set_ellipsize(Pango.EllipsizeMode.END);
if (ctx != null && cell_area != null) {
layout_preview.set_width((cell_area.width - TEXT_LEFT - counter_width - LINE_SPACING) * Pango.SCALE);
layout_preview.set_width((cell_area.width - TEXT_LEFT - counter_width - SPACING) * Pango.SCALE);
layout_preview.set_height(preview_height * Pango.SCALE);
ctx.move_to(cell_area.x + TEXT_LEFT, y);

View file

@ -1029,7 +1029,7 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
id,
part.write_to_buffer(Geary.RFC822.Part.EncodingConversion.UTF8)
);
} catch (Geary.RFC822Error err) {
} catch (Geary.RFC822.Error err) {
debug("Failed to get inline buffer: %s", err.message);
return null;
}

View file

@ -13,7 +13,8 @@ shared_module(
vala_args: geary_vala_args,
c_args: plugin_c_args,
install: true,
install_dir: plugin_dest
install_dir: plugin_dest,
install_rpath: client_lib_dir,
)
i18n.merge_file(

View file

@ -13,7 +13,8 @@ shared_module(
vala_args: geary_vala_args,
c_args: plugin_c_args,
install: true,
install_dir: plugin_dest
install_dir: plugin_dest,
install_rpath: client_lib_dir,
)
i18n.merge_file(

View file

@ -13,7 +13,8 @@ shared_module(
vala_args: geary_vala_args,
c_args: plugin_c_args,
install: true,
install_dir: plugin_dest
install_dir: plugin_dest,
install_rpath: client_lib_dir,
)
i18n.merge_file(

View file

@ -32,7 +32,8 @@ if libmessagingmenu_dep.found()
vala_args: geary_vala_args,
c_args: plugin_c_args,
install: true,
install_dir: plugin_dest
install_dir: plugin_dest,
install_rpath: client_lib_dir,
)
i18n.merge_file(

View file

@ -16,7 +16,8 @@ shared_module(
vala_args: geary_vala_args,
c_args: plugin_c_args,
install: true,
install_dir: plugin_dest
install_dir: plugin_dest,
install_rpath: client_lib_dir,
)
i18n.merge_file(

View file

@ -16,7 +16,8 @@ shared_module(
vala_args: geary_vala_args,
c_args: plugin_c_args,
install: true,
install_dir: plugin_dest
install_dir: plugin_dest,
install_rpath: client_lib_dir,
)
i18n.merge_file(

View file

@ -13,7 +13,8 @@ shared_module(
vala_args: geary_vala_args,
c_args: plugin_c_args,
install: true,
install_dir: plugin_dest
install_dir: plugin_dest,
install_rpath: client_lib_dir,
)
i18n.merge_file(

View file

@ -23,7 +23,7 @@ public class SidebarCountCellRenderer : Gtk.CellRenderer {
public override void get_preferred_width(Gtk.Widget widget, out int minimum_size, out int natural_size) {
unread_count.count = counter;
minimum_size = unread_count.get_width(widget) + FormattedConversationData.LINE_SPACING;
minimum_size = unread_count.get_width(widget) + FormattedConversationData.SPACING;
natural_size = minimum_size;
}

View file

@ -70,7 +70,7 @@ public class Geary.ComposedEmail : EmailHeaderSet, BaseObject {
public string img_src_prefix { get; set; default = ""; }
public ComposedEmail(DateTime date, RFC822.MailboxAddresses from) {
this.date = new RFC822.Date.from_date_time(date);
this.date = new RFC822.Date(date);
this.from = from;
}
@ -123,13 +123,6 @@ public class Geary.ComposedEmail : EmailHeaderSet, BaseObject {
return this;
}
public async Geary.RFC822.Message to_rfc822_message(string? message_id,
GLib.Cancellable? cancellable) {
return yield new RFC822.Message.from_composed_email(
this, message_id, cancellable
);
}
/**
* Determines if an IMG SRC value is present in the HTML part.
*
@ -172,7 +165,7 @@ public class Geary.ComposedEmail : EmailHeaderSet, BaseObject {
ret = null;
} else {
RFC822.MessageIDList? ids = list as RFC822.MessageIDList;
if (ids != null && ids.list.size == 0) {
if (ids != null && ids.size == 0) {
ret = null;
}
}

View file

@ -381,7 +381,7 @@ public class Geary.Email : BaseObject, EmailHeaderSet {
public void set_originators(Geary.RFC822.MailboxAddresses? from,
Geary.RFC822.MailboxAddress? sender,
Geary.RFC822.MailboxAddresses? reply_to)
throws RFC822Error {
throws Error {
// XXX Should be throwing an error here if from is empty or
// sender is same as from
this.from = from;
@ -477,7 +477,7 @@ public class Geary.Email : BaseObject, EmailHeaderSet {
* present. If not, {@link EngineError.INCOMPLETE_MESSAGE} is
* thrown.
*/
public Geary.RFC822.Message get_message() throws EngineError, RFC822Error {
public Geary.RFC822.Message get_message() throws EngineError, Error {
if (message != null)
return message;
@ -523,12 +523,12 @@ public class Geary.Email : BaseObject, EmailHeaderSet {
// References list the email trail back to its source
if (references != null)
ancestors.add_all(references.list);
ancestors.add_all(references.get_all());
// RFC822 requires the In-Reply-To Message-ID be prepended to the References list, but
// this ensures that's the case
if (in_reply_to != null)
ancestors.add_all(in_reply_to.list);
ancestors.add_all(in_reply_to.get_all());
return (ancestors.size > 0) ? ancestors : null;
}

View file

@ -71,7 +71,7 @@ private class Geary.ImapDB.Attachment : Geary.Attachment {
this(
result.rowid_for("message_id"),
Mime.ContentType.deserialize(result.nonnull_string_for("mime_type")),
Mime.ContentType.parse(result.nonnull_string_for("mime_type")),
result.string_for("content_id"),
result.string_for("description"),
disposition,

View file

@ -106,7 +106,7 @@ private class Geary.ImapDB.MessageRow {
if (fields.is_all_set(Geary.Email.Field.DATE)) {
try {
email.set_send_date(
!String.is_empty(date) ? new RFC822.Date(date) : null
!String.is_empty(date) ? new RFC822.Date.from_rfc822_string(date) : null
);
} catch (GLib.Error err) {
debug("Error loading message date from db: %s", err.message);
@ -133,7 +133,7 @@ private class Geary.ImapDB.MessageRow {
}
if (fields.is_all_set(Geary.Email.Field.SUBJECT))
email.set_message_subject(new RFC822.Subject.decode(subject ?? ""));
email.set_message_subject(new RFC822.Subject.from_rfc822_string(subject ?? ""));
if (fields.is_all_set(Geary.Email.Field.HEADER))
email.set_message_header(new RFC822.Header(header ?? Memory.EmptyBuffer.instance));
@ -171,7 +171,9 @@ private class Geary.ImapDB.MessageRow {
return null;
}
return new Geary.Imap.EmailProperties(constructed, new RFC822.Size(rfc822_size));
return new Imap.EmailProperties(
constructed, new Imap.RFC822Size(this.rfc822_size)
);
}
public Geary.EmailFlags? get_generic_email_flags() {
@ -189,7 +191,7 @@ private class Geary.ImapDB.MessageRow {
// null if empty
if (email.fields.is_all_set(Geary.Email.Field.DATE)) {
date = (email.date != null) ? email.date.original : null;
date = (email.date != null) ? email.date.to_rfc822_string() : null;
date_time_t = (email.date != null) ? email.date.value.to_unix() : -1;
fields = fields.set(Geary.Email.Field.DATE);
@ -220,7 +222,7 @@ private class Geary.ImapDB.MessageRow {
}
if (email.fields.is_all_set(Geary.Email.Field.SUBJECT)) {
subject = (email.subject != null) ? email.subject.original : null;
subject = (email.subject != null) ? email.subject.to_rfc822_string() : null;
fields = fields.set(Geary.Email.Field.SUBJECT);
}
@ -272,20 +274,22 @@ private class Geary.ImapDB.MessageRow {
return (addrs == null || addrs.size == 0) ? null : addrs.to_rfc822_string();
}
private RFC822.MailboxAddress? unflatten_address(string? str) {
RFC822.MailboxAddress? addr = null;
if (str != null) {
try {
addr = new RFC822.MailboxAddress.from_rfc822_string(str);
} catch (RFC822Error e) {
// oh well
}
}
return addr;
private RFC822.MailboxAddress? unflatten_address(string? str)
throws RFC822.Error {
return (
String.is_empty_or_whitespace(str)
? null
: new RFC822.MailboxAddress.from_rfc822_string(str)
);
}
private RFC822.MailboxAddresses? unflatten_addresses(string? str) {
return String.is_empty(str) ? null : new RFC822.MailboxAddresses.from_rfc822_string(str);
private RFC822.MailboxAddresses? unflatten_addresses(string? str)
throws RFC822.Error {
return (
String.is_empty_or_whitespace(str)
? null
: new RFC822.MailboxAddresses.from_rfc822_string(str)
);
}
}

View file

@ -5,12 +5,14 @@
*/
public class Geary.Imap.EmailProperties : Geary.EmailProperties, Gee.Hashable<Geary.Imap.EmailProperties> {
public InternalDate? internaldate { get; private set; }
public RFC822.Size? rfc822_size { get; private set; }
public RFC822Size? rfc822_size { get; private set; }
public EmailProperties(InternalDate? internaldate, RFC822.Size? rfc822_size) {
public EmailProperties(InternalDate internaldate,
RFC822Size rfc822_size) {
base (internaldate.value, rfc822_size.value);
this.internaldate = internaldate;
this.rfc822_size = rfc822_size;
}

View file

@ -808,7 +808,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
// accumulate these to submit Imap.EmailProperties all at once
InternalDate? internaldate = null;
RFC822.Size? rfc822_size = null;
RFC822Size? rfc822_size = null;
// accumulate these to submit References all at once
RFC822.MessageID? message_id = null;
@ -848,7 +848,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
break;
case FetchDataSpecifier.RFC822_SIZE:
rfc822_size = (RFC822.Size) data;
rfc822_size = (RFC822Size) data;
break;
case FetchDataSpecifier.FLAGS:
@ -901,7 +901,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
RFC822.Date? date = null;
if (!String.is_empty(value)) {
try {
date = new RFC822.Date(value);
date = new RFC822.Date.from_rfc822_string(value);
} catch (GLib.Error err) {
warning(
"Error parsing date from FETCH response: %s",
@ -979,7 +979,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
if (required_but_not_set(Geary.Email.Field.SUBJECT, required_fields, email)) {
string? value = headers.get("Subject");
if (value != null)
email.set_message_subject(new RFC822.Subject.decode(value));
email.set_message_subject(new RFC822.Subject.from_rfc822_string(value));
else
email.set_message_subject(null);
}
@ -1069,7 +1069,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
MailboxSpecifier mailbox = session.get_mailbox_for_path(this.folder.path);
AppendCommand cmd = new AppendCommand(
mailbox, msg_flags, internaldate, message.get_network_buffer(false)
mailbox, msg_flags, internaldate, message.get_rfc822_buffer()
);
Gee.Map<Command, StatusResponse> responses = yield exec_commands_async(

View file

@ -19,15 +19,20 @@
public interface Geary.Imap.MessageData : Geary.MessageData.AbstractMessageData {
}
public class Geary.Imap.RFC822Size : Geary.RFC822.Size, Geary.Imap.MessageData {
public class Geary.Imap.RFC822Size : Geary.MessageData.Int64MessageData, Geary.Imap.MessageData {
public RFC822Size(int64 value) {
base (value);
}
}
public class Geary.Imap.RFC822Header : Geary.RFC822.Header, Geary.Imap.MessageData {
public RFC822Header(Memory.Buffer buffer) {
base (buffer);
public RFC822Header(Memory.Buffer buffer)
throws ImapError {
try {
base(buffer);
} catch (RFC822.Error error) {
throw new ImapError.INVALID(error.message);
}
}
}
@ -42,4 +47,3 @@ public class Geary.Imap.RFC822Full : Geary.RFC822.Full, Geary.Imap.MessageData {
base (buffer);
}
}

View file

@ -148,7 +148,7 @@ public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
Geary.RFC822.Date? sent_date = null;
if (sent != null) {
try {
sent_date = new RFC822.Date(sent.ascii);
sent_date = new RFC822.Date.from_rfc822_string(sent.ascii);
} catch (GLib.Error err) {
debug(
"Error parsing sent date from FETCH envelope: %s",
@ -159,7 +159,7 @@ public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
return new Envelope(
sent_date,
new Geary.RFC822.Subject.decode(subject.ascii),
new Geary.RFC822.Subject.from_rfc822_string(subject.ascii),
parse_addresses(from), parse_addresses(sender), parse_addresses(reply_to),
(to != null) ? parse_addresses(to) : null,
(cc != null) ? parse_addresses(cc) : null,

View file

@ -51,15 +51,13 @@ public class Geary.Mime.ContentParameters : BaseObject {
}
}
internal ContentParameters.from_gmime(GMime.ParamList? gmime_params) {
Gee.Map<string,string> params = new Gee.HashMap<string,string>();
if (gmime_params != null) {
for (int i = 0; i < gmime_params.length(); i++) {
GMime.Param gmime_param = gmime_params.get_parameter_at(i);
params.set(gmime_param.get_name(), gmime_param.get_value());
}
internal ContentParameters.from_gmime(GMime.ParamList gmime) {
var parameters = new Gee.HashMap<string,string>();
for (int i = 0; i < gmime.length(); i++) {
var param = gmime.get_parameter_at(i);
parameters.set(param.name, param.value);
}
this(params);
this(parameters);
}
/**

View file

@ -65,9 +65,9 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
TYPES_TO_EXTENSIONS["image/x-bmp"] = ".bmp";
}
public static ContentType deserialize(string str) throws MimeError {
// perform a little sanity checking here, as it doesn't appear the GMime constructor has
// any error-reporting at all
public static ContentType parse(string str) throws MimeError {
// perform a little sanity checking here, as it doesn't appear
// the GMime constructor has any error-reporting at all
if (String.is_empty(str))
throw new MimeError.PARSE("Empty MIME Content-Type");
@ -113,7 +113,11 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
mime_type = GLib.ContentType.get_mime_type(glib_type);
}
return !Geary.String.is_empty(mime_type) ? deserialize(mime_type) : null;
return (
!Geary.String.is_empty_or_whitespace(mime_type)
? ContentType.parse(mime_type)
: null
);
}

View file

@ -122,7 +122,7 @@ public class Geary.Outbox.Folder :
// save in database ready for SMTP, but without dot-stuffing
Db.Statement stmt = cx.prepare(
"INSERT INTO SmtpOutboxTable (message, ordering) VALUES (?, ?)");
stmt.bind_string_buffer(0, rfc822.get_network_buffer(false));
stmt.bind_string_buffer(0, rfc822.get_rfc822_buffer());
stmt.bind_int64(1, ordering);
int64 new_id = stmt.exec_insert(cancellable);

View file

@ -4,9 +4,11 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
public errordomain Geary.RFC822Error {
/**
* An error that is thrown when manipulating RFC 822 objects and data.
*/
public errordomain Geary.RFC822.Error {
INVALID,
NOT_FOUND,
FAILED
}

View file

@ -17,9 +17,10 @@
* See [[https://tools.ietf.org/html/rfc5322#section-3.4]]
*/
public class Geary.RFC822.MailboxAddress :
Geary.MessageData.AbstractMessageData,
Geary.MessageData.SearchableMessageData,
Gee.Hashable<MailboxAddress>,
BaseObject {
DecodedMessageData {
private static unichar[] ATEXT = {
'!', '#', '$', '%', '&', '\'', '*', '+', '-',
@ -124,9 +125,11 @@ public class Geary.RFC822.MailboxAddress :
// _internet_address_decode_name() function.
// see if a broken mailer has sent raw 8-bit information
string text = GMime.utils_text_is_8bit(part.data)
? part : GMime.utils_decode_8bit(Geary.RFC822.get_parser_options(),
part.data);
string text = (
!GMime.utils_text_is_8bit(part.data)
? part
: GMime.utils_decode_8bit(get_parser_options(), part.data)
);
// unquote the string then decode the text
GMime.utils_unquote_string(text);
@ -228,29 +231,33 @@ public class Geary.RFC822.MailboxAddress :
this.address = "%s@%s".printf(mailbox, domain);
}
public MailboxAddress.from_rfc822_string(string rfc822) throws RFC822Error {
public MailboxAddress.from_rfc822_string(string rfc822) throws Error {
GMime.InternetAddressList addrlist = GMime.InternetAddressList.parse(
Geary.RFC822.get_parser_options(),
rfc822
);
if (addrlist == null)
return;
int length = addrlist.length();
for (int ctr = 0; ctr < length; ctr++) {
GMime.InternetAddress? addr = addrlist.get_address(ctr);
// TODO: Handle group lists
GMime.InternetAddressMailbox? mbox_addr = addr as GMime.InternetAddressMailbox;
if (mbox_addr != null) {
this.gmime(mbox_addr);
return;
}
if (addrlist == null) {
throw new Error.INVALID("Not a RFC822 mailbox address: %s", rfc822);
}
throw new RFC822Error.INVALID("Could not parse RFC822 address: %s", rfc822);
if (addrlist.length() != 1) {
throw new Error.INVALID(
"Not a single RFC822 mailbox address: %s", rfc822
);
}
GMime.InternetAddress? addr = addrlist.get_address(0);
// TODO: Handle group lists
var mbox_addr = addr as GMime.InternetAddressMailbox;
if (mbox_addr == null) {
throw new Error.INVALID(
"Group lists not currently supported: %s", rfc822
);
}
this.from_gmime(mbox_addr);
}
public MailboxAddress.gmime(GMime.InternetAddressMailbox mailbox) {
public MailboxAddress.from_gmime(GMime.InternetAddressMailbox mailbox) {
// GMime strips source route for us, so the address part
// should only ever contain a single '@'
string? name = mailbox.get_name();
@ -469,7 +476,7 @@ public class Geary.RFC822.MailboxAddress :
GMime.utils_header_encode_phrase(
Geary.RFC822.get_format_options(),
this.name,
"iso-8859-1"
null
),
to_rfc822_address()
)
@ -543,7 +550,7 @@ public class Geary.RFC822.MailboxAddress :
*
* @see to_rfc822_string
*/
public string to_string() {
public override string to_string() {
return to_rfc822_string();
}

View file

@ -1,8 +1,9 @@
/*
* Copyright 2016 Software Freedom Conservancy Inc.
* Copyright © 2016 Software Freedom Conservancy Inc.
* Copyright © 2020 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
* (version 2.1 or later). See the COPYING file in this distribution.
*/
/**
@ -16,7 +17,8 @@
public class Geary.RFC822.MailboxAddresses :
Geary.MessageData.AbstractMessageData,
Geary.MessageData.SearchableMessageData,
Geary.RFC822.MessageData, Gee.Hashable<MailboxAddresses> {
Gee.Hashable<MailboxAddresses>,
DecodedMessageData {
/**
@ -52,6 +54,7 @@ public class Geary.RFC822.MailboxAddresses :
/** Signature for "to_string" implementation for {@link list_to_string}. */
private delegate string ListToStringDelegate(MailboxAddress address);
/** Returns the number of addresses in this list. */
public int size {
get { return this.addrs.size; }
@ -78,33 +81,40 @@ public class Geary.RFC822.MailboxAddresses :
this.addrs.add(addr);
}
public MailboxAddresses.from_rfc822_string(string rfc822) {
GMime.InternetAddressList addrlist = GMime.InternetAddressList.parse(
Geary.RFC822.get_parser_options(),
rfc822
);
if (addrlist == null)
return;
public MailboxAddresses.from_rfc822_string(string rfc822)
throws Error {
var list = GMime.InternetAddressList.parse(null, rfc822);
if (list == null) {
throw new Error.INVALID("Not a RFC822 mailbox address list");
}
this.from_gmime(list);
}
int length = addrlist.length();
for (int ctr = 0; ctr < length; ctr++) {
GMime.InternetAddress? addr = addrlist.get_address(ctr);
GMime.InternetAddressMailbox? mbox_addr = addr as GMime.InternetAddressMailbox;
public MailboxAddresses.from_gmime(GMime.InternetAddressList list)
throws Error {
int length = list.length();
if (length == 0) {
throw new Error.INVALID("No addresses in list");
}
for (int i = 0; i < length; i++) {
var addr = list.get_address(i);
var mbox_addr = addr as GMime.InternetAddressMailbox;
if (mbox_addr != null) {
this.addrs.add(new MailboxAddress.gmime(mbox_addr));
this.addrs.add(new MailboxAddress.from_gmime(mbox_addr));
} else {
// XXX this is pretty bad - we just flatten the
// group's addresses into this list, merging lists and
// losing the group names.
GMime.InternetAddressGroup? mbox_group = addr as GMime.InternetAddressGroup;
var mbox_group = addr as GMime.InternetAddressGroup;
if (mbox_group != null) {
GMime.InternetAddressList group_list = mbox_group.get_members();
for (int i = 0; i < group_list.length(); i++) {
GMime.InternetAddressMailbox? group_addr =
addrlist.get_address(i) as GMime.InternetAddressMailbox;
var group_list = mbox_group.get_members();
for (int j = 0; j < group_list.length(); j++) {
var group_addr =
group_list.get_address(j) as GMime.InternetAddressMailbox;
if (group_addr != null) {
this.addrs.add(new MailboxAddress.gmime(group_addr));
this.addrs.add(
new MailboxAddress.from_gmime(group_addr)
);
}
}
}
@ -112,16 +122,19 @@ public class Geary.RFC822.MailboxAddresses :
}
}
/** Returns the address at the given index, if it exists. */
public new MailboxAddress? get(int index) {
return addrs.get(index);
return this.addrs.get(index);
}
/** Returns a read-only iterator of the addresses in this list. */
public Gee.Iterator<MailboxAddress> iterator() {
return addrs.iterator();
return this.addrs.read_only_view.iterator();
}
/** Returns a read-only collection of the addresses in this list. */
public Gee.List<MailboxAddress> get_all() {
return addrs.read_only_view;
return this.addrs.read_only_view;
}
public bool contains_normalized(string address) {

View file

@ -1,66 +1,103 @@
/* Copyright 2016 Software Freedom Conservancy Inc.
/*
* Copyright © 2016 Software Freedom Conservancy Inc.
* Copyright © 2020 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
/**
* RFC822.MessageData represents a base class for all the various elements that may be present in
* an RFC822 message header. Note that some common elements (such as MailAccount) are not
* MessageData because they exist in an RFC822 header in list (i.e. multiple email addresses) form.
*/
public interface Geary.RFC822.MessageData : Geary.MessageData.AbstractMessageData {
}
/**
* An RFC822 Message-ID.
* A base interface for objects that represent decoded RFC822 headers.
*
* MessageID will normalize all strings so that they begin and end with the proper brackets ("<" and
* ">").
* The value of these objects is the decoded form of the header
* data. Encoded forms can be obtained via {@link to_rfc822_string}.
*/
public class Geary.RFC822.MessageID : Geary.MessageData.StringMessageData, Geary.RFC822.MessageData {
public MessageID(string value) {
string? normalized = normalize(value);
base (normalized ?? value);
}
public interface Geary.RFC822.DecodedMessageData :
Geary.MessageData.AbstractMessageData {
// Adds brackets if required, null if no change required
private static string? normalize(string value) {
bool needs_prefix = !value.has_prefix("<");
bool needs_suffix = !value.has_suffix(">");
if (!needs_prefix && !needs_suffix)
return null;
/** Returns an RFC822-safe string representation of the data. */
public abstract string to_rfc822_string();
return "%s%s%s".printf(needs_prefix ? "<" : "", value, needs_suffix ? ">" : "");
}
}
/**
* A Message-ID list stores its IDs from earliest to latest.
* A base interface for objects that represent encoded RFC822 header data.
*
* The value of these objects is the RFC822 encoded form of the header
* data. Decoded forms can be obtained via means specific to
* implementations of this interface.
*/
public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData, Geary.RFC822.MessageData {
public Gee.List<MessageID> list { get; private set; }
public interface Geary.RFC822.EncodedMessageData :
Geary.MessageData.BlockMessageData {
public MessageIDList() {
list = new Gee.ArrayList<MessageID>();
}
/**
* A RFC822 Message-ID.
*
* The decoded form of the id is the `addr-spec` portion, that is,
* without the leading `<` and tailing `>`.
*/
public class Geary.RFC822.MessageID :
Geary.MessageData.StringMessageData, DecodedMessageData {
private string rfc822;
public MessageID(string value) {
base(value);
}
public MessageIDList.from_collection(Gee.Collection<MessageID> collection) {
this ();
public MessageID.from_rfc822_string(string rfc822) {
base(GMime.utils_decode_message_id(rfc822));
this.rfc822 = rfc822;
}
foreach(MessageID msg_id in collection)
this.list.add(msg_id);
/**
* Returns the {@link Date} in RFC 822 format.
*/
public string to_rfc822_string() {
if (this.rfc822 == null) {
this.rfc822 = "<%s>".printf(this.value);
}
return this.rfc822;
}
}
/**
* A immutable list of RFC822 Message-ID values.
*/
public class Geary.RFC822.MessageIDList :
Geary.MessageData.AbstractMessageData,
DecodedMessageData {
/** Returns the number of ids in this list. */
public int size {
get { return this.list.size; }
}
/** Determines if there are no ids in the list. */
public bool is_empty {
get { return this.list.is_empty; }
}
private Gee.List<MessageID> list = new Gee.ArrayList<MessageID>();
public MessageIDList(Gee.Collection<MessageID>? collection = null) {
if (collection != null) {
this.list.add_all(collection);
}
}
public MessageIDList.single(MessageID msg_id) {
this ();
this();
list.add(msg_id);
}
public MessageIDList.from_rfc822_string(string value) {
this ();
this();
// Have seen some mailers use commas between Message-IDs and whitespace inside Message-IDs,
// meaning that the standard whitespace tokenizer is not sufficient. The only guarantee
@ -143,12 +180,26 @@ public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData,
// from any non-empty string, an empty Message-ID (i.e. "<>") won't.
}
/** Returns the id at the given index, if it exists. */
public new MessageID? get(int index) {
return this.list.get(index);
}
/** Returns a read-only iterator of the ids in this list. */
public Gee.Iterator<MessageID> iterator() {
return this.list.read_only_view.iterator();
}
/** Returns a read-only collection of the ids in this list. */
public Gee.List<MessageID> get_all() {
return this.list.read_only_view;
}
/**
* Returns a new list with the given messages ids appended to this list's.
*/
public MessageIDList append(MessageIDList others) {
MessageIDList new_ids = new MessageIDList();
new_ids.list.add_all(this.list);
MessageIDList new_ids = new MessageIDList(this.list);
new_ids.list.add_all(others.list);
return new_ids;
}
@ -157,87 +208,97 @@ public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData,
return "MessageIDList (%d)".printf(list.size);
}
public virtual string to_rfc822_string() {
public string to_rfc822_string() {
string[] strings = new string[list.size];
for(int i = 0; i < list.size; ++i)
strings[i] = list[i].value;
for(int i = 0; i < this.list.size; ++i)
strings[i] = this.list[i].to_rfc822_string();
return string.joinv(" ", strings);
}
}
public class Geary.RFC822.Date : Geary.RFC822.MessageData, Geary.MessageData.AbstractMessageData,
Gee.Hashable<Geary.RFC822.Date> {
public class Geary.RFC822.Date :
Geary.MessageData.AbstractMessageData,
Gee.Hashable<Geary.RFC822.Date>,
DecodedMessageData {
public string? original { get; private set; }
public DateTime value { get; private set; }
public Date(string rfc822) throws ImapError {
DateTime? value = GMime.utils_header_decode_date(rfc822);
if (value == null) {
throw new ImapError.PARSE_ERROR(
"Unable to parse \"%s\": Outside supported range", rfc822
);
}
this.value = value;
this.original = rfc822;
public GLib.DateTime value { get; private set; }
private string? rfc822;
public Date(GLib.DateTime datetime) {
this.value = datetime;
this.rfc822 = null;
}
public Date.from_date_time(DateTime datetime) {
this.original = null;
this.value = datetime;
public Date.from_rfc822_string(string rfc822) throws Error {
var date = GMime.utils_header_decode_date(rfc822);
if (date == null) {
throw new Error.INVALID("Not ISO-8601 date: %s", rfc822);
}
this.rfc822 = rfc822;
this.value = date;
}
/**
* Returns the {@link Date} in RFC 822 format.
*/
public string to_rfc822_string() {
return GMime.utils_header_format_date(this.value);
}
/**
* Returns {@link Date} for transmission.
*
* @see to_rfc822_string
*/
public virtual string serialize() {
return to_rfc822_string();
if (this.rfc822 == null) {
this.rfc822 = GMime.utils_header_format_date(this.value);
}
return this.rfc822;
}
public virtual bool equal_to(Geary.RFC822.Date other) {
return (this != other) ? value.equal(other.value) : true;
return this == other || this.value.equal(other.value);
}
public virtual uint hash() {
return value.hash();
return this.value.hash();
}
public override string to_string() {
return original ?? value.to_string();
return this.value.to_string();
}
}
public class Geary.RFC822.Size : Geary.MessageData.Int64MessageData, Geary.RFC822.MessageData {
public Size(int64 value) {
base (value);
}
}
public class Geary.RFC822.Subject :
Geary.MessageData.StringMessageData,
Geary.MessageData.SearchableMessageData,
DecodedMessageData {
public class Geary.RFC822.Subject : Geary.MessageData.StringMessageData,
Geary.MessageData.SearchableMessageData, Geary.RFC822.MessageData {
public const string REPLY_PREFACE = "Re:";
public const string FORWARD_PREFACE = "Fwd:";
public string original { get; private set; }
private string rfc822;
public Subject(string value) {
base (value);
original = value;
base(value);
this.rfc822 = null;
}
public Subject.decode(string value) {
base (GMime.utils_header_decode_text(Geary.RFC822.get_parser_options(), value));
original = value;
public Subject.from_rfc822_string(string rfc822) {
base(GMime.utils_header_decode_text(get_parser_options(), rfc822));
this.rfc822 = rfc822;
}
/**
* Returns the subject line encoded for an RFC 822 message.
*/
public string to_rfc822_string() {
if (this.rfc822 == null) {
this.rfc822 = GMime.utils_header_encode_text(
get_format_options(), this.value, null
);
}
return this.rfc822;
}
public bool is_reply() {
@ -296,67 +357,75 @@ public class Geary.RFC822.Subject : Geary.MessageData.StringMessageData,
public string to_searchable_string() {
return value;
}
}
public class Geary.RFC822.Header : Geary.MessageData.BlockMessageData, Geary.RFC822.MessageData {
public class Geary.RFC822.Header :
Geary.MessageData.BlockMessageData, EncodedMessageData {
private GMime.Message? message = null;
private string[]? names = null;
public Header(Memory.Buffer buffer) {
public Header(Memory.Buffer buffer) throws Error {
base("RFC822.Header", buffer);
}
private unowned GMime.HeaderList get_headers() throws RFC822Error {
if (message != null)
return message.get_header_list();
GMime.Parser parser = new GMime.Parser.with_stream(Utils.create_stream_mem(buffer));
var parser = new GMime.Parser.with_stream(
Utils.create_stream_mem(buffer)
);
parser.set_respect_content_length(false);
parser.set_format(MESSAGE);
message = parser.construct_message(Geary.RFC822.get_parser_options());
if (message == null)
throw new RFC822Error.INVALID("Unable to parse RFC 822 headers");
return message.get_header_list();
this.message = parser.construct_message(null);
if (this.message == null) {
throw new Error.INVALID("Unable to parse RFC 822 headers");
}
}
public string? get_header(string name) throws RFC822Error {
GMime.Header header = get_headers().get_header(name);
if (header != null)
// We should not parse the raw-value here, but use GMime's parsing
// functionality instead.
// See: https://gitlab.gnome.org/GNOME/geary/merge_requests/382#note_669699
return GMime.utils_header_unfold(header.get_raw_value());
else
return null;
public string? get_header(string name) {
string? value = null;
var header = this.message.get_header_list().get_header(name);
if (header != null) {
value = header.get_value();
}
return value;
}
public string[] get_header_names() throws RFC822Error {
public string[] get_header_names() {
if (this.names == null) {
this.names = new string[0];
GMime.HeaderList headers = get_headers();
for (int i = 0; i < headers.get_count(); i++) {
names += headers.get_header_at(i).get_name();
GMime.HeaderList headers = this.message.get_header_list();
var names = new string[headers.get_count()];
for (int i = 0; i < names.length; i++) {
names[i] = headers.get_header_at(i).get_name();
}
this.names = names;
}
return this.names;
}
}
public class Geary.RFC822.Text : Geary.MessageData.BlockMessageData, Geary.RFC822.MessageData {
public class Geary.RFC822.Text :
Geary.MessageData.BlockMessageData, EncodedMessageData {
public Text(Memory.Buffer buffer) {
base ("RFC822.Text", buffer);
base("RFC822.Text", buffer);
}
}
public class Geary.RFC822.Full : Geary.MessageData.BlockMessageData, Geary.RFC822.MessageData {
public class Geary.RFC822.Full :
Geary.MessageData.BlockMessageData, EncodedMessageData {
public Full(Memory.Buffer buffer) {
base ("RFC822.Full", buffer);
base("RFC822.Full", buffer);
}
}
// Used for decoding preview text.
/** Represents text providing a preview of an email's body. */
public class Geary.RFC822.PreviewText : Geary.RFC822.Text {
public PreviewText(Memory.Buffer _buffer) {
base (_buffer);
}
@ -391,7 +460,7 @@ public class Geary.RFC822.PreviewText : Geary.RFC822.Text {
preview_buffer.get_valid_utf8(),
is_html ? TextFormat.HTML : TextFormat.PLAIN
);
} catch (RFC822Error err) {
} catch (Error err) {
debug("Failed to parse preview body: %s", err.message);
}
}
@ -403,4 +472,5 @@ public class Geary.RFC822.PreviewText : Geary.RFC822.Text {
public PreviewText.from_string(string preview) {
base (new Geary.Memory.StringBuffer(preview));
}
}

View file

@ -6,6 +6,7 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
/**
* An RFC-822 style email message.
*
@ -15,6 +16,7 @@
*/
public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
/**
* Callback for including non-text MIME entities in message bodies.
*
@ -29,22 +31,45 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
*/
public delegate string? InlinePartReplacer(Part part);
private const string HEADER_IN_REPLY_TO = "In-Reply-To";
private const string HEADER_REFERENCES = "References";
private const string HEADER_MAILER = "X-Mailer";
private const string HEADER_BCC = "Bcc";
/** Options to use when serialising a message in RFC 822 format. */
[Flags]
public enum RFC822FormatOptions {
/** Format for RFC 822 in general. */
NONE,
/**
* The message should be serialised for transmission via SMTP.
*
* SMTP imposes both operational and data-format requirements
* on RFC 822 style messages. In particular, BCC headers
* should not be included since they will expose BCC
* recipients, and lines must be dot-stuffed so as to avoid
* terminating the message early if a line starting with a `.`
* is encountered.
*
* See [[http://tools.ietf.org/html/rfc5321#section-4.5.2]]
*/
SMTP_FORMAT;
}
// Internal note: If a header field is added here, it *must* be
// set in stock_from_gmime().
/** {@inheritDoc} */
/** {@inheritDoc} */
public RFC822.MailboxAddress? sender { get; protected set; default = null; }
// set in Message.from_gmime_message(), below.
/** {@inheritDoc} */
public RFC822.MailboxAddresses? from { get; protected set; default = null; }
/** {@inheritDoc} */
public RFC822.MailboxAddress? sender { get; protected set; default = null; }
/** {@inheritDoc} */
public RFC822.MailboxAddresses? to { get; protected set; default = null; }
@ -85,50 +110,112 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
private size_t? body_offset = null;
public Message(Full full) throws RFC822Error {
GMime.Parser parser = new GMime.Parser.with_stream(Utils.create_stream_mem(full.buffer));
public Message(Full full) throws Error {
GMime.Parser parser = new GMime.Parser.with_stream(
Utils.create_stream_mem(full.buffer)
);
var message = parser.construct_message(get_parser_options());
if (message == null) {
throw new Error.INVALID("Unable to parse RFC 822 message");
}
message = parser.construct_message(Geary.RFC822.get_parser_options());
if (message == null)
throw new RFC822Error.INVALID("Unable to parse RFC 822 message");
this.from_gmime_message(message);
// See the declaration of these fields for why we do this.
body_buffer = full.buffer;
body_offset = (size_t) parser.get_headers_end();
stock_from_gmime();
this.body_buffer = full.buffer;
this.body_offset = (size_t) parser.get_headers_end();
}
public Message.from_gmime_message(GMime.Message message) {
public Message.from_gmime_message(GMime.Message message)
throws Error {
this.message = message;
stock_from_gmime();
this.from = to_addresses(message.get_from());
this.to = to_addresses(message.get_to());
this.cc = to_addresses(message.get_cc());
this.bcc = to_addresses(message.get_bcc());
this.reply_to = to_addresses(message.get_reply_to());
var sender = (
message.get_sender().get_address(0) as GMime.InternetAddressMailbox
);
if (sender != null) {
this.sender = new MailboxAddress.from_gmime(sender);
}
var subject = message.get_subject();
if (subject != null) {
this.subject = new Subject(subject);
}
// Use a pointer here to work around GNOME/vala#986
GLib.DateTime* date = message.get_date();
if (date != null) {
this.date = new Date(date);
}
var message_id = message.get_message_id();
if (message_id != null) {
this.message_id = new MessageID(message_id);
}
// Since these headers may be specified multiple times, we
// need to iterate over all of them to find them.
var headers = message.get_header_list();
for (int i = 0; i < headers.get_count(); i++) {
var header = headers.get_header_at(i);
switch (header.get_name().down()) {
case "in-reply-to":
this.in_reply_to = append_message_id(
this.in_reply_to, header.get_raw_value()
);
break;
case "references":
this.references = append_message_id(
this.references, header.get_raw_value()
);
break;
default:
break;
}
}
this.mailer = message.get_header("X-Mailer");
}
public Message.from_buffer(Memory.Buffer full_email) throws RFC822Error {
public Message.from_buffer(Memory.Buffer full_email)
throws Error {
this(new Geary.RFC822.Full(full_email));
}
public Message.from_parts(Header header, Text body) throws RFC822Error {
public Message.from_parts(Header header, Text body)
throws Error {
GMime.StreamCat stream_cat = new GMime.StreamCat();
stream_cat.add_source(new GMime.StreamMem.with_buffer(header.buffer.get_bytes().get_data()));
stream_cat.add_source(new GMime.StreamMem.with_buffer(body.buffer.get_bytes().get_data()));
GMime.Parser parser = new GMime.Parser.with_stream(stream_cat);
message = parser.construct_message(Geary.RFC822.get_parser_options());
if (message == null)
throw new RFC822Error.INVALID("Unable to parse RFC 822 message");
var message = parser.construct_message(Geary.RFC822.get_parser_options());
if (message == null) {
throw new Error.INVALID("Unable to parse RFC 822 message");
}
body_buffer = body.buffer;
body_offset = 0;
this.from_gmime_message(message);
stock_from_gmime();
// See the declaration of these fields for why we do this.
this.body_buffer = body.buffer;
this.body_offset = 0;
}
public async Message.from_composed_email(Geary.ComposedEmail email,
string? message_id,
GLib.Cancellable? cancellable) {
GLib.Cancellable? cancellable)
throws Error {
this.message = new GMime.Message(true);
//
// Required headers
this.from = email.from;
@ -379,22 +466,6 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
this.message.set_mime_part(main_part);
}
// Makes a copy of the given message without the BCC fields. This is used for sending the email
// without sending the BCC headers to all recipients.
public Message.without_bcc(Message email) throws GLib.Error {
// GMime doesn't make it easy to get a copy of the body of a message. It's easy to
// make a new message and add in all the headers, but calling set_mime_part() with
// the existing one's get_mime_part() result yields a double Content-Type header in
// the *original* message. Clearly the objects aren't meant to be used like that.
// Barring any better way to clone a message, which I couldn't find by looking at
// the docs, we just dump out the old message to a buffer and read it back in to
// create the new object. Kinda sucks, but our hands are tied.
this.from_buffer(email.message_to_memory_buffer(false, false));
this.message.remove_header(HEADER_BCC);
this.bcc = null;
}
private GMime.Object? coalesce_related(Gee.List<GMime.Object> parts,
string type) {
GMime.Object? part = coalesce_parts(parts, "related");
@ -449,15 +520,14 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
string basename,
Geary.Mime.DispositionType disposition,
GLib.Cancellable cancellable)
throws Error {
throws GLib.Error {
Mime.ContentType? mime_type = Mime.ContentType.guess_type(
basename,
buffer
);
if (mime_type == null) {
throw new RFC822Error.INVALID(
throw new Error.INVALID(
_("Could not determine mime type for “%s”.").printf(basename)
);
}
@ -468,7 +538,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
);
if (content_type == null) {
throw new RFC822Error.INVALID(
throw new Error.INVALID(
_("Could not determine content type for mime type “%s” on “%s”.").printf(mime_type.to_string(), basename)
);
}
@ -490,7 +560,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
GMime.Part part,
GMime.ContentType content_type,
GLib.Cancellable cancellable)
throws Error {
throws GLib.Error {
// Text parts should be scanned fully to determine best
// (i.e. most compact) transport encoding to use, but
@ -526,7 +596,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
* out. See the various constructors for details. (Otherwise, we don't have a way
* to get the body part directly, because of GMime's shortcomings.)
*/
public Geary.Email get_email(Geary.EmailIdentifier id) throws Error {
public Geary.Email get_email(Geary.EmailIdentifier id) throws GLib.Error {
assert(body_buffer != null);
assert(body_offset != null);
@ -589,22 +659,21 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
}
/**
* Returns the {@link Message} as a {@link Memory.Buffer} suitable for in-memory use (i.e.
* with native linefeed characters).
* Serialises the message using native (i.e. LF) line endings.
*/
public Memory.Buffer get_native_buffer() throws RFC822Error {
return message_to_memory_buffer(false, false);
public Memory.Buffer get_native_buffer() throws Error {
return message_to_memory_buffer(false, NONE);
}
/**
* Returns the {@link Message} as a {@link Memory.Buffer} suitable for transmission or
* storage (i.e. using protocol-specific linefeeds).
* Serialises the message using RFC 822 (i.e. CRLF) line endings.
*
* The buffer can also be dot-stuffed if required. See
* [[http://tools.ietf.org/html/rfc2821#section-4.5.2]]
* Returns the message as a memory buffer suitable for network
* transmission and interoperability with other RFC 822 consumers.
*/
public Memory.Buffer get_network_buffer(bool dotstuffed) throws RFC822Error {
return message_to_memory_buffer(true, dotstuffed);
public Memory.Buffer get_rfc822_buffer(RFC822FormatOptions options = NONE)
throws Error {
return message_to_memory_buffer(true, options);
}
/**
@ -679,7 +748,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
bool to_html,
InlinePartReplacer? replacer,
ref string? body)
throws RFC822Error {
throws Error {
Part part = new Part(node);
Mime.ContentType content_type = part.content_type;
@ -746,11 +815,11 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
* something that front-facing methods want to return.
*/
private string? internal_get_body(string text_subtype, bool to_html, InlinePartReplacer? replacer)
throws RFC822Error {
throws Error {
string? body = null;
if (!construct_body_from_mime_parts(message.get_mime_part(), Mime.MultipartSubtype.UNSPECIFIED,
text_subtype, to_html, replacer, ref body)) {
throw new RFC822Error.NOT_FOUND("Could not find any \"text/%s\" parts", text_subtype);
throw new Error.NOT_FOUND("Could not find any \"text/%s\" parts", text_subtype);
}
return body;
@ -767,9 +836,9 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
* lieu of the MIME part into the final document. All other MIME
* parts are ignored.
*
* @throws RFC822Error.NOT_FOUND if an HTML body is not present.
* @throws Error.NOT_FOUND if an HTML body is not present.
*/
public string? get_html_body(InlinePartReplacer? replacer) throws RFC822Error {
public string? get_html_body(InlinePartReplacer? replacer) throws Error {
return internal_get_body("html", false, replacer);
}
@ -789,9 +858,10 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
* output is not converted; it's up to the caller to know what
* format to return when invoked.
*
* @throws RFC822Error.NOT_FOUND if a plaintext body is not present.
* @throws Error.NOT_FOUND if a plaintext body is not present.
*/
public string? get_plain_body(bool convert_to_html, InlinePartReplacer? replacer) throws RFC822Error {
public string? get_plain_body(bool convert_to_html, InlinePartReplacer? replacer)
throws Error {
return internal_get_body("plain", convert_to_html, replacer);
}
@ -802,7 +872,8 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
* disabled by passing false in include_sub_messages). Note that values
* that come out of this function are persisted.
*/
public string? get_searchable_body(bool include_sub_messages = true) {
public string? get_searchable_body(bool include_sub_messages = true)
throws Error {
string? body = null;
bool html = false;
try {
@ -874,90 +945,18 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
// UNSPECIFIED disposition means "return all Mime parts"
internal Gee.List<Part> get_attachments(
Mime.DispositionType disposition = Mime.DispositionType.UNSPECIFIED) throws RFC822Error {
Mime.DispositionType disposition = Mime.DispositionType.UNSPECIFIED)
throws Error {
Gee.List<Part> attachments = new Gee.LinkedList<Part>();
get_attachments_recursively(attachments, message.get_mime_part(), disposition);
return attachments;
}
private void stock_from_gmime() {
GMime.HeaderList headers = this.message.get_header_list();
for (int i = 0; i < headers.get_count(); i++) {
GMime.Header header = headers.get_header_at(i);
string name = header.get_name();
// We should not parse the raw-value here, but use GMime's parsing
// functionality instead.
// See: https://gitlab.gnome.org/GNOME/geary/merge_requests/382#note_669699
string value = GMime.utils_header_unfold(header.get_raw_value());
switch (name.down()) {
case "from":
this.from = append_address(this.from, value);
break;
case "sender":
try {
this.sender = new RFC822.MailboxAddress.from_rfc822_string(value);
} catch (Error err) {
debug("Could parse subject: %s", err.message);
}
break;
case "reply-to":
this.reply_to = append_address(this.reply_to, value);
break;
case "to":
this.to = append_address(this.to, value);
break;
case "cc":
this.cc = append_address(this.cc, value);
break;
case "bcc":
this.bcc = append_address(this.bcc, value);
break;
case "subject":
this.subject = new RFC822.Subject.decode(value);
break;
case "date":
try {
this.date = new Geary.RFC822.Date(value);
} catch (Error err) {
debug("Could not parse date: %s", err.message);
}
break;
case "message-id":
this.message_id = new MessageID(value);
break;
case "in-reply-to":
this.in_reply_to = append_message_id(this.in_reply_to, value);
break;
case "references":
this.references = append_message_id(this.references, value);
break;
case "x-mailer":
this.mailer = GMime.utils_header_decode_text(Geary.RFC822.get_parser_options(), value);
break;
default:
// do nothing
break;
}
};
}
private MailboxAddresses append_address(MailboxAddresses? existing,
string header_value) {
MailboxAddresses addresses = new MailboxAddresses.from_rfc822_string(header_value);
if (existing != null) {
addresses = existing.append(addresses);
private MailboxAddresses? to_addresses(GMime.InternetAddressList? list)
throws Error {
MailboxAddresses? addresses = null;
if (list != null && list.length() > 0) {
addresses = new MailboxAddresses.from_gmime(list);
}
return addresses;
}
@ -974,8 +973,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
private void get_attachments_recursively(Gee.List<Part> attachments,
GMime.Object root,
Mime.DispositionType requested_disposition)
throws RFC822Error {
throws Error {
if (root is GMime.Multipart) {
GMime.Multipart multipart = (GMime.Multipart) root;
int count = multipart.get_count();
@ -1063,13 +1061,16 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
}
#endif
public Gee.List<Geary.RFC822.Message> get_sub_messages() {
public Gee.List<Geary.RFC822.Message> get_sub_messages()
throws Error {
Gee.List<Geary.RFC822.Message> messages = new Gee.ArrayList<Geary.RFC822.Message>();
find_sub_messages(messages, message.get_mime_part());
return messages;
}
private void find_sub_messages(Gee.List<Geary.RFC822.Message> messages, GMime.Object root) {
private void find_sub_messages(Gee.List<Message> messages,
GMime.Object root)
throws Error {
// If this is a multipart container, check each of its children.
GMime.Multipart? multipart = root as GMime.Multipart;
if (multipart != null) {
@ -1084,36 +1085,53 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
if (messagepart != null) {
GMime.Message sub_message = messagepart.get_message();
if (sub_message != null) {
messages.add(new Geary.RFC822.Message.from_gmime_message(sub_message));
messages.add(new Message.from_gmime_message(sub_message));
} else {
warning("Corrupt message, possibly bug 769697");
}
}
}
private Memory.Buffer message_to_memory_buffer(bool encoded, bool dotstuffed) throws RFC822Error {
private Memory.Buffer message_to_memory_buffer(bool encode_lf,
RFC822FormatOptions options)
throws Error {
ByteArray byte_array = new ByteArray();
GMime.StreamMem stream = new GMime.StreamMem.with_byte_array(byte_array);
stream.set_owner(false);
GMime.StreamFilter stream_filter = new GMime.StreamFilter(stream);
if (encoded) {
if (encode_lf) {
stream_filter.add(new GMime.FilterUnix2Dos(false));
} else {
stream_filter.add(new GMime.FilterDos2Unix(false));
}
if (dotstuffed) {
if (RFC822FormatOptions.SMTP_FORMAT in options) {
stream_filter.add(new GMime.FilterSmtpData());
}
if (message.write_to_stream(Geary.RFC822.get_format_options(), stream_filter) < 0)
throw new RFC822Error.FAILED("Unable to write RFC822 message to filter stream");
var format = Geary.RFC822.get_format_options();
if (RFC822FormatOptions.SMTP_FORMAT in options) {
format = format.clone();
format.add_hidden_header("Bcc");
}
if (stream_filter.flush() != 0)
throw new RFC822Error.FAILED("Unable to flush RFC822 message to memory stream");
if (message.write_to_stream(format, stream_filter) < 0) {
throw new Error.FAILED(
"Unable to write RFC822 message to filter stream"
);
}
if (stream.flush() != 0)
throw new RFC822Error.FAILED("Unable to flush RFC822 message to memory buffer");
if (stream_filter.flush() != 0) {
throw new Error.FAILED(
"Unable to flush RFC822 message to memory stream"
);
}
if (stream.flush() != 0) {
throw new Error.FAILED(
"Unable to flush RFC822 message to memory buffer"
);
}
return new Memory.ByteBuffer.from_byte_array(byte_array);
}

View file

@ -145,7 +145,7 @@ public class Geary.RFC822.Part : Object {
public Memory.Buffer write_to_buffer(EncodingConversion conversion,
BodyFormatting format = BodyFormatting.NONE)
throws RFC822Error {
throws Error {
ByteArray byte_array = new ByteArray();
GMime.StreamMem stream = new GMime.StreamMem.with_byte_array(byte_array);
stream.set_owner(false);
@ -158,11 +158,11 @@ public class Geary.RFC822.Part : Object {
internal void write_to_stream(GMime.Stream destination,
EncodingConversion conversion,
BodyFormatting format = BodyFormatting.NONE)
throws RFC822Error {
throws Error {
GMime.DataWrapper? wrapper = (this.source_part != null)
? this.source_part.get_content() : null;
if (wrapper == null) {
throw new RFC822Error.INVALID(
throw new Error.INVALID(
"Could not get the content wrapper for content-type %s",
content_type.to_string()
);
@ -227,17 +227,17 @@ public class Geary.RFC822.Part : Object {
}
if (wrapper.write_to_stream(filter) < 0)
throw new RFC822Error.FAILED("Unable to write textual RFC822 part to filter stream");
throw new Error.FAILED("Unable to write textual RFC822 part to filter stream");
if (filter.flush() != 0)
throw new RFC822Error.FAILED("Unable to flush textual RFC822 part to destination stream");
throw new Error.FAILED("Unable to flush textual RFC822 part to destination stream");
if (destination.flush() != 0)
throw new RFC822Error.FAILED("Unable to flush textual RFC822 part to destination");
throw new Error.FAILED("Unable to flush textual RFC822 part to destination");
} else {
// Keep as binary
if (wrapper.write_to_stream(destination) < 0)
throw new RFC822Error.FAILED("Unable to write binary RFC822 part to destination stream");
throw new Error.FAILED("Unable to write binary RFC822 part to destination stream");
if (destination.flush() != 0)
throw new RFC822Error.FAILED("Unable to flush binary RFC822 part to destination");
throw new Error.FAILED("Unable to flush binary RFC822 part to destination");
}
}

View file

@ -1,179 +1,138 @@
/* Copyright 2016 Software Freedom Conservancy Inc.
/*
* Copyright 2016 Software Freedom Conservancy Inc.
* Portions copyright (C) 2000-2013 Jeffrey Stedfast
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
* (version 2.1 or later). See the COPYING file in this distribution.
*/
namespace Geary.RFC822.Utils {
// We use DEL to mark quote levels, since it's unlikely to be in email bodies, is a single byte
// in UTF-8, and is unmolested by GMime.FilterHTML.
public const char QUOTE_MARKER = '\x7f';
// We use DEL to mark quote levels, since it's unlikely to be in
// email bodies, is a single byte in UTF-8, and is unmolested by
// GMime.FilterHTML.
public const char QUOTE_MARKER = '\x7f';
/**
* Uses the best-possible transfer of bytes from the Memory.Buffer to the GMime.StreamMem object.
* The StreamMem object should be destroyed *before* the Memory.Buffer object, since this method
* will use unowned variants whenever possible.
*/
public GMime.StreamMem create_stream_mem(Memory.Buffer buffer) {
Memory.UnownedByteArrayBuffer? unowned_bytes_array_buffer = buffer as Memory.UnownedByteArrayBuffer;
if (unowned_bytes_array_buffer != null) {
// set_byte_array doesn't do any copying and doesn't take ownership -- perfect, this is
// the best of all possible worlds, assuming the Memory.Buffer is not destroyed first
GMime.StreamMem stream = new GMime.StreamMem();
stream.set_byte_array(unowned_bytes_array_buffer.to_unowned_byte_array());
return stream;
public string create_subject_for_reply(Email email) {
return (email.subject ?? new Subject("")).create_reply().value;
}
Memory.UnownedBytesBuffer? unowned_bytes_buffer = buffer as Memory.UnownedBytesBuffer;
if (unowned_bytes_buffer != null) {
// StreamMem.with_buffer does do a buffer copy (there's not set_buffer() call like
// set_byte_array() for some reason), but don't do a buffer copy when it comes out of the
// Memory.Buffer
return new GMime.StreamMem.with_buffer(unowned_bytes_buffer.to_unowned_uint8_array());
public string create_subject_for_forward(Email email) {
return (email.subject ?? new Subject("")).create_forward().value;
}
// do plain-old buffer copy
return new GMime.StreamMem.with_buffer(buffer.get_uint8_array());
}
public MailboxAddresses create_to_addresses_for_reply(Email email,
Gee.List<MailboxAddress>? sender_addresses = null) {
var new_to = new Gee.ArrayList<MailboxAddress>();
public string create_subject_for_reply(Geary.Email email) {
return (email.subject ?? new Geary.RFC822.Subject("")).create_reply().value;
}
// If we're replying to something we sent, send it to the same people we originally did.
// Otherwise, we'll send to the reply-to address or the from address.
if (email.to != null && email_is_from_sender(email, sender_addresses))
new_to.add_all(email.to.get_all());
else if (email.reply_to != null)
new_to.add_all(email.reply_to.get_all());
else if (email.from != null)
new_to.add_all(email.from.get_all());
public string create_subject_for_forward(Geary.Email email) {
return (email.subject ?? new Geary.RFC822.Subject("")).create_forward().value;
}
// Removes address from the list of addresses. If the list contains only the given address, the
// behavior depends on empty_ok: if true the list will be emptied, otherwise it will leave the
// address in the list once. Used to remove the sender's address from a list of addresses being
// created for the "reply to" recipients.
private void remove_address(Gee.List<Geary.RFC822.MailboxAddress> addresses,
RFC822.MailboxAddress address, bool empty_ok = false) {
for (int i = 0; i < addresses.size; ++i) {
if (addresses[i].equal_to(address) && (empty_ok || addresses.size > 1))
addresses.remove_at(i--);
}
}
private bool email_is_from_sender(Geary.Email email, Gee.List<RFC822.MailboxAddress>? sender_addresses) {
if (sender_addresses == null || email.from == null)
return false;
return Geary.traverse<RFC822.MailboxAddress>(sender_addresses)
.any(a => email.from.get_all().contains(a));
}
public Geary.RFC822.MailboxAddresses create_to_addresses_for_reply(Geary.Email email,
Gee.List< Geary.RFC822.MailboxAddress>? sender_addresses = null) {
Gee.List<Geary.RFC822.MailboxAddress> new_to =
new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
// If we're replying to something we sent, send it to the same people we originally did.
// Otherwise, we'll send to the reply-to address or the from address.
if (email.to != null && email_is_from_sender(email, sender_addresses))
new_to.add_all(email.to.get_all());
else if (email.reply_to != null)
new_to.add_all(email.reply_to.get_all());
else if (email.from != null)
new_to.add_all(email.from.get_all());
// Exclude the current sender. No need to receive the mail they're sending.
if (sender_addresses != null) {
foreach (RFC822.MailboxAddress address in sender_addresses)
remove_address(new_to, address);
}
return new Geary.RFC822.MailboxAddresses(new_to);
}
public Geary.RFC822.MailboxAddresses create_cc_addresses_for_reply_all(Geary.Email email,
Gee.List<Geary.RFC822.MailboxAddress>? sender_addresses = null) {
Gee.List<Geary.RFC822.MailboxAddress> new_cc = new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
// If we're replying to something we received, also add other recipients. Don't do this for
// emails we sent, since everyone we sent it to is already covered in
// create_to_addresses_for_reply().
if (email.to != null && !email_is_from_sender(email, sender_addresses))
new_cc.add_all(email.to.get_all());
if (email.cc != null)
new_cc.add_all(email.cc.get_all());
// Again, exclude the current sender.
if (sender_addresses != null) {
foreach (RFC822.MailboxAddress address in sender_addresses)
remove_address(new_cc, address, true);
}
return new Geary.RFC822.MailboxAddresses(new_cc);
}
public Geary.RFC822.MailboxAddresses merge_addresses(Geary.RFC822.MailboxAddresses? first,
Geary.RFC822.MailboxAddresses? second) {
Gee.List<Geary.RFC822.MailboxAddress> result = new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
if (first != null) {
result.add_all(first.get_all());
// Add addresses from second that aren't in first.
if (second != null)
foreach (Geary.RFC822.MailboxAddress address in second)
if (!first.contains_normalized(address.address))
result.add(address);
} else if (second != null) {
result.add_all(second.get_all());
}
return new Geary.RFC822.MailboxAddresses(result);
}
public Geary.RFC822.MailboxAddresses remove_addresses(Geary.RFC822.MailboxAddresses? from_addresses,
Geary.RFC822.MailboxAddresses? remove_addresses) {
Gee.List<Geary.RFC822.MailboxAddress> result = new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
if (from_addresses != null) {
result.add_all(from_addresses.get_all());
if (remove_addresses != null)
foreach (Geary.RFC822.MailboxAddress address in remove_addresses)
remove_address(result, address, true);
}
return new Geary.RFC822.MailboxAddresses(result);
}
public string reply_references(Geary.Email source) {
// generate list for References
Gee.ArrayList<RFC822.MessageID> list = new Gee.ArrayList<RFC822.MessageID>();
// 1. Start with the source's References list
if (source.references != null && source.references.list.size > 0)
list.add_all(source.references.list);
// 2. If there are In-Reply-To Message-IDs and they're not in the References list, append them
if (source.in_reply_to != null) {
foreach (RFC822.MessageID reply_id in source.in_reply_to.list) {
if (!list.contains(reply_id))
list.add(reply_id);
// Exclude the current sender. No need to receive the mail they're sending.
if (sender_addresses != null) {
foreach (var address in sender_addresses) {
remove_address(new_to, address);
}
}
return new MailboxAddresses(new_to);
}
// 3. Append the source's Message-ID, if available.
if (source.message_id != null)
list.add(source.message_id);
public MailboxAddresses create_cc_addresses_for_reply_all(Email email,
Gee.List<MailboxAddress>? sender_addresses = null) {
var new_cc = new Gee.ArrayList<MailboxAddress>();
string[] strings = new string[list.size];
for(int i = 0; i < list.size; ++i)
strings[i] = list[i].value;
// If we're replying to something we received, also add other recipients. Don't do this for
// emails we sent, since everyone we sent it to is already covered in
// create_to_addresses_for_reply().
if (email.to != null && !email_is_from_sender(email, sender_addresses))
new_cc.add_all(email.to.get_all());
return (list.size > 0) ? string.joinv(" ", strings) : "";
}
if (email.cc != null)
new_cc.add_all(email.cc.get_all());
public string email_addresses_for_reply(Geary.RFC822.MailboxAddresses? addresses, TextFormat format) {
if (addresses == null)
return "";
// Again, exclude the current sender.
if (sender_addresses != null) {
foreach (var address in sender_addresses) {
remove_address(new_cc, address, true);
}
}
switch (format) {
return new MailboxAddresses(new_cc);
}
public MailboxAddresses merge_addresses(MailboxAddresses? first,
MailboxAddresses? second) {
var result = new Gee.ArrayList<MailboxAddress>();
if (first != null) {
result.add_all(first.get_all());
// Add addresses from second that aren't in first.
if (second != null)
foreach (MailboxAddress address in second)
if (!first.contains_normalized(address.address))
result.add(address);
} else if (second != null) {
result.add_all(second.get_all());
}
return new MailboxAddresses(result);
}
public MailboxAddresses remove_addresses(MailboxAddresses? from_addresses,
MailboxAddresses? remove_addresses) {
Gee.List<MailboxAddress> result = new Gee.ArrayList<MailboxAddress>();
if (from_addresses != null) {
result.add_all(from_addresses.get_all());
if (remove_addresses != null)
foreach (MailboxAddress address in remove_addresses)
remove_address(result, address, true);
}
return new MailboxAddresses(result);
}
public string reply_references(Email source) {
// generate list for References
var list = new Gee.ArrayList<MessageID>();
// 1. Start with the source's References list
if (source.references != null) {
list.add_all(source.references.get_all());
}
// 2. If there are In-Reply-To Message-IDs and they're not in the References list, append them
if (source.in_reply_to != null) {
foreach (var reply_id in source.in_reply_to.get_all()) {
if (!list.contains(reply_id)) {
list.add(reply_id);
}
}
}
// 3. Append the source's Message-ID, if available.
if (source.message_id != null) {
list.add(source.message_id);
}
string[] strings = new string[list.size];
for(int i = 0; i < list.size; ++i) {
strings[i] = list[i].value;
}
return (list.size > 0) ? string.joinv(" ", strings) : "";
}
public string email_addresses_for_reply(MailboxAddresses? addresses,
TextFormat format) {
if (addresses == null)
return "";
switch (format) {
case TextFormat.HTML:
return HTML.escape_markup(addresses.to_full_display());
@ -182,127 +141,177 @@ public string email_addresses_for_reply(Geary.RFC822.MailboxAddresses? addresses
default:
assert_not_reached();
}
}
public bool comp_char_arr_slice(uint8[] array, uint start, string comp) {
for (int i = 0; i < comp.length; i++) {
if (array[start + i] != comp[i])
return false;
}
}
return true;
}
/**
* Obtains the best preview text from a plain or HTML string.
*
* The given string `text` should have UNIX encoded line endings (LF),
* rather than RFC822 (CRLF). The string returned will will have had
* its whitespace squashed.
*/
public string to_preview_text(string? text, TextFormat format) {
string preview = "";
/**
* Obtains the best preview text from a plain or HTML string.
*
* The given string `text` should have UNIX encoded line endings (LF),
* rather than RFC822 (CRLF). The string returned will will have had
* its whitespace squashed.
*/
public string to_preview_text(string? text, TextFormat format) {
string preview = "";
if (format == TextFormat.PLAIN) {
StringBuilder buf = new StringBuilder();
string[] all_lines = text.split("\n");
bool in_inline_pgp_header = false;
foreach (string line in all_lines) {
if (in_inline_pgp_header) {
if (Geary.String.is_empty(line)) {
in_inline_pgp_header = false;
if (format == TextFormat.PLAIN) {
StringBuilder buf = new StringBuilder();
string[] all_lines = text.split("\n");
bool in_inline_pgp_header = false;
foreach (string line in all_lines) {
if (in_inline_pgp_header) {
if (Geary.String.is_empty(line)) {
in_inline_pgp_header = false;
}
continue;
}
continue;
if (line.has_prefix("-----BEGIN PGP SIGNED MESSAGE-----")) {
in_inline_pgp_header = true;
continue;
}
if (line.has_prefix(">"))
continue;
if (line.has_prefix("--"))
continue;
if (line.has_prefix("===="))
continue;
if (line.has_prefix("~~~~"))
continue;
if (Geary.String.is_empty_or_whitespace(line)) {
buf.append("\n");
continue;
}
buf.append(" ");
buf.append(line);
}
if (line.has_prefix("-----BEGIN PGP SIGNED MESSAGE-----")) {
in_inline_pgp_header = true;
continue;
}
if (line.has_prefix(">"))
continue;
if (line.has_prefix("--"))
continue;
if (line.has_prefix("===="))
continue;
if (line.has_prefix("~~~~"))
continue;
if (Geary.String.is_empty_or_whitespace(line)) {
buf.append("\n");
continue;
}
buf.append(" ");
buf.append(line);
preview = buf.str;
} else if (format == TextFormat.HTML) {
preview = Geary.HTML.html_to_text(text, false);
}
preview = buf.str;
} else if (format == TextFormat.HTML) {
preview = Geary.HTML.html_to_text(text, false);
// XXX really shouldn't have to call make_valid here but do so to
// avoid segfaults in the regex engine on invalid char data. See
// issue #186 for the proper fix.
return Geary.String.reduce_whitespace(preview.make_valid());
}
// XXX really shouldn't have to call make_valid here but do so to
// avoid segfaults in the regex engine on invalid char data. See
// issue #186 for the proper fix.
return Geary.String.reduce_whitespace(preview.make_valid());
}
/**
* Uses a GMime.FilterBest to determine the best charset.
*
* This may require processing the entire stream, so occurs in a
* background thread.
*/
internal async string get_best_charset(GMime.Stream in_stream,
GLib.Cancellable? cancellable)
throws GLib.Error {
GMime.FilterBest filter = new GMime.FilterBest(CHARSET);
GMime.StreamFilter out_stream = new GMime.StreamFilter(
new GMime.StreamNull()
);
out_stream.add(filter);
/**
* Uses a GMime.FilterBest to determine the best charset.
*
* This may require processing the entire stream, so occurs in a
* background thread.
*/
public async string get_best_charset(GMime.Stream in_stream,
GLib.Cancellable? cancellable)
throws GLib.Error {
GMime.FilterBest filter = new GMime.FilterBest(CHARSET);
GMime.StreamFilter out_stream = new GMime.StreamFilter(
new GMime.StreamNull()
);
out_stream.add(filter);
yield Nonblocking.Concurrent.global.schedule_async(() => {
in_stream.write_to_stream(out_stream);
in_stream.reset();
out_stream.close();
},
cancellable
);
return filter.get_charset();
}
yield Nonblocking.Concurrent.global.schedule_async(() => {
in_stream.write_to_stream(out_stream);
in_stream.reset();
out_stream.close();
},
cancellable
);
return filter.get_charset();
}
/**
* Uses a GMime.FilterBest to determine the best encoding.
*
* This may require processing the entire stream, so occurs in a
* background thread.
*/
internal async GMime.ContentEncoding get_best_encoding(GMime.Stream in_stream,
GMime.EncodingConstraint constraint,
GLib.Cancellable? cancellable)
throws GLib.Error {
GMime.FilterBest filter = new GMime.FilterBest(ENCODING);
GMime.StreamFilter out_stream = new GMime.StreamFilter(
new GMime.StreamNull()
);
out_stream.add(filter);
/**
* Uses a GMime.FilterBest to determine the best encoding.
*
* This may require processing the entire stream, so occurs in a
* background thread.
*/
public async GMime.ContentEncoding get_best_encoding(GMime.Stream in_stream,
GMime.EncodingConstraint constraint,
GLib.Cancellable? cancellable)
throws GLib.Error {
GMime.FilterBest filter = new GMime.FilterBest(ENCODING);
GMime.StreamFilter out_stream = new GMime.StreamFilter(
new GMime.StreamNull()
);
out_stream.add(filter);
yield Nonblocking.Concurrent.global.schedule_async(() => {
in_stream.write_to_stream(out_stream);
in_stream.reset();
out_stream.close();
},
cancellable
);
return filter.encoding(constraint);
}
yield Nonblocking.Concurrent.global.schedule_async(() => {
in_stream.write_to_stream(out_stream);
in_stream.reset();
out_stream.close();
},
cancellable
);
return filter.encoding(constraint);
}
/**
* Uses the best-possible transfer of bytes from the Memory.Buffer to the GMime.StreamMem object.
* The StreamMem object should be destroyed *before* the Memory.Buffer object, since this method
* will use unowned variants whenever possible.
*/
internal GMime.StreamMem create_stream_mem(Memory.Buffer buffer) {
Memory.UnownedByteArrayBuffer? unowned_bytes_array_buffer = buffer as Memory.UnownedByteArrayBuffer;
if (unowned_bytes_array_buffer != null) {
// set_byte_array doesn't do any copying and doesn't take ownership -- perfect, this is
// the best of all possible worlds, assuming the Memory.Buffer is not destroyed first
GMime.StreamMem stream = new GMime.StreamMem();
stream.set_byte_array(unowned_bytes_array_buffer.to_unowned_byte_array());
return stream;
}
Memory.UnownedBytesBuffer? unowned_bytes_buffer = buffer as Memory.UnownedBytesBuffer;
if (unowned_bytes_buffer != null) {
// StreamMem.with_buffer does do a buffer copy (there's not set_buffer() call like
// set_byte_array() for some reason), but don't do a buffer copy when it comes out of the
// Memory.Buffer
return new GMime.StreamMem.with_buffer(unowned_bytes_buffer.to_unowned_uint8_array());
}
// do plain-old buffer copy
return new GMime.StreamMem.with_buffer(buffer.get_uint8_array());
}
internal bool comp_char_arr_slice(uint8[] array, uint start, string comp) {
for (int i = 0; i < comp.length; i++) {
if (array[start + i] != comp[i])
return false;
}
return true;
}
// Removes address from the list of addresses. If the list contains only the given address, the
// behavior depends on empty_ok: if true the list will be emptied, otherwise it will leave the
// address in the list once. Used to remove the sender's address from a list of addresses being
// created for the "reply to" recipients.
private void remove_address(Gee.List<MailboxAddress> addresses,
MailboxAddress address,
bool empty_ok = false) {
for (int i = 0; i < addresses.size; ++i) {
if (addresses[i].equal_to(address) &&
(empty_ok || addresses.size > 1)) {
addresses.remove_at(i--);
}
}
}
private bool email_is_from_sender(Email email, Gee.List<RFC822.MailboxAddress>? sender_addresses) {
var ret = false;
if (sender_addresses != null && email.from != null) {
ret = traverse<MailboxAddress>(sender_addresses)
.any(a => email.from.get_all().contains(a));
}
return ret;
}
}

View file

@ -6,60 +6,66 @@
namespace Geary.RFC822 {
/**
* Common text formats supported by {@link Geary.RFC822}.
*/
public enum TextFormat {
PLAIN,
HTML
}
/**
* Official IANA charset encoding name for the UTF-8 character set.
*/
public const string UTF8_CHARSET = "UTF-8";
private int init_count = 0;
internal Regex? invalid_filename_character_re = null;
public void init() {
if (init_count++ != 0)
return;
GMime.init();
GMime.ParserOptions.get_default().set_allow_addresses_without_domain(true);
try {
invalid_filename_character_re = new Regex("[/\\0]");
} catch (RegexError e) {
assert_not_reached();
/**
* Common text formats supported by {@link Geary.RFC822}.
*/
public enum TextFormat {
PLAIN,
HTML
}
}
public GMime.FormatOptions get_format_options() {
return GMime.FormatOptions.get_default().clone();
}
/**
* Official IANA charset encoding name for the UTF-8 character set.
*/
public const string UTF8_CHARSET = "UTF-8";
public GMime.ParserOptions get_parser_options() {
return GMime.ParserOptions.get_default().clone();
}
/**
* Official IANA charset encoding name for the ASCII character set.
*/
public const string ASCII_CHARSET = "US-ASCII";
public string? get_charset() {
return UTF8_CHARSET;
}
internal Regex? invalid_filename_character_re = null;
internal bool is_utf_8(string charset) {
string up = charset.up();
return (
// ASCII is a subset of UTF-8, so it's also valid
up == "ASCII" ||
up == "US-ASCII" ||
up == "US_ASCII" ||
up == "UTF-8" ||
up == "UTF8" ||
up == "UTF_8"
);
}
private int init_count = 0;
public void init() {
if (init_count++ != 0)
return;
GMime.init();
GMime.ParserOptions.get_default().set_allow_addresses_without_domain(true);
try {
invalid_filename_character_re = new Regex("[/\\0]");
} catch (RegexError e) {
assert_not_reached();
}
}
public GMime.FormatOptions get_format_options() {
return GMime.FormatOptions.get_default().clone();
}
public GMime.ParserOptions get_parser_options() {
return GMime.ParserOptions.get_default().clone();
}
public string? get_charset() {
return UTF8_CHARSET;
}
internal bool is_utf_8(string charset) {
string up = charset.up();
return (
// ASCII is a subset of UTF-8, so it's also valid
up == "ASCII" ||
up == "US-ASCII" ||
up == "US_ASCII" ||
up == "UTF-8" ||
up == "UTF8" ||
up == "UTF_8"
);
}
}

View file

@ -119,47 +119,28 @@ internal class Geary.Smtp.ClientConnection : BaseObject, Logging.Source {
* Returns the final Response of the transaction. If the ResponseCode is not a successful
* completion, the message should not be considered sent.
*/
public async Response send_data_async(Memory.Buffer data, bool already_dotstuffed,
Cancellable? cancellable = null) throws Error {
public async Response send_data_async(Memory.Buffer data,
GLib.Cancellable? cancellable = null)
throws GLib.Error {
check_connected();
// In the case of DATA, want to receive an intermediate response code, specifically 354
Response response = yield transaction_async(new Request(Command.DATA), cancellable);
if (!response.code.is_start_data())
return response;
if (response.code.is_start_data()) {
debug("SMTP Data: <%z>", data.size);
debug("SMTP Data: <%z>", data.size);
if (!already_dotstuffed) {
// By using DataStreamNewlineType.ANY, we're assured to get each line and convert to
// a proper line terminator for SMTP
DataInputStream dins = new DataInputStream(data.get_input_stream());
dins.set_newline_type(DataStreamNewlineType.ANY);
// Read each line and dot-stuff if necessary
for (;;) {
size_t length;
string? line = yield dins.read_line_async(Priority.DEFAULT, cancellable, out length);
if (line == null)
break;
// stuffing
if (line[0] == '.')
yield Stream.write_string_async(douts, ".", cancellable);
yield Stream.write_string_async(douts, line, cancellable);
yield Stream.write_string_async(douts, DataFormat.LINE_TERMINATOR, cancellable);
}
} else {
// ready to go, send and commit
yield Stream.write_all_async(douts, data, cancellable);
yield Stream.write_all_async(this.douts, data, cancellable);
// terminate buffer and flush to server
yield Stream.write_string_async(
this.douts, DataFormat.DATA_TERMINATOR, cancellable
);
yield this.douts.flush_async(Priority.DEFAULT, cancellable);
response = yield recv_response_async(cancellable);
}
// terminate buffer and flush to server
yield Stream.write_string_async(douts, DataFormat.DATA_TERMINATOR, cancellable);
yield douts.flush_async(Priority.DEFAULT, cancellable);
return yield recv_response_async(cancellable);
return response;
}
public async void send_request_async(Request request, Cancellable? cancellable = null) throws Error {

View file

@ -214,9 +214,9 @@ public class Geary.Smtp.ClientSession : BaseObject, Logging.Source {
yield send_rcpts_async(addrlist, cancellable);
// DATA
Geary.RFC822.Message email_copy = new Geary.RFC822.Message.without_bcc(email);
response = yield cx.send_data_async(email_copy.get_network_buffer(true), true,
cancellable);
response = yield cx.send_data_async(
email.get_rfc822_buffer(), cancellable
);
if (!response.code.is_success_completed())
response.throw_error("Unable to send message");

View file

@ -61,7 +61,7 @@ class Geary.AttachmentTest : TestCase {
public override void set_up() {
try {
this.content_type = Mime.ContentType.deserialize(CONTENT_TYPE);
this.content_type = Mime.ContentType.parse(CONTENT_TYPE);
this.default_type = Mime.ContentType.ATTACHMENT_DEFAULT;
this.content_disposition = new Mime.ContentDisposition("attachment", null);

View file

@ -434,7 +434,7 @@ class Geary.App.ConversationMonitorTest : TestCase {
references.message_id
);
}
email.set_send_date(new Geary.RFC822.Date.from_date_time(now));
email.set_send_date(new RFC822.Date(now));
email.set_email_properties(new MockEmailProperties(now));
email.set_full_references(mid, null, refs_list);
return email;

View file

@ -489,7 +489,7 @@ class Geary.App.ConversationSetTest : TestCase {
references.message_id
);
}
email.set_send_date(new Geary.RFC822.Date.from_date_time(now));
email.set_send_date(new RFC822.Date(now));
email.set_email_properties(new MockEmailProperties(now));
email.set_full_references(mid, null, refs_list);
return email;

View file

@ -289,7 +289,7 @@ class Geary.App.ConversationTest : TestCase {
);
email.set_full_references(mid, null, null);
email.set_email_properties(new MockEmailProperties(now));
email.set_send_date(new Geary.RFC822.Date.from_date_time(now));
email.set_send_date(new RFC822.Date(now));
return email;
}

View file

@ -10,6 +10,7 @@ class Geary.Mime.ContentTypeTest : TestCase {
public ContentTypeTest() {
base("Geary.Mime.ContentTypeTest");
add_test("static_defaults", static_defaults);
add_test("parse", parse);
add_test("get_file_name_extension", get_file_name_extension);
add_test("guess_type_from_name", guess_type_from_name);
add_test("guess_type_from_buf", guess_type_from_buf);
@ -26,6 +27,26 @@ class Geary.Mime.ContentTypeTest : TestCase {
);
}
public void parse() throws GLib.Error {
var test_article = ContentType.parse("text/plain");
assert_string("text", test_article.media_type);
assert_string("plain", test_article.media_subtype);
try {
ContentType.parse("");
assert_not_reached();
} catch (MimeError.PARSE error) {
// All good
}
try {
ContentType.parse("textplain");
assert_not_reached();
} catch (MimeError.PARSE error) {
// All good
}
}
public void get_file_name_extension() throws Error {
assert(new ContentType("image", "jpeg", null).get_file_name_extension() == ".jpeg");
assert(new ContentType("test", "unknown", null).get_file_name_extension() == null);

View file

@ -24,7 +24,7 @@ class Geary.RFC822.MailboxAddressTest : TestCase {
add_test("equal_to", equal_to);
}
public void is_valid_address() throws Error {
public void is_valid_address() throws GLib.Error {
assert(Geary.RFC822.MailboxAddress.is_valid_address("john@dep.aol.museum") == true);
assert(Geary.RFC822.MailboxAddress.is_valid_address("test@example.com") == true);
assert(Geary.RFC822.MailboxAddress.is_valid_address("test.other@example.com") == true);
@ -44,7 +44,7 @@ class Geary.RFC822.MailboxAddressTest : TestCase {
assert(Geary.RFC822.MailboxAddress.is_valid_address("\"Surname, Name\" <mail@example.com>") == true);
}
public void unescaped_constructor() throws Error {
public void unescaped_constructor() throws GLib.Error {
MailboxAddress addr1 = new MailboxAddress("test1", "test2@example.com");
assert(addr1.name == "test1");
assert(addr1.address == "test2@example.com");
@ -72,7 +72,7 @@ class Geary.RFC822.MailboxAddressTest : TestCase {
assert(addr5.domain == "");
}
public void from_rfc822_string_encoded() throws Error {
public void from_rfc822_string_encoded() throws GLib.Error {
try {
MailboxAddress addr = new MailboxAddress.from_rfc822_string("test@example.com");
assert(addr.name == null);
@ -172,7 +172,7 @@ class Geary.RFC822.MailboxAddressTest : TestCase {
assert(addr.name == "Firstname ¯_(ツ)_/¯ Lastname via=?UTF-8?Q?_Vendor=22_");
}
public void has_distinct_name() throws Error {
public void has_distinct_name() throws GLib.Error {
assert(new MailboxAddress("example", "example@example.com").has_distinct_name() == true);
assert(new MailboxAddress("", "example@example.com").has_distinct_name() == false);
@ -185,7 +185,7 @@ class Geary.RFC822.MailboxAddressTest : TestCase {
assert(new MailboxAddress("'prefix-example@example.com'", "example@example.com").has_distinct_name() == true);
}
public void is_spoofed() throws Error {
public void is_spoofed() throws GLib.Error {
assert(new MailboxAddress(null, "example@example.com").is_spoofed() == false);
assert(new MailboxAddress("", "example@example.com").is_spoofed() == false);
assert(new MailboxAddress("", "example@example.com").is_spoofed() == false);
@ -213,7 +213,7 @@ class Geary.RFC822.MailboxAddressTest : TestCase {
}
}
public void to_full_display() throws Error {
public void to_full_display() throws GLib.Error {
assert(new MailboxAddress("", "example@example.com").to_full_display() ==
"example@example.com");
assert(new MailboxAddress("Test", "example@example.com").to_full_display() ==
@ -229,7 +229,7 @@ class Geary.RFC822.MailboxAddressTest : TestCase {
);
}
public void to_short_display() throws Error {
public void to_short_display() throws GLib.Error {
assert(new MailboxAddress("", "example@example.com").to_short_display() ==
"example@example.com");
assert(new MailboxAddress("Test", "example@example.com").to_short_display() ==
@ -288,7 +288,7 @@ class Geary.RFC822.MailboxAddressTest : TestCase {
}
public void to_rfc822_string() throws Error {
public void to_rfc822_string() throws GLib.Error {
assert(new MailboxAddress("", "example@example.com").to_rfc822_string() ==
"example@example.com");
assert(new MailboxAddress(" ", "example@example.com").to_rfc822_string() ==

View file

@ -16,7 +16,7 @@ class Geary.RFC822.MailboxAddressesTest : TestCase {
add_test("hash", hash);
}
public void from_rfc822_string_encoded() throws Error {
public void from_rfc822_string_encoded() throws GLib.Error {
MailboxAddresses addrs = new MailboxAddresses.from_rfc822_string("test@example.com");
assert(addrs.size == 1);
@ -43,7 +43,7 @@ class Geary.RFC822.MailboxAddressesTest : TestCase {
assert_string("\"Surname, Name\" <mail@example.com>", addrs.to_rfc822_string());
}
public void to_rfc822_string() throws Error {
public void to_rfc822_string() throws GLib.Error {
assert(new MailboxAddresses().to_rfc822_string() == "");
assert(new_addreses({ "test1@example.com" })
.to_rfc822_string() == "test1@example.com");
@ -51,7 +51,7 @@ class Geary.RFC822.MailboxAddressesTest : TestCase {
.to_rfc822_string() == "test1@example.com, test2@example.com");
}
public void equal_to() throws Error {
public void equal_to() throws GLib.Error {
var mailboxes_a = new_addreses({ "test1@example.com" });
var mailboxes_b = new_addreses({ "test1@example.com" });
var mailboxes_c = new_addreses({ "test2@example.com" });
@ -83,7 +83,7 @@ class Geary.RFC822.MailboxAddressesTest : TestCase {
);
}
public void hash() throws Error {
public void hash() throws GLib.Error {
var mailboxes_a = new_addreses({ "test1@example.com" });
var mailboxes_b = new_addreses({ "test1@example.com" });
var mailboxes_c = new_addreses({ "test2@example.com" });

View file

@ -17,7 +17,7 @@ class Geary.RFC822.MessageDataTest : TestCase {
add_test("PreviewText.with_header", preview_text_with_header);
}
public void preview_text_with_header() throws Error {
public void preview_text_with_header() throws GLib.Error {
PreviewText plain_preview1 = new PreviewText.with_header(
new Geary.Memory.StringBuffer(PLAIN_BODY1_HEADERS),
new Geary.Memory.StringBuffer(PLAIN_BODY1_ENCODED)
@ -61,7 +61,7 @@ class Geary.RFC822.MessageDataTest : TestCase {
public void date_from_rfc822() throws GLib.Error {
const string FULL_HOUR_TZ = "Thu, 28 Feb 2019 00:00:00 -0100";
Date full_hour_tz = new Date(FULL_HOUR_TZ);
Date full_hour_tz = new Date.from_rfc822_string(FULL_HOUR_TZ);
assert_int64(
((int64) (-1 * 3600)) * 1000 * 1000,
full_hour_tz.value.get_utc_offset(),
@ -81,7 +81,7 @@ class Geary.RFC822.MessageDataTest : TestCase {
);
const string HALF_HOUR_TZ = "Thu, 28 Feb 2019 00:00:00 +1030";
Date half_hour_tz = new Date(HALF_HOUR_TZ);
Date half_hour_tz = new Date.from_rfc822_string(HALF_HOUR_TZ);
assert_int64(
((int64) (10.5 * 3600)) * 1000 * 1000,
half_hour_tz.value.get_utc_offset()
@ -96,15 +96,15 @@ class Geary.RFC822.MessageDataTest : TestCase {
public void date_to_rfc822() throws GLib.Error {
const string FULL_HOUR_TZ = "Thu, 28 Feb 2019 00:00:00 -0100";
Date full_hour_tz = new Date(FULL_HOUR_TZ);
Date full_hour_tz = new Date.from_rfc822_string(FULL_HOUR_TZ);
assert_string(FULL_HOUR_TZ, full_hour_tz.to_rfc822_string());
const string HALF_HOUR_TZ = "Thu, 28 Feb 2019 00:00:00 +1030";
Date half_hour_tz = new Date(HALF_HOUR_TZ);
Date half_hour_tz = new Date.from_rfc822_string(HALF_HOUR_TZ);
assert_string(HALF_HOUR_TZ, half_hour_tz.to_rfc822_string());
const string NEG_HALF_HOUR_TZ = "Thu, 28 Feb 2019 00:00:00 -1030";
Date neg_half_hour_tz = new Date(NEG_HALF_HOUR_TZ);
Date neg_half_hour_tz = new Date.from_rfc822_string(NEG_HALF_HOUR_TZ);
assert_string(NEG_HALF_HOUR_TZ, neg_half_hour_tz.to_rfc822_string());
}

View file

@ -59,12 +59,13 @@ This is the second line.
add_test("get_searchable_recipients", get_searchable_recipients);
add_test("from_composed_email", from_composed_email);
add_test("from_composed_email_inline_attachments", from_composed_email_inline_attachments);
add_test("get_network_buffer", get_network_buffer);
add_test("get_network_buffer_dot_stuff", get_network_buffer_dot_stuff);
add_test("get_network_buffer_long_ascii_line", get_network_buffer_long_ascii_line);
add_test("get_rfc822_buffer", get_rfc822_buffer);
add_test("get_rfc822_buffer_dot_stuff", get_rfc822_buffer_dot_stuff);
add_test("get_rfc822_buffer_no_bcc", get_rfc822_buffer_no_bcc);
add_test("get_rfc822_buffer_long_ascii_line", get_rfc822_buffer_long_ascii_line);
}
public void basic_message_from_buffer() throws Error {
public void basic_message_from_buffer() throws GLib.Error {
Message basic = resource_to_message(BASIC_TEXT_PLAIN);
assert_data(basic.subject, "Re: Basic text/plain message");
@ -74,21 +75,21 @@ This is the second line.
assert_addresses(basic.to, "Charlie <charlie@example.net>");
assert_addresses(basic.cc, "Dave <dave@example.net>");
assert_addresses(basic.bcc, "Eve <eve@example.net>");
//assert_data(basic.message_id, "<3456@example.net>");
assert_data(basic.message_id, "3456@example.net");
assert_message_id_list(basic.in_reply_to, "<1234@local.machine.example>");
assert_message_id_list(basic.references, "<1234@local.machine.example>");
assert_data(basic.date, "Fri, 21 Nov 1997 10:01:10 -0600");
assert_data(basic.date, "1997-11-21T10:01:10-0600");
assert(basic.mailer == "Geary Test Suite 1.0");
}
public void encoded_recipient() throws Error {
public void encoded_recipient() throws GLib.Error {
Message enc = string_to_message(ENCODED_TO);
// Courtesy Mailsploit https://www.mailsploit.com
assert(enc.to[0].name == "potus@whitehouse.gov <test>");
assert_string("potus@whitehouse.gov <test>", enc.to[0].name);
}
public void duplicate_mailbox() throws Error {
public void duplicate_mailbox() throws GLib.Error {
Message dup = string_to_message(DUPLICATE_TO);
assert(dup.to.size == 2);
@ -97,16 +98,16 @@ This is the second line.
);
}
public void duplicate_message_id() throws Error {
public void duplicate_message_id() throws GLib.Error {
Message dup = string_to_message(DUPLICATE_REFERENCES);
assert(dup.references.list.size == 2);
assert(dup.references.size == 2);
assert_message_id_list(
dup.references, "<1234@local.machine.example> <5678@local.machine.example>"
);
}
public void text_plain_as_plain() throws Error {
public void text_plain_as_plain() throws GLib.Error {
Message test = resource_to_message(BASIC_TEXT_PLAIN);
assert_true(test.has_plain_body(), "Expected plain body");
@ -114,7 +115,7 @@ This is the second line.
assert_string(BASIC_PLAIN_BODY, test.get_plain_body(false, null));
}
public void text_plain_as_html() throws Error {
public void text_plain_as_html() throws GLib.Error {
Message test = resource_to_message(BASIC_TEXT_PLAIN);
assert_true(test.has_plain_body(), "Expected plain body");
@ -125,7 +126,7 @@ This is the second line.
);
}
public void text_html_as_html() throws Error {
public void text_html_as_html() throws GLib.Error {
Message test = resource_to_message(BASIC_TEXT_HTML);
assert_true(test.has_html_body(), "Expected html body");
@ -133,7 +134,7 @@ This is the second line.
assert_string(BASIC_HTML_BODY, test.get_html_body(null));
}
public void text_html_as_plain() throws Error {
public void text_html_as_plain() throws GLib.Error {
Message test = resource_to_message(BASIC_TEXT_HTML);
assert_true(test.has_html_body(), "Expected html body");
@ -141,7 +142,7 @@ This is the second line.
assert_string(BASIC_HTML_BODY, test.get_html_body(null));
}
public void tnef_extract_attachments() throws Error {
public void tnef_extract_attachments() throws GLib.Error {
Message test = resource_to_message(BASIC_MULTIPART_TNEF);
Gee.List<Part> attachments = test.get_attachments();
assert_true(attachments.size == 2);
@ -149,7 +150,7 @@ This is the second line.
assert_true(attachments[1].get_clean_filename() == "bookmark.htm");
}
public void multipart_alternative_as_plain() throws Error {
public void multipart_alternative_as_plain() throws GLib.Error {
Message test = resource_to_message(BASIC_MULTIPART_ALTERNATIVE);
assert_true(test.has_plain_body(), "Expected plain body");
@ -157,7 +158,7 @@ This is the second line.
assert_string(BASIC_PLAIN_BODY, test.get_plain_body(false, null));
}
public void multipart_alternative_as_converted_html() throws Error {
public void multipart_alternative_as_converted_html() throws GLib.Error {
Message test = resource_to_message(BASIC_MULTIPART_ALTERNATIVE);
assert_true(test.has_plain_body(), "Expected plain body");
@ -168,7 +169,7 @@ This is the second line.
);
}
public void multipart_alternative_as_html() throws Error {
public void multipart_alternative_as_html() throws GLib.Error {
Message test = resource_to_message(BASIC_MULTIPART_ALTERNATIVE);
assert_true(test.has_plain_body(), "Expected plain body");
@ -176,13 +177,13 @@ This is the second line.
assert_string(BASIC_HTML_BODY, test.get_html_body(null));
}
public void get_preview() throws Error {
public void get_preview() throws GLib.Error {
Message multipart_signed = string_to_message(MULTIPART_SIGNED_MESSAGE_TEXT);
assert(multipart_signed.get_preview() == MULTIPART_SIGNED_MESSAGE_PREVIEW);
}
public void get_recipients() throws Error {
public void get_recipients() throws GLib.Error {
Message test = string_to_message(SIMPLE_MULTIRECIPIENT_TO_CC_BCC);
Gee.List<RFC822.MailboxAddress>? addresses = test.get_recipients();
@ -195,14 +196,14 @@ This is the second line.
assert_addresses_list(addresses, verify_list, "get_recipients");
}
public void get_searchable_body() throws Error {
public void get_searchable_body() throws GLib.Error {
Message test = resource_to_message(BASIC_TEXT_HTML);
string searchable = test.get_searchable_body();
assert_true(searchable.contains("This is the first line"), "Expected body text");
assert_false(searchable.contains("<P>"), "Expected html removed");
}
public void get_searchable_recipients() throws Error {
public void get_searchable_recipients() throws GLib.Error {
Message test = string_to_message(SIMPLE_MULTIRECIPIENT_TO_CC_BCC);
string searchable = test.get_searchable_recipients();
assert_true(searchable.contains("Jane Doe <jdoe@somewhere.tld>"), "Expected to address");
@ -210,13 +211,13 @@ This is the second line.
assert_true(searchable.contains("Jane Doe BCC <jdoe_bcc@somewhere.tld>"), "Expected bcc address");
}
public void get_network_buffer() throws Error {
public void get_rfc822_buffer() throws GLib.Error {
Message test = resource_to_message(BASIC_TEXT_PLAIN);
Memory.Buffer buffer = test.get_network_buffer(true);
Memory.Buffer buffer = test.get_rfc822_buffer(NONE);
assert_true(buffer.to_string() == NETWORK_BUFFER_EXPECTED, "Network buffer differs");
}
public void get_network_buffer_dot_stuff() throws GLib.Error {
public void get_rfc822_buffer_dot_stuff() throws GLib.Error {
RFC822.MailboxAddress to = new RFC822.MailboxAddress(
"Test", "test@example.com"
);
@ -235,11 +236,42 @@ This is the second line.
);
Geary.RFC822.Message message = message_from_composed_email.end(async_result());
string message_data = message.get_network_buffer(true).to_string();
string message_data = message.get_rfc822_buffer(SMTP_FORMAT).to_string();
assert_true(message_data.has_suffix("..newline\r\n..\r\n"));
}
public void get_network_buffer_long_ascii_line() throws GLib.Error {
public void get_rfc822_buffer_no_bcc() throws GLib.Error {
RFC822.MailboxAddress to = new RFC822.MailboxAddress(
"Test", "test@example.com"
);
RFC822.MailboxAddress bcc = new RFC822.MailboxAddress(
"BCC", "bcc@example.com"
);
RFC822.MailboxAddress from = new RFC822.MailboxAddress(
"Sender", "sender@example.com"
);
Geary.ComposedEmail composed = new Geary.ComposedEmail(
new GLib.DateTime.now_local(),
new Geary.RFC822.MailboxAddresses.single(from)
).set_to(
new Geary.RFC822.MailboxAddresses.single(to)
).set_bcc(
new Geary.RFC822.MailboxAddresses.single(bcc)
);
composed.body_text = "\nbody\n";
this.message_from_composed_email.begin(
composed,
this.async_completion
);
Geary.RFC822.Message message = message_from_composed_email.end(async_result());
string message_data = message.get_rfc822_buffer(SMTP_FORMAT).to_string();
assert_true("To: Test <test@example.com>\r\n" in message_data);
assert_false("bcc" in message_data.down());
}
public void get_rfc822_buffer_long_ascii_line() throws GLib.Error {
RFC822.MailboxAddress to = new RFC822.MailboxAddress(
"Test", "test@example.com"
);
@ -265,7 +297,7 @@ This is the second line.
);
Geary.RFC822.Message message = message_from_composed_email.end(async_result());
string message_data = message.get_network_buffer(true).to_string();
string message_data = message.get_rfc822_buffer(SMTP_FORMAT).to_string();
foreach (var line in message_data.split("\n")) {
assert_true(line.length < 1000, line);
}
@ -318,7 +350,7 @@ This is the second line.
);
}
public void from_composed_email_inline_attachments() throws Error {
public void from_composed_email_inline_attachments() throws GLib.Error {
RFC822.MailboxAddress to = new RFC822.MailboxAddress(
"Test", "test@example.com"
);
@ -371,7 +403,8 @@ This is the second line.
assert_true(out_buffer.size > (buffer.size+buffer2.size), "Expected sizeable message");
}
private async Geary.RFC822.Message message_from_composed_email(Geary.ComposedEmail composed) {
private async Message message_from_composed_email(ComposedEmail composed)
throws GLib.Error {
return yield new Geary.RFC822.Message.from_composed_email(
composed,
GMime.utils_generate_message_id(composed.from.get(0).domain),
@ -379,7 +412,7 @@ This is the second line.
);
}
private Message resource_to_message(string path) throws Error {
private Message resource_to_message(string path) throws GLib.Error {
GLib.File resource =
GLib.File.new_for_uri(RESOURCE_URI).resolve_relative_path(path);
@ -391,7 +424,7 @@ This is the second line.
);
}
private Message string_to_message(string message_text) throws Error {
private Message string_to_message(string message_text) throws GLib.Error {
return new Message.from_buffer(
new Geary.Memory.StringBuffer(message_text)
);
@ -399,21 +432,21 @@ This is the second line.
private void assert_data(Geary.MessageData.AbstractMessageData? actual,
string expected)
throws Error {
throws GLib.Error {
assert_non_null(actual, expected);
assert_string(expected, actual.to_string());
}
private void assert_address(Geary.RFC822.MailboxAddress? address,
string expected)
throws Error {
throws GLib.Error {
assert_non_null(address, expected);
assert_string(expected, address.to_rfc822_string());
}
private void assert_addresses(Geary.RFC822.MailboxAddresses? addresses,
string expected)
throws Error {
throws GLib.Error {
assert_non_null(addresses, expected);
assert_string(expected, addresses.to_rfc822_string());
}
@ -421,7 +454,7 @@ This is the second line.
private void assert_addresses_list(Gee.List<RFC822.MailboxAddress>? addresses,
Gee.List<string> expected,
string context)
throws Error {
throws GLib.Error {
assert_non_null(addresses, context + " not null");
assert_true(addresses.size == expected.size, context + " size");
foreach (RFC822.MailboxAddress address in addresses) {
@ -431,9 +464,9 @@ This is the second line.
private void assert_message_id_list(Geary.RFC822.MessageIDList? ids,
string expected)
throws Error {
assert_non_null(ids, expected);
assert(ids.to_rfc822_string() == expected);
throws GLib.Error {
assert_non_null(ids, "ids are null");
assert_string(expected, ids.to_rfc822_string());
}
// Courtesy Mailsploit https://www.mailsploit.com

View file

@ -23,7 +23,7 @@ class Geary.RFC822.PartTest : TestCase {
add_test("write_to_buffer_plain_utf8", write_to_buffer_plain_utf8);
}
public void new_from_minimal_mime_part() throws Error {
public void new_from_minimal_mime_part() throws GLib.Error {
Part test = new Part(new_part("test/plain", CR_BODY.data));
assert_null_string(test.content_id, "content_id");
@ -31,7 +31,7 @@ class Geary.RFC822.PartTest : TestCase {
assert_null(test.content_disposition, "content_disposition");
}
public void new_from_complete_mime_part() throws Error {
public void new_from_complete_mime_part() throws GLib.Error {
const string TYPE = "text/plain";
const string ID = "test-id";
const string DESC = "test description";
@ -58,7 +58,7 @@ class Geary.RFC822.PartTest : TestCase {
);
}
public void write_to_buffer_plain() throws Error {
public void write_to_buffer_plain() throws GLib.Error {
Part test = new Part(new_part("text/plain", CR_BODY.data));
Memory.Buffer buf = test.write_to_buffer(Part.EncodingConversion.NONE);
@ -66,7 +66,7 @@ class Geary.RFC822.PartTest : TestCase {
assert_string(CR_BODY, buf.to_string());
}
public void write_to_buffer_plain_crlf() throws Error {
public void write_to_buffer_plain_crlf() throws GLib.Error {
Part test = new Part(new_part("text/plain", CRLF_BODY.data));
Memory.Buffer buf = test.write_to_buffer(Part.EncodingConversion.NONE);
@ -75,7 +75,7 @@ class Geary.RFC822.PartTest : TestCase {
assert_string(CR_BODY, buf.to_string());
}
public void write_to_buffer_plain_ical() throws Error {
public void write_to_buffer_plain_ical() throws GLib.Error {
Part test = new Part(new_part("text/calendar", ICAL_BODY.data));
Memory.Buffer buf = test.write_to_buffer(Part.EncodingConversion.NONE);

View file

@ -15,7 +15,7 @@ class Geary.RFC822.Utils.Test : TestCase {
add_test("best_encoding_binary", best_encoding_binary);
}
public void to_preview_text() throws Error {
public void to_preview_text() throws GLib.Error {
assert(Geary.RFC822.Utils.to_preview_text(PLAIN_BODY_ENCODED, Geary.RFC822.TextFormat.PLAIN) ==
PLAIN_BODY_EXPECTED);
assert(Geary.RFC822.Utils.to_preview_text(HTML_BODY_ENCODED, Geary.RFC822.TextFormat.HTML) ==

View file

@ -118,7 +118,8 @@ class Integration.Smtp.ClientSession : TestCase {
}
private async Geary.RFC822.Message new_message(Geary.RFC822.MailboxAddress from,
Geary.RFC822.MailboxAddress to) {
Geary.RFC822.MailboxAddress to)
throws Geary.RFC822.Error {
Geary.ComposedEmail composed = new Geary.ComposedEmail(
new GLib.DateTime.now_local(),
new Geary.RFC822.MailboxAddresses.single(from)

View file

@ -52,14 +52,14 @@ geary_test_engine_sources = [
'engine/imap-db/imap-db-folder-test.vala',
'engine/imap-engine/account-processor-test.vala',
'engine/imap-engine/imap-engine-generic-account-test.vala',
'engine/mime-content-type-test.vala',
'engine/mime/mime-content-type-test.vala',
'engine/outbox/outbox-email-identifier-test.vala',
'engine/rfc822-mailbox-address-test.vala',
'engine/rfc822-mailbox-addresses-test.vala',
'engine/rfc822-message-test.vala',
'engine/rfc822-message-data-test.vala',
'engine/rfc822-part-test.vala',
'engine/rfc822-utils-test.vala',
'engine/rfc822/rfc822-mailbox-address-test.vala',
'engine/rfc822/rfc822-mailbox-addresses-test.vala',
'engine/rfc822/rfc822-message-test.vala',
'engine/rfc822/rfc822-message-data-test.vala',
'engine/rfc822/rfc822-part-test.vala',
'engine/rfc822/rfc822-utils-test.vala',
'engine/util-ascii-test.vala',
'engine/util-config-file-test.vala',
'engine/util-html-test.vala',

View file

@ -83,10 +83,12 @@ int main(string[] args) {
engine.add_suite(new Geary.Outbox.EmailIdentifierTest().get_suite());
engine.add_suite(new Geary.RFC822.MailboxAddressTest().get_suite());
engine.add_suite(new Geary.RFC822.MailboxAddressesTest().get_suite());
engine.add_suite(new Geary.RFC822.MessageTest().get_suite());
engine.add_suite(new Geary.RFC822.MessageDataTest().get_suite());
engine.add_suite(new Geary.RFC822.PartTest().get_suite());
engine.add_suite(new Geary.RFC822.Utils.Test().get_suite());
// Message requires all of the rest of the package working, so put
// last
engine.add_suite(new Geary.RFC822.MessageTest().get_suite());
engine.add_suite(new Geary.String.Test().get_suite());
engine.add_suite(new Geary.ComposedEmailTest().get_suite());

View file

@ -132,6 +132,10 @@ public class TestServer : GLib.Object {
foreach (var line in this.script) {
result.line = line;
switch (line.action) {
case CONNECTED:
// no-op
break;
case SEND_LINE:
debug("Sending: %s", line.value);
try {

View file

@ -251,11 +251,23 @@
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkShortcutsSection">
<property name="visible">True</property>
<property name="section-name">single-key</property>
<property name="title" translatable="yes">Single-key Shortcuts</property>
<child>
<object class="GtkShortcutsGroup">
<property name="visible">True</property>
<property name="title" translatable="yes"
context="shortcut window">Single-key shortcuts</property>
context="shortcut window">Single-key shortcuts (if enabled)</property>
<child>
<object class="GtkShortcutsShortcut">
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="visible">True</property>
@ -298,6 +310,16 @@
<property name="accelerator">E</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkShortcutsGroup">
<property name="visible">True</property>
<child>
<object class="GtkShortcutsShortcut">
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="visible">True</property>
@ -329,7 +351,7 @@
<child>
<object class="GtkShortcutsShortcut">
<property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Find in current conversations</property>
<property name="title" translatable="yes" context="shortcut window">Find in current conversation</property>
<property name="accelerator">slash</property>
</object>
</child>