Merge branch 'mainline' into remove-old-msgs-beyond-storage-pref
This commit is contained in:
commit
71262f0e79
62 changed files with 3163 additions and 1355 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -11,3 +11,4 @@ pl
|
|||
pt_BR
|
||||
sv
|
||||
tr
|
||||
uk
|
||||
|
|
|
|||
1540
help/uk/uk.po
Normal file
1540
help/uk/uk.po
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
155
po/es.po
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
30
po/sk.po
30
po/sk.po
|
|
@ -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
143
po/tr.po
|
|
@ -16,8 +16,8 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: geary.mainline\n"
|
||||
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/geary/issues\n"
|
||||
"POT-Creation-Date: 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
126
po/uk.po
|
|
@ -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 "Вставити посилання"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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() ==
|
||||
|
|
@ -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" });
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
@ -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) ==
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue